一 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. 都在用 AI 生成美少女,而我却。。。

    最近 AI 画画特别的火,你能从网上看到非常多好看的图片,于是我就开始了我的安装之旅,我看到的图是这样的. 这样的. 还有这样的. 然后我就开始了我的 AI 安装生成计划. 安装环境 首先我们需要安装 ...

  2. 如何把已安装的nodejs高版本降级为低版本(图文教程)

    第一步.先清空本地安装的node.js版本 1.按健win+R弹出窗口,键盘输入cmd,然后敲回车(或者鼠标直接点击电脑桌面最左下角的win窗口图标弹出,输入cmd再点击回车键) 2.然后进入命令控制 ...

  3. Python连接hadoop-hive连接方法

    import impala.dbapi as ipdbconn = ipdb.connect(host='IP', port= 端口, database='数据库名', auth_mechanism= ...

  4. MySQL 中的锁有哪些类型,MySQL 中加锁的原则

    MySQL 中的锁理解 锁的类型 全局锁 缺点 适用范围 表级锁 表锁 元数据锁 意向锁 自增锁 行锁 Record Lock Gap Lock Next-Key Lock 插入意向锁 加锁的原则 1 ...

  5. 在线设计器 DesignO 的分析

    需求分析 现有POD网站的在线编辑器不是很好用. 可配置性不强,素材无法在后台实现管理 可扩展性不强,无法应用于多个行业,比如包装.服装 产品分析 官方网站:https://www.designnbu ...

  6. Oracle 11g 单机服务器ASM部署

    Oracle oracle,相比都有所了解,是一家企业级的数据库公司(收费),上图是oracle官网,也是对外的服务平台 oracle有自己独特的安装方式:ASM   :  自动存储管理(ASM,Au ...

  7. Linux驱动、应用调试技巧

    原文地址:https://hceng.cn/2019/04/08/Linux%E9%A9%B1%E5%8A%A8%E3%80%81%E5%BA%94%E7%94%A8%E8%B0%83%E8%AF%9 ...

  8. Cocos Creator微信登录接入(完全小白教程)(安卓篇)

    1:创建 Creator项目,如下

  9. 在docker中导入python的包时ImportError: libgthread-2.0.so.0: cannot open shared object file: No such file or directory

    问题: ImportError: libGL.so.1: cannot open shared object file: No such file or directory ImportError: ...

  10. JavaSE——this关键字

    this修饰的变量用于指代成员变量,其主要作用是(区分局部变量和成员变量的重名问题) 方法的形参如果与成员变量同名,不带this修饰的变量指的是形参,而不是成员变量 方法的形参没有与成员变量同名,不带 ...