Сервер работает на движке Unreal.Это важно!
Вы отправляете свои данные дважды: один раз через поток данных, в следующий раз через write
.Не делайте этого: отправьте его один раз, используя ваш предпочтительный метод.
Если вы хотите использовать поток данных:
void Client::send(const QByteArray &data) {
QDataStream out(this->qctcpSocket);
out << data;
}
4 + data.size()
байт записано впоток.Начальные 4 байта несут младшую байтовую длину массива, который следует.Если вы не хотите отправлять длину массива, вы должны использовать writeRawData
в потоке или write
в сокете:
void Client::send(const QByteArray &data) {
// use when there's a data stream already on the socket
this->out.writeRawData(data.constData(), data.size());
// or
// use when there's no data stream available
this->qctcpSocket->write(data);
}
На сервере вы 'Вы столкнулись с двумя основными проблемами:
Вы предполагаете, что данные поступают в одном блоке.Такой гарантии нет: насколько вам известно, ваш обработчик данных может быть уведомлен о наличии одного байта.
Вы обрабатываете только один пакет - вы можете получить любое числоиз них, и вы должны продолжать читать их, пока больше нет данных.
Давайте иметь:
include <utility> // Unreal headers are omitted in this example
class Server {
FSocket *rcvSocket;
...
};
bool Server::hardReceiveError() {
// the socket has lost synchronization - we can't proceed!
rcvSocket->Close();
...
return false;
}
void Server::processPacket(const TArray<uint8> &pkt) {
// process the packet
...
}
// Apparently the Unreal engine doesn't care enough to offer this (?)
template <typename T>
std::enable_if<std::is_integral<T>::value, T>::type fromLittleEndian(const void *buf) {
T ret;
memcpy(&ret, buf, sizeof(ret));
if (!FGenericPlatformProperties::IsLittleEndian()) {
uint8 *r = reinterpret_cast<uint8*>(&ret);
for (int i = 0; i < sizeof(T)/2; ++i)
std::swap(r[i], r[sizeof(T)-1-i]);
}
return ret;
}
Для получения данных байтового массива, отправленных через данныеПоток, вы должны обрабатывать размер:
// handle all available data
void Server::receiveHandlerDataStream() {
while (receiveDataStream());
}
// receive one packet
bool Server::receiveDataStream() {
uint32 pendingData = 0;
rcvSocket->HasPendingData(pendingData);
if (pendingData < 4)
return false;
int32 bytesRead = 0;
uint8 buf[4];
rcvSocket->Recv(buf, sizeof(buf), bytesRead, ESocketReceiveFlags::Peek);
if (bytesRead != 4)
return hardReceiveError();
auto length = fromLittleEndian<int32>(buf);
if (pendingData < (4+length)
return false;
bytesRead = 0;
rcvSocket->Recv(buf, sizeof(buf), bytesRead, ESocketReceiveFlags::None);
if (bytesRead != 4)
return hardReceiveError();
TArray<uint8> data;
data.AddUninitialized(length);
bytesRead = 0;
if (length) { // we may have 0-length packets
rcvSocket->Recv(data.GetData(), data.ArrayNum, bytesRead, ESocketReceiveFlags::None);
if (bytesRead != length)
return hardReceiveError();
}
processPacket(data);
return true;
}
Для того, чтобы вместо этого получать необработанные пакеты - не отправленные как байтовый массив через поток данных:
// handle all available data
void Server::receiveHandlerRaw() {
while (receiveRaw());
}
// receive one packet
bool Server::receiveRaw() {
uint32 pendingData = 0;
constexpr uint32 expects = 25;
rcvSocket->HasPendingData(pendingData);
if (pendingData < expects)
return false;
int32 bytesRead = 0;
TArray<uint8> data;
data.AddUninitialized(expects);
rcvSocket->Recv(data.GetData(), data.ArrayNum, bytesRead, ESocketReceiveFlags::None);
if (bytesRead != data.ArrayNum)
return hardReceiveError();
processPacket(data);
return true;
}
Примечание: Это не проверено.