QT网络通信的接口与使用


文章目录

  • 前言
  • 1.服务端实现流程
    • 1.1步骤 1:创建 QTcpServer 并监听端口
    • 1.2步骤 2:处理新连接请求
    • 1.3步骤 3:接收客户端数据
    • 1.4步骤 4:处理客户端断开
  • 2.客户端实现流程
    • 2.1步骤 1:创建 QTcpSocket 并连接服务器
    • 2.2步骤 2:发送数据
    • 2.3步骤 3:接收服务器回复
    • 2.4步骤 4:处理连接和错误
  • 3.关键注意事项
  • 4.TCP粘包问题及其处理
    • 4.1TCP粘包是什么
    • 4.2TCP粘包为什么会产生
    • 4.3TCP粘包的解决方案


前言

在Qt中实现TCP通信主要依赖 QTcpServer(服务端)和 QTcpSocket(客户端和服务端通信)类。

TCP/IP通信(即SOCKET通信)是通过网线将服务器Server端和客户机Client端进行连接,在遵循ISO/OSI模型的四层层级构架的基础上通过TCP/IP协议建立的通讯。控制器可以设置为服务器端或客户端。

服务端(简化版)

class MyServer : public QObject {Q_OBJECT
public:MyServer(QObject *parent = nullptr) : QObject(parent) {server = new QTcpServer(this);connect(server, &QTcpServer::newConnection, this, &MyServer::onNewConnection);server->listen(QHostAddress::Any, 8888);}private slots:void onNewConnection() { /* ... */ }void onReadyRead() { /* ... */ }void onDisconnected() { /* ... */ }private:QTcpServer *server;QList<QTcpSocket*> m_clients;
};

客户端(简化版)

class MyClient : public QObject {Q_OBJECT
public:MyClient(QObject *parent = nullptr) : QObject(parent) {socket = new QTcpSocket(this);connect(socket, &QTcpSocket::connected, this, &MyClient::onConnected);connect(socket, &QTcpSocket::readyRead, this, &MyClient::onReadyRead);socket->connectToHost("127.0.0.1", 8888);}void send(const QString &message) {socket->write(message.toUtf8());}private slots:void onConnected() { /* ... */ }void onReadyRead() { /* ... */ }private:QTcpSocket *socket;
};

运行效果
服务端启动后监听端口,客户端连接并发送数据。
服务端接收数据并回复,客户端显示回复内容。
断开连接后资源自动释放。

1.服务端实现流程

1.1步骤 1:创建 QTcpServer 并监听端口

// 创建TCP服务端对象
QTcpServer *server = new QTcpServer(this);// 监听所有IP的指定端口(例如8888)
if (!server->listen(QHostAddress::Any, 8888)) {qDebug() << "Server could not start. Error:" << server->errorString();
} else {qDebug() << "Server started on port 8888";
}

1.2步骤 2:处理新连接请求

当客户端连接时,QTcpServer 会触发 newConnection 信号,需通过槽函数处理:

// 连接信号到槽函数
connect(server, &QTcpServer::newConnection, this, &MyServer::onNewConnection);// 槽函数实现
void MyServer::onNewConnection() {// 获取新连接的socket对象QTcpSocket *socket = server->nextPendingConnection();// 存储socket以便后续通信(例如添加到列表)m_clients.append(socket);// 处理客户端数据到达的信号connect(socket, &QTcpSocket::readyRead, this, &MyServer::onReadyRead);// 处理断开连接的信号connect(socket, &QTcpSocket::disconnected, this, &MyServer::onDisconnected);
}

1.3步骤 3:接收客户端数据

通过 readyRead 信号读取数据:

void MyServer::onReadyRead() {QTcpSocket *socket = qobject_cast<QTcpSocket*>(sender());if (!socket) return;QByteArray data = socket->readAll();qDebug() << "Received data:" << data;// 示例:回复客户端socket->write("Server received: " + data);
}

1.4步骤 4:处理客户端断开

void MyServer::onDisconnected() {QTcpSocket *socket = qobject_cast<QTcpSocket*>(sender());if (!socket) return;m_clients.removeOne(socket);socket->deleteLater();qDebug() << "Client disconnected";
}

2.客户端实现流程

2.1步骤 1:创建 QTcpSocket 并连接服务器

QTcpSocket *socket = new QTcpSocket(this);// 连接服务器(假设服务器IP为127.0.0.1,端口8888)
socket->connectToHost("127.0.0.1", 8888);// 监听连接成功信号
connect(socket, &QTcpSocket::connected, this, &MyClient::onConnected);// 监听数据到达信号
connect(socket, &QTcpSocket::readyRead, this, &MyClient::onReadyRead);// 监听错误信号
connect(socket, &QTcpSocket::errorOccurred, this, &MyClient::onError);

2.2步骤 2:发送数据

void MyClient::sendData(const QByteArray &data) {if (socket->state() == QAbstractSocket::ConnectedState) {socket->write(data);socket->flush(); // 确保立即发送}
}

2.3步骤 3:接收服务器回复

void MyClient::onReadyRead() {QByteArray data = socket->readAll();qDebug() << "Server response:" << data;
}

2.4步骤 4:处理连接和错误

void MyClient::onConnected() {qDebug() << "Connected to server!";
}void MyClient::onError(QAbstractSocket::SocketError error) {qDebug() << "Error:" << socket->errorString();
}

3.关键注意事项

  1. 异步通信:
    Qt的TCP操作基于事件循环,所有操作(连接、读写)都是异步的,需通过信号槽处理结果。

  2. 数据分包与粘包:
    TCP是流式协议,需自行处理数据边界(例如定义协议头尾或使用长度前缀)。

  3. 资源管理:
    及时释放断开连接的 QTcpSocket 对象(调用 deleteLater)。

  4. 跨线程操作:
    若在多线程中使用,需将 QTcpSocket 或 QTcpServer 移至子线程(使用 moveToThread)。

4.TCP粘包问题及其处理

4.1TCP粘包是什么

TCP的粘包和拆包问题往往出现在基于TCP协议的通讯中,比如RPC框架、Netty等。

TCP在接受数据的时候,有一个滑动窗口来控制接受数据的大小,这个滑动窗口你就可以理解为一个缓冲区的大小。缓冲区满了就会把数据发送。数据包的大小是不固定的,有时候比缓冲区大有时候小。
如果一次请求发送的数据量比较小,没达到缓冲区大小,TCP则会将多个请求合并为同一个请求进行发送,这就形成了粘包问题;
如果一次请求发送的数据量比较大,超过了缓冲区大小,TCP就会将其拆分为多次发送,这就是拆包,也就是将一个大的包拆分为多个小包进行发送。

这是最好理解的粘包问题的产生原因。还有一些其他的原因比如
1   客户端的发送频率远高于服务器的接收频率,就会导致数据在服务器的tcp接收缓冲区滞留形成粘连,比如客户端1s内连续发送了两个hello world!,服务器过了2s才接收数据,那一次性读出两个hello world!。
2   tcp底层的安全和效率机制不允许字节数特别少的小包发送频率过高,tcp会在底层累计数据长度到一定大小才一起发送,比如连续发送1字节的数据要累计到多个字节才发送,可以了解下tcp底层的Nagle算法。
3   再就是我们提到的最简单的情况,发送端缓冲区有上次未发送完的数据或者接收端的缓冲区里有未取出的数据导致数据粘连。
在这里插入图片描述

4.2TCP粘包为什么会产生

1.TCP会发生粘包问题:TCP 是面向连接的传输协议,TCP 传输的数据是以流的形式,而流数据是没有明确的开始结尾边界,所以 TCP 也没办法判断哪一段流属于一个消息;TCP协议是流式协议;所谓流式协议,即协议的内容是像流水一样的字节流,内容与内容之间没有明确的分界标志,需要认为手动地去给这些协议划分边界。
粘包时:发送方每次写入数据 < 接收方套接字(Socket)缓冲区大小。
拆包时:发送方每次写入数据 > 接收方套接字(Socket)缓冲区大小。

2.UDP不会发生粘包问题:UDP具有保护消息边界,在每个UDP包中就有了消息头(UDP长度、源端口、目的端口、校验和)。
粘包拆包问题在数据链路层、网络层以及传输层都有可能发生。日常的网络应用开发大都在传输层进行,由于UDP有消息保护边界,不会发生粘包拆包问题,因此粘包拆包问题只发生在TCP协议中

4.3TCP粘包的解决方案

  1. 客户端在发送数据包的时候,每个包都固定长度,比如1024个字节大小,如果客户端发送的数据长度不足1024个字节,则通过补充空格的方式补全到指定长度;
  2. 客户端在每个包的末尾使用固定的分隔符,例如\r\n,如果一个包被拆分了,则等待下一个包发送过来之后找到其中的\r\n,然后对其拆分后的头部部分与前一个包的剩余部分进行合并,这样就得到了一个完整的包;
  3. 将消息分为头部和消息体,在头部中保存有当前整个消息的长度,只有在读取到足够长度的消息之后才算是读到了一个完整的消息;
  4. 通过自定义协议进行粘包和拆包的处理。

优缺点分析

  • 解决方案1:固定数据大小
    虽然这种方式可以解决粘包问题,但这种固定数据大小的传输方式,当数据量比较小时会使用空字符来填充,所以会额外的增加网络传输的负担,因此不是理想的解决方案。
  • 解决方案2:特殊字符结尾
    以特殊符号作为粘包的解决方案的最大优点是实现简单,但存在一定的局限性,比如当一条消息中间如果出现了结束符就会造成半包的问题,所以如果是复杂的字符串要对内容进行编码和解码处理,这样才能保证结束符的正确性。
  • 解决方案4:设置消息头
    此解决方案可以解决粘包问题,并且对于空间的利用也相对高
  • 解决方案4:自定义请求协议
    此解决方案虽然可以解决粘包问题,但消息的设计和代码的实现复杂度比较高,所以也不是理想的解决方案

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/73312.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

华为OD机试2025A卷七日集训第1期 - 按算法分类,由易到难,循序渐进,玩转OD(Python/JS/C/C++)

目录 一、适合人群二、本期训练时间三、如何参加四、7日集训第1期五、精心挑选21道高频100分经典题目&#xff0c;作为入门。第1天、逻辑分析第2天、逻辑分析第3天、逻辑分析第4天、逻辑分析第5天、双指针第6天、二叉树第7天、回溯 六、集训总结六、国内直接使用最新GPT-4.5、满…

Qt 重入和线程安全

重入和线程安全 在整个文档中&#xff0c;"重入"和 "线程安全 "这两个术语被用来标记类和函数&#xff0c;以表明它们在多线程应用程序中的使用方式&#xff1a; 线程安全函数可以同时被多个线程调用&#xff0c;即使调用使用的是共享数据&#xff0c;因…

Elasticsearch:构建 AI 驱动的搜索体验

Elasticsearch 介绍 当你开始使用 Elastic 时&#xff0c;你将使用 Elasticsearch Relevance Engine™&#xff08;ESRE&#xff09;&#xff0c;它专为 AI 搜索应用程序提供支持。借助 ESRE&#xff0c;你可以利用一整套开发者工具&#xff0c;包括 Elastic 的文本搜索、向量…

鸿蒙生态开发

鸿蒙生态开发概述 鸿蒙生态是华为基于开源鸿蒙&#xff08;OpenHarmony&#xff09;构建的分布式操作系统生态&#xff0c;旨在通过开放共享的模式连接智能终端设备、操作系统和应用服务&#xff0c;覆盖消费电子、工业物联网、智能家居等多个领域。以下从定义与架构、核心技术…

JVM如何处理Java中的精度转换: 从源码到字节码

你好&#xff0c;我是 shengjk1&#xff0c;多年大厂经验&#xff0c;努力构建 通俗易懂的、好玩的编程语言教程。 欢迎关注&#xff01;你会有如下收益&#xff1a; 了解大厂经验拥有和大厂相匹配的技术等 希望看什么&#xff0c;评论或者私信告诉我&#xff01; 文章目录 一…

vue-next-admin修改配置指南

官方文档地址&#xff1a;vue-next-admin 1.如何开启侧边栏logo 在scr-layout-navbars-topBar-setings.vue中添加 getThemeConfig.value.isShowLogo true; 设置为true即可默认打开 2.修改侧边栏顶部的logo与文字 先把想要的图标存到我的项目然后下载 然后把后面的几个文件拉…

gin学习

gin学习笔记&#xff0c;不仅包含了基本的增删查改外&#xff0c;还包括参数传递&#xff0c;上传下载&#xff0c;模版、session与中间件等&#xff0c;方便收藏自习可用 文章目录 获得个请求get打印字符串get请求xmlget请求跳转http方法路由可以通过Context的Param方法来获取…

Flutter运行错误:UG! exception in phase ‘semantic analysis‘

最近在Mac Mini M4上通过Android Studio导入Flutter项目并运行&#xff0c;结果一直跑不起来&#xff0c;错误日志如下&#xff1a; 执行命令查看版本信息&#xff1a; flutter doctor --verbose通过输出信息Java version OpenJDK Runtime Environment (build 21.0.41242208…

【计算机网络运输层详解】

文章目录 一、前言二、运输层的功能1. 端到端通信2. 复用与分用3. 差错检测4. 流量控制5. 拥塞控制 三、运输层协议&#xff1a;TCP 和 UDP1. TCP&#xff1a;面向连接的可靠传输协议2. UDP&#xff1a;无连接的传输协议 四、端口号与进程通信1. 端口号分类2. 端口通信模型 五、…

51单片机和STM32 入门分析

51单片机和STM32是嵌入式开发中两种主流的微控制器&#xff0c;它们在架构、性能、应用场景等方面存在显著差异。以下是两者的对比分析及选择建议&#xff1a; 1. 51单片机与STM32的定义与特点 51单片机 定义&#xff1a;基于Intel 8051内核的8位微控制器&#xff0c;结构简单…

开源视觉语言模型MiniMax-VL-01:动态分辨率+4M超长文本,性能比肩GPT-4o

在人工智能领域&#xff0c;构建能够像人类一样理解、思考和行动的智能体&#xff08;AI Agent&#xff09;一直是研究人员的终极目标之一。而实现这一目标的关键在于模型是否具备足够强大的感知能力、记忆能力和推理能力。近期&#xff0c;国内人工智能公司MiniMax重磅开源了其…

excel 列单元格合并(合并列相同行)

代码 首先自定义注解CellMerge&#xff0c;用于标记哪些属性需要合并&#xff0c;哪个是主键**&#xff08;这里做了一个优化&#xff0c;可以标记多个主键&#xff09;** import org.dromara.common.excel.core.CellMergeStrategy;import java.lang.annotation.*;/*** excel…

flowable适配达梦7 (2.1)

经过第一版的问题解决&#xff0c;后端项目可以启动&#xff0c;前端页面也集成进去。 前端在流程设计页面报错 之后发现主要是组件中modelerStore这个值没有 解决方法:在data增加对象 给component/process/designer.vue 中涉及到的每个子组件传入 :modelerStore“modeler…

Prometheus Exporter系列-Mysql_Exporter一键部署

新项目旧项目都需要给研发配置mysql监控&#xff0c;这里mysql监控对应aws 阿里云 腾讯云 华为云的云mysql产品或开源自建mysql。 exporter安装虽然简单&#xff0c;经常手动操作不免让人心烦&#xff0c;一键完成省去繁琐的常规操作。 配置信息对的情况下测试多次都可以正常安…

2025年移动端开发性能优化实践与趋势分析

启动速度优化 本质&#xff1a;缩短首次可见帧渲染时间。 方法&#xff1a; iOS&#xff1a;利用Core ML本地模型轻量化部署&#xff0c;减少云端等待。Android&#xff1a;强制启用SplashScreen API&#xff0c;通过setKeepOnScreenCondition控制动画时长。冷启动需将耗时操…

【MySQL篇】DEPENDENT SUBQUERY(依赖性子查询)优化:从百秒到秒级响应的四种优化办法

&#x1f4ab;《博主介绍》&#xff1a;✨又是一天没白过&#xff0c;我是奈斯&#xff0c;从事IT领域✨ &#x1f4ab;《擅长领域》&#xff1a;✌️擅长阿里云AnalyticDB for MySQL(分布式数据仓库)、Oracle、MySQL、Linux、prometheus监控&#xff1b;并对SQLserver、NoSQL(…

全文 - MLIR Toy Tutorial Chapter 1: Toy Language and AST

Toy 语言 本教程&#xff0c;将会借助一个玩具语言来讲解&#xff0c;这个语言我们称其为 Toy。Toy 是一个基于张量的语言&#xff0c;它允许你定义函数&#xff0c;执行一些数学计算&#xff0c;并且打印结果。做这样的设定&#xff0c;是因为我们希望让教程保持简明&#xff…

排序复习_代码纯享

头文件 #pragma once #include<iostream> #include<vector> #include<utility> using std::vector; using std::cout; using std::cin; using std::endl; using std::swap;//插入排序 //1、直接插入排序&#xff08;稳定&#xff09; void InsertSort(vecto…

CSS语言的双向链表

CSS语言的双向链表 引言 在计算机科学中&#xff0c;数据结构是一个极为重要的概念&#xff0c;而链表则是最常见的数据结构之一。链表可以分为单向链表和双向链表&#xff0c;其中双向链表因其灵活性和高效性而受到广泛应用。在前端开发的领域&#xff0c;尤其是CSS&#xf…

简单理解机器学习中top_k、top_p、temperature三个参数的作用

AI系列文章&#xff1a; AWS AI认证考试中经常提及几个重要的工具介绍 简单理解机器学习中top_k、top_p、temperature三个参数的作用 用Deepseek Kimi 快速生成高质量的ppt 在机器学习中&#xff0c;top_k、top_p 和 temperature 是用于控制生成模型&#xff08;如语言模型…