线程中的QTcpSocket并不总是立即发送数据

时间:2015-09-21 07:32:05

标签: c++ multithreading qt tcp

我有一个服务器,它在一个单独的线程中处理每个客户端连接。为了测试它,我实现了一个简单的回声" service:我在客户端输入一条消息,然后发送到服务器,服务器将其发回,客户端显示它。

因为我只发送短数据包,所以我只使用一次写入和读取来进行当前测试,我从来没有任何拆分数据包。

客户端使用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是一个连续的流,但我并不期望在没有负载的LAN上,传输几个字节需要几分钟。有趣的是,客户端可以正确地立即发送所有内容,它永远不会等待进一步的写入将它们粘合在一起。

由于客户似乎工作得很好,我怀疑我的线程出了问题。

一旦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)";
}

我知道,这不是处理线程的best practice,我只是使用&#34; Qt4方法&#34;快速熟悉QTcpSocket。

有趣的是,每次拨打write()时都会收到警告:

  

QSocketNotifier:无法启用或禁用套接字通知程序   另一个线程

我想知道为什么。我在我的线程的QTcpSocket方法中创建了一个run()的实例,所以它应该在同一个帖子中,不应该吗?

实际上,如果我将套接字的实例化移动到构造函数,我得到

  

QObject:无法为不同的父级创建子级   线。 (Parent是QTcpSocket(0x2c54a80),父亲的线程是   QThread(0x10776d0),当前线程为TcpThread(0x2c53c18)

我之前没有得到过。但是,即使在构造函数中进行实例化,我的代码也完全相同:消息有时会立即发送,有时它会粘贴到下一条消息并与之一起发送,无论两者之间经过多长时间。

我做错了什么?

1 个答案:

答案 0 :(得分:0)

使用QTcpSocket毫无意义的线程。它是异步API,因此使用线程获得的收益很小(问题可能只有当你有其他耗时的工作阻止事件循环时)。

另一个问题是您创建对象并将其分配给线程的方式。你已经给出了关于这个主题的完美链接,但你完全不理解它。

在QT中使用线程时的简单规则:

  • 从不为自己分配线程
  • 只有在没有父级的情况下才能在线程之间移动对象(它们是内存结构树的根,整个三个移动到新线程)
  • 设置父级将对象分配给父线程
  • 插槽和信号的默认连接检测到信号是从接收器分配给的不同线程发出的,因此在这种情况下,信号被包装并使用其事件循环传递到适当的线程。
  • 如果您在信号槽中使用值对象,则不需要额外的同步(锁和互斥锁)
  • 当您覆盖QThread::run()并使用信号和插槽时,您应该在该线程中运行事件循环,方法是调用QThread::exec()或创建新的事件循环。

所以一般来说,你的代码中有很多错误。我会重新放弃QThread或至少阅读那篇文章再次理解并应用其建议(它是在Qt5发布之前编写的,所以你的主张“Qt4风格”无效)。