我正在QByteArray
上发送QTcpSocket
。我面临的问题是,虽然我发送了一个长度为25的数组,但在服务器上它会收到59个字节。这是我的示例代码:
//client
QDataStream out(qctcpSocket);
out<<qByteArray; // qByteArray is of length 25
const int nbBytes = qctcpSocket->write(qByteArray); // nbBytes returbs 25
//server
TArray<char> data;
uint32 pendingData = 0;
TArray<char> newData; // customized Template for Array
newData.InsertZeroed(0, pendingData);
int32 bytesRead = 0;
rcvSocket->Recv(reinterpret_cast<uint8*>(newData.GetData()), pendingData, bytesRead);
data += newData;//length is 59 !!
答案 0 :(得分:1)
我在您的客户端代码中看到两个问题。由于您使用(据称)连接的套接字作为设备的数据流:
QDataStream out(qctcpSocket);
您通过套接字发送数据,使用以下行:
out<<qByteArray; // qByteArray is of length 25
然后您将使用下一个发送更多数据:
const int nbBytes = qctcpSocket->write(qByteArray); // nbBytes returbs 25
这是第一个问题:您尝试两次发送相同的数据。但是每次调用之间的数据不同(这是第二个问题):使用流执行第一次发送将在套接字上放置一个长度为QByteArray::size + 4
的字节数组,由流对象添加四个额外字节(它是实际字节数组之前的大端32位整数,并以字节为单位保持其长度。)
相反,第二个发送只将字节数组中的25个字节放在套接字上,我想,这就是你想要的第一个。
我的建议是完全摆脱数据流并使用write
类的QTcpSocket
方法,如果你没有在另一个端点上使用Qt,这应该是最好的选择(服务器)。
如果您仍想使用该流,请删除发送方的write
,并确保在接收端点上使用QDataStream
对象,因此您必须:
//Build a QByteArray out of the incoming bytes:
QByteArray raw_data = ...
//Use it as the stream device
QDataStream stream(raw_data);
//Use another byte array to fetch the data out of the stream
QByteArray data;
stream >> data;
或者你可以手动剥离我不推荐的前四个字节。
答案 1 :(得分:1)
服务器似乎在虚幻引擎上运行。这很重要!
您需要两次发送数据:一次通过数据流,下次通过write
。不要这样做:使用您首选的方法发送一次。
如果您想使用数据流:
void Client::send(const QByteArray &data) {
QDataStream out(this->qctcpSocket);
out << data;
}
有4 + data.size()
个字节写入流。最初的4个字节带有后续数组的little-endian长度。如果您不想发送数组长度,则必须在套接字上的或 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;
}
接收原始数据包 - 不通过数据流作为bytearray发送:
// 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;
}
注意:这是未经测试的。