Qt TCP通信,多线程服务器端

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/q294971352/article/details/51416603

相信许多初学Qt的同学都会和我一样遇到这样的问题:

一、Qt TCP通信在使用nextPendingConnect后,服务器端就只会与最后接入的客户端通信,这个时候就会考虑继承QThread实现多线程,从而实现多个客户端与服务器端通信,每当一个新的客户端连接时,通过标识码socketDescriptor,实现与对应的客户端通信。

void server::incomingConnection(int socketDescriptor)
{
    socketList.append(socketDescriptor);

    serverThread *thread = new serverThread(socketDescriptor, 0);

    connect(thread, SIGNAL(started()), dlg, SLOT(showConnection()));
    connect(thread, SIGNAL(disconnectTCP(int)), dlg, SLOT(showDisconnection(int)));    
    connect(thread, SIGNAL(revData(QString)), dlg, SLOT(revData(QString)));
    connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
    connect(dlg, SIGNAL(sendData(QString, int)), thread, SLOT(sendData(QString, int)));

    thread->start();
}

二、虽然多线程服务器端的例子书上和网上很多(虽然基本一样= =!), 都是简单的时间服务器,只实现简单的发送功能,而且每个客户端发一次就断开了,但是许多时候我们都要使用完整的收发功能。对于发送实现还比较简单只需要根据socketDescriptor和write函数就可以将信息发送到指定的客户端:

void serverThread::sendData(QString data, int id)
{
    if (id == socketDescriptor)
    {
        tso->write(data.toLocal8Bit());
    }
}

接收方面,许多人第一时间就会想到连接readReady()信号,这个时候问题又发生了,经过一番qDebug发现readReady()信号根本就没触发。到这里网上的资料也少了,在许多资料都提到阻塞式接收和waitForReadyRead(),但是具体的都没写了,就一个函数要怎么用啊,多少给个例子呗,然而怎么找都没有。然后我就在Qt文档里找这个函数,居然就发现了一个例子:

int numRead = 0, numReadTotal = 0;
char buffer[50];

forever {
    numRead  = socket.read(buffer, 50);

        // do whatever with array

    numReadTotal += numRead;
        if (numRead == 0 && !socket.waitForReadyRead())
            break;
}

果然还是官方的靠谱,赶紧把自己的程序改改,然后就可以接受数据了,然后就没有然后了。

void serverThread::run()
{
    tso = new QTcpSocket;

    if (!tso->setSocketDescriptor(socketDescriptor))
        return;

    connect(tso, &QTcpSocket::disconnected, this, &serverThread::disconnectToHost);

    QByteArray data;

    forever
    {
        data = tso->readAll();
        QString msg = QString::fromLocal8Bit(data);
        if (tso->waitForReadyRead())
        {    
            if (msg.length() != 0)
            {
                msg = tso->peerAddress().toString() + ':'+ msg;
                emit revData(msg);
            }
        }
    }
}

当然这种方法比较low,而且在实现send的时候会出现一个在线程中新开一个线程的警告,所以为了达到更好的效果,我们可以继承TcpSocket类,在里面实现数据的收发,而且这样也不需要使用到阻塞。

修改后serverThread的部分源码:

void serverThread::run()
{
    sock = new MySocket(socketDescriptor, 0);

    if (!sock->setSocketDescriptor(socketDescriptor))
        return ;

    connect(sock, &MySocket::disconnected, this, &serverThread::disconnectToHost);
    connect(sock, SIGNAL(revData(QString)), this, SLOT(recvData(QString)));
    connect(this, SIGNAL(sendDat(QString,int)), sock, SLOT(sendMsg(QString,int)));

    exec();
}

void serverThread::sendData(QString data, int id)
{
    if (data == "")
        return ;

    emit sendDat(data, id);
}

void serverThread::recvData(QString msg)
{
    emit revData(msg);
}

这里线程中只是一个信号转发的功能。

在MySocket类中只需要像普通一样实现数据收发就行啦:

MySocket::MySocket(int socketDescriptor, QObject *parent)
    : QTcpSocket(parent), socketDescriptor(socketDescriptor)
{
    connect(this, SIGNAL(readyRead()),
            this, SLOT(recvData()));
}

void MySocket::sendMsg(QString msg, int id)
{
    if (id == socketDescriptor)
    {
        write(msg.toLocal8Bit());
    }
}

void MySocket::recvData()
{
    QByteArray data;

    data = readAll();
    QString msg = peerAddress().toString() + ':'+ QString::fromLocal8Bit(data);

    emit revData(msg);
}

程序运行图如下:
这里写图片描述
因为是自己平常调试用,所以端口号是写死了的,需要动态设置端口的同学,就自己多加几个控件,多写几行代码啦。

代码下载,由于CSDN的下载有点坑,请移步GitHub:
https://github.com/DragonPang/QtMultiThreadTcpServer

扫描二维码关注公众号,回复: 4004358 查看本文章

猜你喜欢

转载自blog.csdn.net/q294971352/article/details/51416603