Qt中跨进程Socket通信以及socket跨线程通信

一 QTcpServer 创建流程

  1. 创建套接字服务器 QTcpServer 对象,
  2. 通过 QTcpServer 对象设置监听,即:QTcpServer::listen()
  3. 基于 QTcpServer::newConnection() 信号检测是否有新的客户端连接
  4. 如果有新的客户端连接调用*QTcpServer::nextPendingConnection() 得到通信的QTcpSocket对象 使用通信的套接字对象 QTcpSocket 和客户端进行通信,readyRead信号
 //1.创建server对象
    auto server=new QTcpServer(this);
    //2.设置服务器监听listen(ipAddr,port)
    auto res=server->listen(QHostAddress::Any,8888);//返回监听成功与否,可能存在端口占用情况
    //3.基于 QTcpServer::newConnection() 信号检测是否有新的客户端连接
    connect(server,&QTcpServer::newConnection,[=]()
    {
        QTcpSocket* tcpSocket=server->nextPendingConnection();//接收新的客户端连接,用于实际的收发处理

        //4.收发处理,
        //4.1 当收到数据请求时,tcpSocket会发射readyread信号
        connect(tcpSocket,&QTcpSocket::readyRead,[=]()
        {
            //收到信息请求
            auto sMsg=tcpSocket->readAll();
            qDebug()<<"Datas from the remote client:"<<sMsg;
        });
        //4.2 写数据
        QByteArray sWriteMsg="Hello Client";
        tcpSocket->write(sWriteMsg);
    });

本文福利,莬费领取Qt开发学习资料包、技术视频,内容包括(C++语言基础,Qt编程入门,QT信号与槽机制,QT界面开发-图像绘制,QT网络,QT数据库编程,QT项目实战,QSS,OpenCV,Quick模块,面试题等等)↓↓↓↓↓↓见下面↓↓文章底部点击莬费领取↓↓ 

二 QTcpSocketClient创建流程

  1. 创建套接字服务器 QTcpSocket对象,
  2. 连接服务器,绑定服务器端绑定的IP和端口信息, QAbstractSocket::connectToHost(QHostAddress("127.0.0.1"),8888)
  3. 检测是否与服务器连接成功,connectToHost() 函数并成功建立连接之后发出 connected() 信号
  4. 使用通信的套接字对象 QTcpSocket 和客户端进行通信,readyRead信号
    //client建立流程
    //1.创建通信的套接字类 QTcpSocket 对象
    QTcpSocket* tcpSocket=new QTcpSocket(this);
    //2.使用服务器端绑定的 IP 和端口连接服务器 QAbstractSocket::connectToHost()
    tcpSocket->connectToHost(QHostAddress("127.0.0.1"),8888);
    //3.检测是否与服务器连接成功,connectToHost() 函数并成功建立连接之后发出 connected() 信号
    connect(tcpSocket,&QTcpSocket::connected,[=]()
    {
        qDebug()<<"Success to connect to the remote server";
    });
    //4.使用 QTcpSocket 对象和服务器进行通信,收到数据请求时,tcpSocket会发射readyread信号
    connect(tcpSocket,&QTcpSocket::readyRead,[=]()
    {
        //收到信息请求
        auto sMsg=tcpSocket->readAll();
        qDebug()<<"Datas from the remote server:"<<sMsg;
    });

三 qtSocket多线程通信

模拟客户端发送文件,服务器接收文件为例子,使用多线程方式进行通信,部分代码实现思路。

3.1 socketClient 多线程发送文件实现思路

3.1 .1 SendFile线程任务类实现思路

由于该线程需要完成多个子功能,因此使用moveToThread方式可更加灵活实现多线程

class SendFile : public QObject
//通过 slot 机智定义两个work()函数处理不同的任务功能:
public slots:
    //创建线程任务函数
    void connectServer();//连接服务器
    void sendFileTask();//发送文件
……

//通过signal信号来向主线程发送任务完成情况及线程间的通信
signals:
    void connectOk();
    void disconnectOk();
    void sendCurrentPercen(int n);

3.1.2 主线程中实现思路

//1.创建线程对象
QThread* pThread=new QThread;
 //2.创建线程任务对像
SendFile* worker=new SendFile;
 //3.将任务对像象添加到线程中
worker->moveToThread(pThread);
//4.启动线程 
 pThread->start();
//5.信号槽机制关联执行线程任务及线程完成情况
 connect(this,&Dialogtest::startConnectServer,worker,&SendFile::connectServer);
 connect(this,&Dialogtest::sendFileSignal,worker,&SendFile::sendFileTask);

 connect(worker,&SendFile::connectOk,this,[=](){//连接成功 });
 connect(worker,&SendFile::disconnectOk,this,[=](){ //已断开连接});
 connect(worker,&SendFile::sendCurrentPercen,this,[=](int nPercent){ //进度条处理 });

3.2 socketServer多线程接收文件实现思路

3.2.1 TcpServerHelper派生于QTcpServer类

qt中server间跨线程通信时,要保证socket对象的创建与使用线程一致,不能在主线程创建,通过指针传递给子线程进程使用,正确做法是重写socketServer中的incommingConnetction()方法,与客户端连接请求进行设定匹配,即创建一个QTcpServer派生的server类,并重写incomingConnection方法。主要功能仅是 当客户端发起新链接时,该函数会被自动调用,仅向外发送socket描述符。

class TcpServerHelper : public QTcpServer
{
    Q_OBJECT
public:
    explicit TcpServerHelper(QObject *parent = nullptr);
protected:
    //重写incomingConnection,用于多线程通讯,子线程中不能使用主线程中创建的套接字对象
    void incomingConnection(qintptr socketDescriptor);
signals:
    void newSockDescriptor(qintptr _sock);
};


//.cpp
TcpServerHelper::TcpServerHelper(QObject *parent) : QTcpServer(parent){}
//当客户端发起新链接时,该函数会被自动调用,仅向外发送socket描述符
void TcpServerHelper::incomingConnection(qintptr socketDescriptor)
{
    emit newSockDescriptor(socketDescriptor);
}

3.2.2 ReceFile线程任务类

由于该线程仅处理接收数据功能,线程功能比较单一,故派生QThread子类,并重写run()方法来实现多线程

class ReceFile() : public Qthread
class ReceFile(qintptr _socketDesc,QObject* parent=nullptr);
void run();//taskInterface,run函数中要注意使用exec()保持子线程时刻监听,避免子线程退出问题

//通过signal信号来向主线程接收任务完成情况及线程间的通信
signals:
    void readDoneSig();
void ReceFile::run()
{
    //子线程中创建tcpSocket对象,设置socket描述符,此时将和发起链接的客户端进行通信
    m_tcpSocket=new QTcpSocket;
    m_tcpSocket->setSocketDescriptor(m_sockDecriptor);

    QFile* file=new QFile("recv.txt");
    file->open(QFile::WriteOnly);

    //接收数据
    connect(m_tcpSocket,&QTcpSocket::readyRead,[=]()
    {
        static int count=0;
        static int total=0;
        //第一次读文件信息,文件大小等
        if(count==0)
            m_tcpSocket->read((char*)&total,4);

        //读剩余数据
        QByteArray readDatas=m_tcpSocket->readAll();
        count+=readDatas.size();

        //判断数据是否接收完毕
        if(count==total)
        {
                m_tcpSocket->close();
                m_tcpSocket->deleteLater();
                m_tcpSocket=nullptr;
                file->close();
                file->deleteLater();

                emit readDoneSig();
        }

    });

    //子线程进入事件循环,保持时刻监听
    exec();
}

3.2.3 主线程

主线程中当客户端发起连接请求时才开启子线程工作,即:
 connect(m_tcpServer,&TcpServerHelper::newSockDescriptor,this,[=](qintptr _sockDesc)
    {
        //创建子线程,并启动线程
        ReceFile* recvThread=new ReceFile(_sockDesc);
        recvThread->start();

        //资源释放,善后工作
        connect(recvThread,&ReceFile::readDoneSig,[=]()
        {
            //子线程退出
            recvThread->exit();
            recvThread->wait();
            recvThread->deleteLater();
        });
    });

3.3 关于Qt中socket跨线程通讯问题

主线程创建的socket套接字对象,传入到子线程中,可能因为qt版本或者windows平台问题,日志可能会报主线程创建的socket对象不能在子线程工作。另外,一个server对应一个client线程,可能没有问题出现,但是QTcpServer若为每个客户端分配一个独立线程【典型的如聊天室】,必须重写 IncomingConnection()函数。 Qt帮助文档:不能在线程中调用QTcpServer自动创建的QTcpSocket对象,并且在incomingConnection()的帮助中有提到,若将客户端的连接传入单独的线程,则QTcpSocket对象必须创建在线程中,socket对象的创建通过重写incomingConnection()函数实现。

QTcpServer类的工作机制:

  1. 在有传入连接时,QTcpServer会创建一个与客户端匹配的socket,并返回一个指向socket内存的socketDescriptor(socket描述符),在QT中该描述符是qintptr类型的。
  2. 然后,QTcpServer会自动调用incomingConnection()函数,该函数接收这个socketDescriptor。

incomingConnection源码实现:

  1. 首先,创建了一个QTcpSocket对象,
  2. 然后,调用QTcpSocket::setSocketDescriptor(qintptr socketDescriptor),设置socket描述符;
  3. 最后,调用addPendingConnection(QTcpSocket * socket),将创建的QTcpSocket对象指针挂起在已创建列表中,该行为可终止waitForNewConnection()的阻塞,并且用户可以通过调用nextPendingConnection()函数获得这个QTcpSocket对象指针。注意:在线程版的incomingConnection()函数中,可以省略这步addPendingConnection()的调用,因为不再需要通过nextPendingConnection()函数来获得socket指针了。 重写incomingConnection()函数: 重写函数需要创建一个QTcpServer的派生类,另外还需创建一个线程类。

文章转自博客园(斗战胜佛美猴王):Qt中跨进程Socket通信以及socket跨线程通信 - 斗战胜佛美猴王 - 博客园

本文福利,莬费领取Qt开发学习资料包、技术视频,内容包括(C++语言基础,Qt编程入门,QT信号与槽机制,QT界面开发-图像绘制,QT网络,QT数据库编程,QT项目实战,QSS,OpenCV,Quick模块,面试题等等)↓↓↓↓↓↓见下面↓↓文章底部点击莬费领取↓↓

猜你喜欢

转载自blog.csdn.net/QtCompany/article/details/132239177