线程中的 QTcpSocket 并不总是立即发送数据
QTcpSocket in a thread doesn't always send data instantly
我有一个服务器,在一个单独的线程中处理每个客户端连接。为了测试它,我实现了一个简单的"echo"服务:我在客户机中输入一条消息,它被发送到服务器,服务器将它发送回来,客户机显示它。
由于我只发送短数据包,因此在当前的测试中只使用单个写和读操作,因此从未有过任何分割数据包。
客户端使用write()
和readAll()
,它一直工作完美(用数据包嗅探工具验证)
我几乎一直观察到的是,服务器每隔一秒才发送数据包。
例如:- 客户端发送"abc"
- 服务器接收并返回。
write()
函数返回3. - 客户端没有收到任何消息。我检查封包嗅探器,没有发送数据包
- 客户端发送"def",几秒钟,甚至几分钟后
- 服务器接收并返回。
write()
函数返回3. - 客户端同时接收abc和def消息。90%的时间作为两个单独的数据包,10%的时间作为"abcdef"。
客户端的调试输出示例,指示发送和接收的内容(注意客户端发送的所有内容都是手动输入的,并且在每个消息之间我至少等待10秒):
Client: 1
Client: 2
Server: 1
Server: 2
Client: 12345
Server: 12345
Client: abc
Client: def
Server: abcdef
Client: 123
Server: 123
Client: 456
Client: 789
Server: 456789
Client: a
Server: a
Client: b
Client: c
Server: b
Server: c
我知道TCP是一个连续的流,但我没有想到在一个无负载的局域网上,它应该花几分钟来传输几个字节。有趣的是,客户端可以立即正确地发送所有内容,它从不等待进一步的写入将它们粘合在一起。
由于客户端似乎工作完美,我怀疑我的线程有问题。
一旦QTcpServer
接收到传入的连接,我启动一个新线程,将socketDescriptor传递给线程的构造函数。
TcpThread::TcpThread(int socketDescriptor, QObject *parent) : QThread(parent)
{
this->socketDescriptor = socketDescriptor;
}
void TcpThread::run()
{
if (!tcpSocket->setSocketDescriptor(socketDescriptor)) {
qDebug() << tcpSocket->error();
return;
}
connect(tcpSocket, SIGNAL(readyRead()), this, SLOT(read_data()));
while (tcpSocket->state() == QAbstractSocket::ConnectedState)
{
tcpSocket->waitForDisconnected(-1);
}
}
void TcpThread::read_data()
{
QByteArray data = tcpSocket->readAll();
int nr = tcpSocket->write(data);
qDebug() << data << " (" << nr << " bytes written)";
}
我知道,这不是处理线程的最佳实践,我只是使用"Qt4方法"来快速熟悉QTcpSocket。
有趣的是,我在每次调用write()
时都会收到警告:
QSocketNotifier:不能启用或禁用套接字通知器另一个线程
我想知道为什么。我在我的线程的run()
方法中创建了QTcpSocket
的实例,所以它应该在同一个线程中,不是吗?
实际上,如果我将套接字的实例化移到构造函数中,我将得到
QObject:不能为父对象创建子对象线程。(父线程是QTcpSocket(0x2c54a80),父线程是QThread(0x10776d0),当前线程是TcpThread(0x2c53c18)
我以前没有得到。然而,即使在构造函数中进行了实例化,我的代码也完全相同:消息有时立即发送,有时与下一条消息粘在一起并发送,无论两者之间间隔多长时间。
我做错了什么?
使用带有QTcpSocket
的线程是没有意义的。它是异步API,所以使用线程的收益是最小的(问题可能只有当你有其他耗时的任务阻塞事件循环时)。
另一个问题是您创建对象并将其分配给线程的方式。你给出了一个关于这个话题的完美链接,但你完全不理解它。
在QT中使用线程的简单规则:
- 永远不要给自己分配线程
- 你可以在线程之间移动对象,如果他们没有父对象(如果他们是内存结构树的根,整个三个被移动到新线程)
- 设置父线程分配对象给父线程
- 槽和信号的默认连接检测信号是从分配给接收器的不同线程发出的,因此在这种情况下,信号被包装并通过其事件循环传递给适当的线程。
- 如果你在信号槽中使用值对象,你不需要额外的同步(锁和互斥锁)
- 当你覆盖
QThread::run()
并使用信号和插槽时,你应该在该线程中运行事件循环,通过调用QThread::exec()
或创建新的事件循环。
所以通常你的代码中会有很多错误。我建议放弃QThread
,或者至少再次阅读那篇文章,理解它并应用它的建议(它是在Qt5发布之前写的,所以你所说的"Qt4风格"是无效的)。
- 防止主数据类型C++的隐式转换
- 用于访问容器<T>数据成员的正确 API
- 嵌套在类中时无法设置成员数据
- 使用流处理接收到的数据
- 静态数据成员的问题-修复链接错误会导致编译器错误
- 处理小于cpu数据总线的数据类型.(c++转换为机器代码)
- QTcpSocket 在 RemoteHostClosedError 后重新连接到服务器时无法传输数据
- QTcpServer/QTcpSocket:使用 QDataStream 与直接发送 UTF-8 数据
- QTcpSocket 发送的数据比想要的多 - Qt/C++
- QTcpSocket ready读取信号在传输数据时停止发出
- QSslSocket 在等待数据时超时(但 QTcpSocket 不会)
- 在QTcpSocket和python套接字之间交换数据的正确方法是什么?
- 从QTcpSocket上的数据流中连续运行复杂算法的最佳Qt线程解决方案是什么
- 在QTcpSocket中连续发送XML数据的最简单方法
- QTcpSocket 写入数据结构
- QTcpSocket每~0.5s才读取一次数据,即使是非常小的数据包,任何方法都可以加快速度
- 线程中的 QTcpSocket 并不总是立即发送数据
- Qt QTcpSocket在读取数据时不发出信号
- 在等待QTcpSocket数据时阻塞GUI
- 如何立即写入数据到套接字,而不使用flush和waitFOrBytesWritten与QTCPSocket