【发布时间】:2020-11-23 19:55:01
【问题描述】:
总结: TCP 套接字中的一些内存将被其他传入数据覆盖。
应用: 一个使用Qt 中的TCP(QTcpSocket 和QTcpServer) 的客户端/服务器系统。客户端从服务器请求一个帧(只是一个简单的字符串消息),以及由该帧组成的响应(服务器->客户端)(614400字节用于测试目的)。帧大小是预先确定的并且是固定的。
实施细节: 根据 TCP 协议(服务器 -> 客户端)的保证,我知道我应该能够从套接字读取 614400 字节并且它们是有序的。如果这两件事中的任何一个失败,则连接一定失败。
重要代码: 假设套接字已连接。
此代码向服务器请求一个帧。称为GetFrame() 函数。
// Prompt the server to send a frame over
if(socket->isWritable() && !is_receiving) { // Validate that socket is ready
is_receiving = true; // Forces only one request to go out at a time
qDebug() << "Getting frame from socket..." << image_no;
int written = SafeWrite((char*)"ReadyFrame"); // Writes then flushes the write buffer
if (written == -1) {
qDebug() << "Failed to write...";
return temp_frame.data();
}
this->SocketRead();
is_receiving = false;
}
qDebug() << image_no << "- Image Received";
image_no ++;
return temp_frame.data();
此代码等待刚刚请求的帧被读取。这是SocketRead() 函数
size_t byte_pos = 0;
qint64 bytes_read = 0;
do {
if (!socket->waitForReadyRead(500)) { // If it timed out return existing frame
if (!(socket->bytesAvailable() > 0)) {
qDebug() << "Timed Out" << byte_pos;
break;
}
}
bytes_read = socket->read((char*)temp_frame.data() + byte_pos, frame_byte_size - byte_pos);
if (bytes_read < 0) {
qDebug() << "Reading Failed" << bytes_read << errno;
break;
}
byte_pos += bytes_read;
} while (byte_pos < frame_byte_size && is_connected); // While we still have more pixels
qDebug() << "Finished Receiving Frame: " << byte_pos;
如上面的代码所示,我一直读取直到完全接收到帧(其中读取的字节数等于帧中的字节数)。
我遇到的问题是 QTcpSocket 读取操作以不符合 TCP 协议保证的方式跳过字节。由于我跳过字节,我最终没有到达 while 循环的结尾,而只是“超时”。 为什么会这样?
到目前为止我做了什么: 服务器发送的数据直接转换为 uint16_t(短)整数,用于客户端的其他部分。我已将服务器更改为简单地输出数据,这些数据只是为每个发送的数字加一个。由于数据类型是 uint16_t 并且字节数超过了该整数类型的最大数量,因此 int-16 将每 65535 循环一次。
这是一个数据可视化软件,所以这个调试配置(在客户端)会导致这样的结果:
我已经确定(您可以在图形底部看到一点)某些字节被跳过。在temp_frame的内存中可以看到内存跳过的确切点:
在正确的情况下,这应该按顺序计数。
从 Wireshark 并遵循这个特定的 TCP 连接,我确定 所有字节实际上都到达了(全部为 6114400),并且 所有数字都按顺序排列 (我使用了一个 python 脚本来确保计数是连续的)。
这是一个开源项目的工作,所以this 是客户端的整个代码库。
总的来说,我看不出在这个解决方案中我怎么会做错事,我所做的只是以标准方式从套接字读取。
【问题讨论】:
-
请阅读您应用的标签的说明,Qt 是 C++ 而不是 C。另外,不要用图片来表示文本并考虑提取minimal reproducible example。
-
虽然内核和 NIC 可能能够处理高数据速率,但您可能会用完 kernel 套接字缓冲区来存储数据。考虑使用
getsockopt和setsockopt和SO_RCVBUF来设置更大的空间。详情请见man 7 socket -
好的,感谢@CraigEstey 的建议,我试过了,但没有解决问题。
-
具体来说,我使用
setsockopt和SO_RCVBUF将缓冲区大小设置为614400。 -
澄清一下,您是说您看到了
"Timed Out"调试消息吗?SocketRead中的评论// If it timed out return existing frame也有点奇怪。这本身就表明您可能仅仅因为您决定不再等待而最终处理部分帧数据。而不是使用waitForReadyRead,您应该连接到readyRead信号并在数据到达时处理/调度数据。
标签: c++ qt sockets qtcpsocket