QT多线程TCP服务器客户端通讯编程

  • Post author:
  • Post category:其他


在QT网络编程中,服务器往往需要和多个客户端保持连接,因此,我在熟悉多线程编程过程中,在TCP服务每接收到一个客户端连接请求,就新建一个线程。客户端也是在新建的线程中维护的。

程序的思路如下:

程序使用了非模式对话框来分别显示服务器和客户端,默认情况下客户端使用127.0.0.1这个本地IP来和自己进程内的服务器通讯。服务器在监听到新客户端的连接请求后新建对话框以及一个线程与客户端通讯,客户端建立连接成功后会将自己的线程ID发送给服务器,服务器接收到数据后将自己的线程ID返回给客户端。客户端断开连接后会自动将对话框关闭以及释放资源。服务器端在客户端断开后,也会自动将对话框关闭。程序运行后的界面如下图所示:

在这里插入图片描述

程序起始的时候只有客户端和服务器的主界面,单击打开新客户端按钮后会创建“客户端连接”对话框,点击里面的连接按钮,服务器主界面会创建“服务器连接”对话框,此时这两个对话框会在各自新建的线程里面通讯,上图是点击两次打开新客户端,并且连接后的效果,对话框的位置得自己手动移动,有些简陋,望海涵。在点击客户端连接的断开按钮后,程序会结束客户端网络线程,在网络线程结束后,客户端对话框会被关闭与销毁,这是由于程序中使用connect函数连接线程的finished信号与对话框的close和deleteLater槽函数。

在多线程编程中,不能使用QTcpServer的nextPendingConnection函数来获取客户端连接,要继承QTcpServer,重写虚函数incomingConnection来建立新线程,在新线程的run函数中使用new来实例化QTcpSocket,并且使用setSocketDescriptor函数来关联客户端连接。

在线程中使用connect函数关联QTcpSocket信号与槽函数的时候,需要使用Qt::DirectConnection来设置第五个参数,否则在信号到来的时候会报告下面的错误:

QObject: Cannot create children for a parent that is in a different thread.

(Parent is QTcpSocket(0x19446950), parent’s thread is QThread(0x178d81a0), current thread is ServerConnectionThread(0x19446948)

另外不知道为什么在线程中处理error(QAbstractSocket::SocketError)

信号的时候会报告下面的错误:

QObject::connect: Cannot queue arguments of type ‘QAbstractSocket::SocketError’

(Make sure ‘QAbstractSocket::SocketError’ is registered using qRegisterMetaType().)

添加如下代码后问题解决:

qRegisterMetaType<QAbstractSocket::SocketError>("QAbstractSocket::SocketError");

完整代码下载链接:


QT多线程TCP服务器客户端通讯程序


下面是关键代码:

//继承自QTcpServer的类:
    class MyTcpServer : public QTcpServer
{
    Q_OBJECT
public:
    MyTcpServer(QObject* parent = 0) :
        QTcpServer(parent){};
protected:
    void incomingConnection(qintptr handle) override
    {
        ServerConnectionThread* thread =
                new ServerConnectionThread(handle, this);
        connect(thread, &ServerConnectionThread::finished,
                thread, &ServerConnectionThread::deleteLater);
        thread->start();
        emit newConnection(thread);
    }
signals:
    void newConnection(ServerConnectionThread*);
};

//在新建的线程中将QTcpSocket实例与客户端连接关联到一起
void ServerConnectionThread::run()
{
    clientSocket = new QTcpSocket();
    //由于不能将clientSocket的父指针设成this,因此需要在线程结束的时候做清理工作:
    connect(this, &ServerConnectionThread::finished,
            clientSocket, &QTcpSocket::deleteLater);
    //由于QTcpSocket不能够跨线程操作,因此只能通过传递套接字描述符的方式在子线程中
    //设置QTcpSocket与客户端连接的关联。
    if (!clientSocket->setSocketDescriptor(clientHandle))
    {
        emit errorStr(clientSocket->errorString());
        return;
    }

    emit sendClientAddrAndThreadId(this,
                clientSocket->peerAddress().toString(),
                (qint64)currentThreadId());

    connect(clientSocket, &QTcpSocket::readyRead,
            this, &ServerConnectionThread::readSocket,
            Qt::DirectConnection);

    qRegisterMetaType<QAbstractSocket::SocketError>("QAbstractSocket::SocketError");
    connect(clientSocket, SIGNAL(error(QAbstractSocket::SocketError)),
             this, SLOT(sendError(QAbstractSocket::SocketError)));
    exec();
}



版权声明:本文为haohaoganhuo原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。