Qt中跨进程Socket通信以及socket跨线程通信
一 QTcpServer 创建流程
- 创建套接字服务器 QTcpServer 对象,
- 通过 QTcpServer 对象设置监听,即:QTcpServer::listen()
- 基于 QTcpServer::newConnection() 信号检测是否有新的客户端连接
- 如果有新的客户端连接调用*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创建流程
- 创建套接字服务器 QTcpSocket对象,
- 连接服务器,绑定服务器端绑定的IP和端口信息, QAbstractSocket::connectToHost(QHostAddress("127.0.0.1"),8888)
- 检测是否与服务器连接成功,connectToHost() 函数并成功建立连接之后发出 connected() 信号
- 使用通信的套接字对象 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类的工作机制:
- 在有传入连接时,QTcpServer会创建一个与客户端匹配的socket,并返回一个指向socket内存的socketDescriptor(socket描述符),在QT中该描述符是qintptr类型的。
- 然后,QTcpServer会自动调用incomingConnection()函数,该函数接收这个socketDescriptor。
incomingConnection源码实现:
- 首先,创建了一个QTcpSocket对象,
- 然后,调用QTcpSocket::setSocketDescriptor(qintptr socketDescriptor),设置socket描述符;
- 最后,调用addPendingConnection(QTcpSocket * socket),将创建的QTcpSocket对象指针挂起在已创建列表中,该行为可终止waitForNewConnection()的阻塞,并且用户可以通过调用nextPendingConnection()函数获得这个QTcpSocket对象指针。注意:在线程版的incomingConnection()函数中,可以省略这步addPendingConnection()的调用,因为不再需要通过nextPendingConnection()函数来获得socket指针了。 重写incomingConnection()函数: 重写函数需要创建一个QTcpServer的派生类,另外还需创建一个线程类。
Qt中跨进程Socket通信以及socket跨线程通信的更多相关文章
- ZeroMq实现跨线程通信
ZeroMq实现跨线程通信 之前在技术崇拜的技术经理指导下阅读了ZeroMq的基础代码,现在就将阅读的心得与成果记录一下,并重新模仿实现了一下经理的异步队列. 1.对外接口 //主要接口(1)void ...
- Zenject与UniRx结合实现跨线程通信Signal
修改Zenject下ProfileBlock.cs源码, 取消有关UnityEngine.Profiling.Profiler的代码. 然后使用Zenject的Signal: // 定义Signal ...
- Qt自带的阴影类、跨线程问题汇总、hover相关、全屏轮子,一些思考。
一点思考:故事的结局重不重要? 我语文不好,但是我数学不好. 我数学不好,但是我英语不好. 我英语不好,但是我物理不好. 我物理不好,但是我化学不好. 我化学不好,但是我历史不好. 我历史不好,但是我 ...
- samephore()信号量跨线程通信
samephore1: #include <stdio.h> #include <stdlib.h> #include <Windows.h> ] = " ...
- JNI加载Native Library 以及 跨线程和Qt通信
Part1 Java Native Interface-JNI-JAVA本地调用 JNI标准是Java平台的一部分, 允许Java代码和其他语言进行交互; 开始实现-> Step 1) 编写Ja ...
- Android中线程通信的方式
Android 跨线程通信 android 中是不允许在主线程中进行 网络访问等事情的因为UI如果停止响应5秒左右的话整个应用就会崩溃,到Android4.0 以后 Google强制规定,与网络相关的 ...
- JAVA多线程提高二:传统线程的互斥与同步&传统线程通信机制
本文主要是回顾线程之间互斥和同步,以及线程之间通信,在最开始没有juc并发包情况下,如何实现的,也就是我们传统的方式如何来实现的,回顾知识是为了后面的提高作准备. 一.线程的互斥 为什么会有线程的互斥 ...
- java多线程——线程通信
一.线程通信目标 1.线程通信的目标是使线程间能够互相发送信号 2.线程通信使线程能够等待其他线程的信号 二.几种方式 1.通过共享对象 2.忙等待 线程 B 运行在一个循环里,以等待信号 (不释放c ...
- Java多线程-同步:synchronized 和线程通信:生产者消费者模式
大家伙周末愉快,小乐又来给大家献上技术大餐.上次是说到了Java多线程的创建和状态|乐字节,接下来,我们再来接着说Java多线程-同步:synchronized 和线程通信:生产者消费者模式. 一.同 ...
- Android 进阶12:进程通信之 Socket (顺便回顾 TCP UDP)
不要害怕困难,这是你进步的机会! 读完本文你将了解: OSI 七层网络模型 TCPIP 四层模型 TCP 协议 TCP 的三次握手 TCP 的四次挥手 UDP 协议 Socket 简介 Socket ...
随机推荐
- 02#Web 实战:实现树形控件
前言 这是一篇个人练习 Web 前端各种常见的控件.组件的实战系列文章.本篇文章将介绍个人通过 JQuery + 无序列表 + CSS 动画完成一个简易的树形控件. 最终实现的效果是: 这样结构比较复 ...
- OpenAI Chat completion API 入门指南
Chat completion API 入门指南 ChatGPT 由 OpenAI 最先进的语言模型 gpt-3.5-turbo 提供支持. 使用 OpenAI API,您可以使用 GPT-3.5-t ...
- 记录篇-浪潮服务器raid卡
在说raid卡之前,首先,我们要明确,不同的raid卡型号,会有不同的配置方式,但是总体来说是大同小异的 这里举例浪潮机架式服务器经常用到的raid卡型号: PM8204 (其实像3108 ...
- RT-Thread Studio使用——创建工程并配置外部时钟(转)
硬件:正点原子阿波罗F429开发板,主控STM32F429IGT6,晶振25MHz. 软件:RT-Thread Studio RT-Thread版本:4.1.0 1.创建工程 根据所使用的硬件信息,配 ...
- Column count doesn't match value count at row 1存储的数据与数据库表的字段类型定义不相匹配
一.造成这个原因可能是一个关于创建json数据类型的mysql表格插入的一个报错提示: 26行为错误示范:27是正确书写规范.
- java运算符相关学习
java运算符 面试题1: 计算2*8如何操作效率更高? 剖析: 2 * 8 => 实际上是2 * 2 * 2 * 2 2<<3 System.out.println(2<&l ...
- ERNIE1-2
ERNIE1.0 ERNIE: Enhanced Representation through Knowledge Integration提出了三种级别的masking:(1)word piece级别 ...
- golang 数组(array)
1. 概念 golang中的数组是具有固定长度及相同数据类型的序列集合 2. 初始化数组 var 数组名 [数组大小]数据类型 package main import "fmt" ...
- 阶梯场景jp@gc - Stepping Thread Group (deprecated)
1.新建线程,添加配置元件.监听器 由上可见: 需要启动100个线程, 然后间隔30s就持续5s去启动10个线程, 那么就需要这样重复操作10次,才能100个线程全部启动. 最后整体100个线程持续运 ...
- 苹果App 上架 app store 提示 “构建版本错误”使用Application Loader发布App
步骤1 打开Application Loader(有2种方法) 或 步骤2 使用开发者帐号登录 步骤3 选择需要上传发布的ipa包 选择成功后,会显示ipa包的相关信息 步骤4 上传验证 上传成功 转 ...