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

二 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. 然后,QTcpSer​​​​​​​ver会自动调用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跨线程通信的更多相关文章

  1. ZeroMq实现跨线程通信

    ZeroMq实现跨线程通信 之前在技术崇拜的技术经理指导下阅读了ZeroMq的基础代码,现在就将阅读的心得与成果记录一下,并重新模仿实现了一下经理的异步队列. 1.对外接口 //主要接口(1)void ...

  2. Zenject与UniRx结合实现跨线程通信Signal

    修改Zenject下ProfileBlock.cs源码, 取消有关UnityEngine.Profiling.Profiler的代码. 然后使用Zenject的Signal: // 定义Signal ...

  3. Qt自带的阴影类、跨线程问题汇总、hover相关、全屏轮子,一些思考。

    一点思考:故事的结局重不重要? 我语文不好,但是我数学不好. 我数学不好,但是我英语不好. 我英语不好,但是我物理不好. 我物理不好,但是我化学不好. 我化学不好,但是我历史不好. 我历史不好,但是我 ...

  4. samephore()信号量跨线程通信

    samephore1: #include <stdio.h> #include <stdlib.h> #include <Windows.h> ] = " ...

  5. JNI加载Native Library 以及 跨线程和Qt通信

    Part1 Java Native Interface-JNI-JAVA本地调用 JNI标准是Java平台的一部分, 允许Java代码和其他语言进行交互; 开始实现-> Step 1) 编写Ja ...

  6. Android中线程通信的方式

    Android 跨线程通信 android 中是不允许在主线程中进行 网络访问等事情的因为UI如果停止响应5秒左右的话整个应用就会崩溃,到Android4.0 以后 Google强制规定,与网络相关的 ...

  7. JAVA多线程提高二:传统线程的互斥与同步&传统线程通信机制

    本文主要是回顾线程之间互斥和同步,以及线程之间通信,在最开始没有juc并发包情况下,如何实现的,也就是我们传统的方式如何来实现的,回顾知识是为了后面的提高作准备. 一.线程的互斥 为什么会有线程的互斥 ...

  8. java多线程——线程通信

    一.线程通信目标 1.线程通信的目标是使线程间能够互相发送信号 2.线程通信使线程能够等待其他线程的信号 二.几种方式 1.通过共享对象 2.忙等待 线程 B 运行在一个循环里,以等待信号 (不释放c ...

  9. Java多线程-同步:synchronized 和线程通信:生产者消费者模式

    大家伙周末愉快,小乐又来给大家献上技术大餐.上次是说到了Java多线程的创建和状态|乐字节,接下来,我们再来接着说Java多线程-同步:synchronized 和线程通信:生产者消费者模式. 一.同 ...

  10. Android 进阶12:进程通信之 Socket (顺便回顾 TCP UDP)

    不要害怕困难,这是你进步的机会! 读完本文你将了解: OSI 七层网络模型 TCPIP 四层模型 TCP 协议 TCP 的三次握手 TCP 的四次挥手 UDP 协议 Socket 简介 Socket ...

随机推荐

  1. 获取微信小程序列表渲染 index

    微信小程序列表渲染 index(索引值)通过 wx:for-index="index" 来获取: <view class="item" wx:for=&q ...

  2. android 投屏

    https://blog.csdn.net/aa464971/article/details/83349215

  3. 【2】java之object类

    一.Object 类的基本定义 ​ Object 类是所有类的父类,最大的一个好处就是:利用 Object 类可以接收所有类的对象(向上自动转型).如果不确定参数类型,使用 Object 类型是最好的 ...

  4. Kubernetes持久化存储

    一.emptyDir持久化存储配置 emptyDir 的一些用途: 缓存空间,例如基于磁盘的归并排序. 为耗时较长的计算任务提供检查点,以便任务能方便地从崩溃前状态恢复执行. 在 Web 服务器容器服 ...

  5. org.elasticsearch.ElasticsearchException: not all primary shards of [.geoip_databases] index are active解决办法

    解决办法 在配置elasticsearch.yml中加上 ingest.geoip.downloader.enabled: false

  6. 在Linux中实现打印目录程序遇到问题及解决

    今日阅读Linux程序设计第四版时,书中给出了一段实例代码,功能为实现/home目录下各级目录结构,当然不一定非得是/home下目录才可以,任何一级目录都可以. 自己尝试在Ubuntu系统运行编译,实 ...

  7. Altium Designer Winter 09 — 01 — 快速创建项目

    新建项目 新建原理图 导入所需的库 添加元器件和接插件 连接导线 自动标注.修改元件属性 编译前--修改项目属性 编译,查看消息 生成网表.BOM.简易BOM,打印文件

  8. 各版本JDK及镜像仓库

    Adoptium Marketplace 毕昇(华为) Dragonwell(阿里) Tencent Kona OpenJDK OracleJDK RedHatOpenJDK Temurin(ecli ...

  9. myJRebel 已不可用

    周末在家里撸代码,突然 IDEA 提示 JRebel 需要激活. 原来一直使用的 myJRebel 的激活码,天真的以为是我的网络问题,尝试重新激活,结果不管用,就想去 myJrebel 的网站上去看 ...

  10. Cannot read property 'xxxx' of null

    在使用的vue3+element-plus的项目中,我使用了form表单对数据进行校验. 但是使用了: refDom.value.validate((valid) => {}) 后,过一会控制台 ...