一 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. HTML5----响应式(自适应)网页设计(自动适应屏幕大小)

    HTML5----响应式(自适应)网页设计(自动适应屏幕大小) 现在,很多项目都需要做响应式或者自适应的来适应我们不同屏幕尺寸的手机,电脑等设备,那么就需要我们在页面上下功夫,但移动端的布局不同于pc ...

  2. MySQL数据类型补充

    数据类型 整数数据类型 特殊说明: ​ 对于整数类型,MySQL还支持在类型名称后面加小括号(M),而小括号中的M表示显示宽度,M的取值范围是(0, 255).int(M)这个M在字段的属性中指定了u ...

  3. TNF拮抗剂的结构、功能与结核感染_Wallis2008

    中信国健临床通讯 2009年第2期 TNF拮抗剂的结构.功能与结核感染 Robert S. Wallis. THE LANCET Infectious Diseases. 2008; 8:601–61 ...

  4. .NET AsyncLocal 避坑指南

    目录 AsyncLocal 用法简介 AsyncLocal 实现原理 AsyncLocal 的坑 AsyncLocal 的避坑指南 HttpContextAccessor 的实现原理 AsyncLoc ...

  5. vue3.0中ref动态绑定

    // 自己使用 <div v-for="item in ['lisi','wanger']" :key="item"> <test :ref= ...

  6. elasticsearch 内存分配设置

    一.背景 elasticsearch版本为2.3.3 elasticsearch 默认安装后设置的内存是1GB,对于现实业务来说太小 预计在五台机器上配置elasticsearch构建集群,但是构建索 ...

  7. react 03 组件传值

    一 基础 props: 父传子  单向 import React from 'react'; import ReactDOM from 'react-dom'; import './index.css ...

  8. 数据库基础day1

    数据库基础 MySQL概述 SQL 函数 概念 函数 是指一段可以直接被另一段程序调用的程序或代码. 3.1字符串函数 函数 功能 CONCAT(S1,S2,...Sn) 字符串拼接,将S1,S2,. ...

  9. CodeGym自学笔记06——内存寻址和变量

    内存寻址和变量 print() print() 函数用于在屏幕上逐个字符显示文本.当屏幕上某一行没有足够空间时,文本开始在下一行显示. println() 可以使用 println() 函数停止在当前 ...

  10. Echarts 设置Y轴 || X轴的刻度只显示整数

    目前的效果如下:(有小数) 想要的效果如下:(整数) 解决方案: 在配置项的yAxis或者xAxis对象中添加属性:minInterval: 1,表示将刻度的最小间距设置为1 注意: minInter ...