Qt中Socket跨线程通讯问题

发布时间 2023-04-30 19:52:34作者: 斗战胜佛美猴王

对于一个QTcpServer服务器来说,每当有新客户端连接时,系统会为其分配一个新的QTcpSocket对象进行管理。默认情况下,在incomingConnection函数中创建的QTcpSocket对象将在应用程序主线程中运行,而不是连接所需的处理线程中运行。如果开发者需要确保收到数据的顺序以及避免线程竞争,可以采取以下方式:

1. 将QTcpSocket的派生类(如MyTcpSocket)定义为私有,并提供一个公共的工厂函数 createNewSocket(),在其中设置信号槽并调用QObject::moveToThread(QThread *targetThread)函数将该socket放进指定的线程处理。

void MyThread::incomingConnection(qintptr socketDescriptor)
{
    MyTcpSocket* socket = MyTcpSocket::createNewSocket();
    // 手动断开Qt::AutoConnection机制,信号与动作都在父线程内循环
    QObject::disconnect(socket, 0, 0, 0);

    connect(socket, SIGNAL(disconnected()), socket, SLOT(deleteLater()));
    connect(this, SIGNAL(stop()), socket, SLOT(slotStop()));
    connect(socket, SIGNAL(signalSendData(const QByteArray&)), this, SLOT(slotRecvData(const QByteArray&)));

    socket->setSocketDescriptor(socketDescriptor);
    
    socket->moveToThread(m_thread); // 放进目标线程
}

2.在createNewSocket()函数内部实例化MyTcpSocket对象,在构造函数中设置this->moveToThread(thread)方法,把MyTcpSocket的运行线程设置为指定处理线程。

MyTcpSocket* MyTcpSocket::createNewSocket()
{
    MyTcpSocket *socket = new MyTcpSocket();
    socket->moveToThread(targetThread);

    return socket;
}

MyTcpSocket::MyTcpSocket(QObject *parent)
    : QTcpSocket(parent)
{
    connect(this, SIGNAL(readyRead()), this, SLOT(readData()));
    connect(this, SIGNAL(error(QAbstractSocket::SocketError)),
            this, SLOT(displayError(QAbstractSocket::SocketError)));
    connect(this, &QTcpSocket::disconnected, this, &QTcpSocket::deleteLater);
}

使用这种方法,可以确保处理m_thread线程的操作发生在MyTcpSocket对象所绑定的线程上,避免了许多线程安全问题和不确定性。