QT网络通信
九、网络
- 基础概念
1.1 TCP/UDP
TCP/UDP
UDP TCP 协议相同点:都存在于传输层,全双工通信
TCP:全双工通信、面向连接、可靠
TCP(即传输控制协议):是一种面向连接的传输层协议,它能提供高可靠性通信(即数据无误、数据无丢失、数据无失序、数据无重复到达的通信)。
高可靠原因:1.三次握手、四次挥手
2. 序列号和应答机制
3. 超时,错误重传机制
适用场景
适合于对传输质量要求较高的通信
在需要可靠数据传输的场合,通常使用TCP协议
MSN/QQ等即时通讯软件的用户登录账户管理相关的功能通常采用TCP协议
UDP:全双工通信、面向无连接、不可靠
UDP(User Datagram Protocol)用户数据报协议,是不可靠的无连接的协议。在数据发送前,因为不需要进行连接,所以可以进行高效率的数据传输。
适用场景
发送小尺寸数据(如对DNS服务器进行IP地址查询时)
适合于广播/组播式通信中。
MSN/QQ/Skype等即时通讯软件的点对点文本通讯以及音视频通讯通常采用UDP协议
1.2 IP地址与端口号
IP地址
1. IP地址是Internet中主机的标识,本质: 二进制
IP地址一般分割为 4个八位二进制的数(4字节-32位)
2. Internet中的主机要与别的机器通信必须具有一个IP地址
3. IP地址(长度)为32位(IPv4),128位(IPv6),目前ipv6还未普及,主要学习ipv4
4. 每个数据包都必须携带目的IP地址和源IP地址,路由器依靠此信息为数据包选择路由
5. 表示方法 : 点分十进制
点分十进制表示就是用4组从0~255的数字,来表示一个IP地址。
端口号Port
1. 为了区分一台主机接收到的数据包应该给哪个进程来进行处理,使用端口号来区别
(通过 IP地址 找到哪台主机 通过 port端口号 来找到哪台主机的哪个进程)
2. TCP端口号与UDP端口号独立 (UDP port为8888,TCP port也可为8888 )
3. 端口号一般由IANA (Internet Assigned Numbers Authority) 管理
4. 端口(sin_prot)用2个字节表示 2byte (IP地址占4个字节)
众所周知端口(被占用): 1~1023(1~255之间为众所周知端口,通常由UNIX系统占用)
比如文件传输端口 TFTP 端口号为 69
已登记端口:1024~49151 (----可用来建立与其它主机的会话----)
动态或私有端口:49152~65535 --固定某些服务使用--
自定义的端口号建议使用2000以上,且非豹子号(例如6666/8888等)。
仅以使用2000以上,非豹子号的端口号,本次授课采用8887
2、准备工作
与数据库编程一样,Qt的网络功能需要在.pro项目配置文件中增加network模块。
网络编程主要用到两个类:
- QTcpServer
表示一个基于Tcp的服务器,需要注意的是,此类直接继承了QObject类,不继承QIODevice类,因此不具备任何IO的能力。
- QTcpSocket
表示一个基于TCP的Socket连接,间接继承了QIODevice类,因此使用此类对象进行IO读写。
3、相关函数
// 构造函数,堆内存开辟
QTcpServer::QTcpServer(QObject * parent = 0)
// 开启监听服务,等待客户端发起连接
// 参数1:监听来源(那个网段的IP地址),默认值表示不加任何限制
// 参数2:服务器占用的端口号。默认值为0表示随机选取。8887
bool QTcpServer::listen(const QHostAddress & address = QHostAddress::Any, quint16 port = 0)
// 查看当前是否正在监听
bool QTcpServer::isListening() const
// 关闭监听服务
void QTcpServer::close()
#include "dialog.h"
#include "ui_dialog.h"Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
// 抢占前台
setWindowFlags(Qt::WindowStaysOnTopHint);
server = new QTcpServer(this); // 开启监听
bool result = server->listen(QHostAddress::Any,8887);
if(!result)
{
ui->textBrowser->append("监听失败");
return;
}
ui->textBrowser->append("监听开启成功,端口号为8887");
}Dialog::~Dialog()
{
// 关闭监听功能
if(server->isListening()) // 判断是否正在监听
{
server->close();
}
delete ui;
}
客户端
// 构造函数,堆区开辟
QTcpSocket::QTcpSocket(QObject * parent = 0)
// 连接到服务器
// 参数1:服务器的IP地址
// 参数2:服务器的端口号
// 参数3:打开模式。默认读写模式
void QAbstractSocket::connectToHost(const QString & hostName,
quint16 port,
OpenMode openMode = ReadWritel)[virtual]
#include "dialog.h"
#include "ui_dialog.h"Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
setWindowFlags(Qt::WindowStaysOnTopHint); connect(ui->pushButtonConn,SIGNAL(clicked()),
this,SLOT(btnConnClickedSlot()));
connect(ui->pushButtonSend,SIGNAL(clicked()),
this,SLOT(btnSendClickedSlot())); socket = new QTcpSocket(this);
}Dialog::~Dialog()
{
// 如果数据流处于打开状态
if(socket->isOpen())
{
// 如果打开则关闭
socket->close();
}
delete ui;
}void Dialog::btnConnClickedSlot()
{
// 获取用户输入的IP和端口
QString ip = ui->lineEditIp->text();
int port = ui->spinBox->value(); // 建立连接
socket->connectToHost(ip,port);
}void Dialog::btnSendClickedSlot()
{}
我们怎么直到,是否有客户端连接了那?
所有通知类,第一时间想到到的信号槽。
// 每当有新的连接可以用时,就会发出此信号
void QTcpServer::newConnection() [signal]
// 连接成功的信号
void QAbstractSocket::connected()[signal]
// 连接失败的信号
void QAbstractSocket::disconnected()[signal]
#include "dialog.h"
#include "ui_dialog.h"Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
setWindowFlags(Qt::WindowStaysOnTopHint); connect(ui->pushButtonConn,SIGNAL(clicked()),
this,SLOT(btnConnClickedSlot()));
connect(ui->pushButtonSend,SIGNAL(clicked()),
this,SLOT(btnSendClickedSlot())); socket = new QTcpSocket(this);
connect(socket,SIGNAL(connected()),
this,SLOT(connectedSlot()));
connect(socket,SIGNAL(disconnected()),
this,SLOT(disconnectedSlot()));
}Dialog::~Dialog()
{
// 如果数据流处于打开状态
if(socket->isOpen())
{
// 如果打开则关闭
socket->close();
}
delete ui;
}void Dialog::btnConnClickedSlot()
{
// 获取用户输入的IP和端口
QString ip = ui->lineEditIp->text();
int port = ui->spinBox->value(); // 建立连接
socket->connectToHost(ip,port);
}void Dialog::btnSendClickedSlot()
{}// 连接成功
void Dialog::connectedSlot()
{
// 屏蔽连接按钮
ui->pushButtonConn->setEnabled(false);
ui->pushButtonConn->setText("已连接"); // 释放发送按钮
ui->pushButtonSend->setEnabled(true);
}// 断开连接
void Dialog::disconnectedSlot()
{
// 释放连接按钮
ui->pushButtonConn->setEnabled(true);
ui->pushButtonConn->setText("连接"); // 屏蔽发送按钮
ui->pushButtonSend->setEnabled(false);
}
// 返回与客户端连接的QTcpSocket对象
QTcpSocket * QTcpServer::nextPendingConnection()[virtual]
// 获取对面(客户端)的IP地址
// 返回值为IP地址封装类
QHostAddress QAbstractSocket::peerAddress() const
// 返回对面的(客户端)端口号
quint16 QAbstractSocket::peerPort() const
// 转换为IP地址字符串,在计算机中会自动增加一个前缀
QString QHostAddress::toString() const
客户端.cpp
#include "dialog.h"
#include "ui_dialog.h"Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
setWindowFlags(Qt::WindowStaysOnTopHint); connect(ui->pushButtonConn,SIGNAL(clicked()),
this,SLOT(btnConnClickedSlot()));
connect(ui->pushButtonSend,SIGNAL(clicked()),
this,SLOT(btnSendClickedSlot())); socket = new QTcpSocket(this);
connect(socket,SIGNAL(connected()),
this,SLOT(connectedSlot()));
connect(socket,SIGNAL(disconnected()),
this,SLOT(disconnectedSlot()));
}Dialog::~Dialog()
{
// 如果数据流处于打开状态
if(socket->isOpen())
{
// 如果打开则关闭
socket->close();
}
delete ui;
}void Dialog::btnConnClickedSlot()
{
// 获取用户输入的IP和端口
QString ip = ui->lineEditIp->text();
int port = ui->spinBox->value(); // 建立连接
socket->connectToHost(ip,port);
}void Dialog::btnSendClickedSlot()
{}// 连接成功
void Dialog::connectedSlot()
{
// 屏蔽连接按钮
ui->pushButtonConn->setEnabled(false);
ui->pushButtonConn->setText("已连接"); // 释放发送按钮
ui->pushButtonSend->setEnabled(true);
}// 断开连接
void Dialog::disconnectedSlot()
{
// 释放连接按钮
ui->pushButtonConn->setEnabled(true);
ui->pushButtonConn->setText("连接"); // 屏蔽发送按钮
ui->pushButtonSend->setEnabled(false);
}
服务端.cpp
#include "dialog.h"
#include "ui_dialog.h"Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
// 抢占前台
setWindowFlags(Qt::WindowStaysOnTopHint);
server = new QTcpServer(this); // 开启监听
bool result = server->listen(QHostAddress::Any,8887);
if(!result)
{
ui->textBrowser->append("监听失败");
return;
}
ui->textBrowser->append("监听开启成功,端口号为8887");
connect(server,SIGNAL(newConnection()),this,
SLOT(newConnectionSlot()));
}Dialog::~Dialog()
{
// 关闭监听功能
if(server->isListening()) // 判断是否正在监听
{
server->close();
}
delete ui;
}void Dialog::newConnectionSlot()
{
// 如果不是第一次连接,就踢掉上一个人
if(socket != NULL)
{
// 踢掉上一个人
socket->close();
} // 保存当前连接对象
socket = server->nextPendingConnection(); // 获取对面的IP地址
QString ip = socket->peerAddress().toString(); // 获取对面的端口号
quint16 port = socket->peerPort(); ip.prepend("新连接来了!").append(":").append(QString::number(port)); ui->textBrowser->append(ip);
}
// 构造函数
// 参数事Qt的读写类,可以是QFile,也可以是QTcpSocket........
QTextStream::QTextStream(QIODevice * device)
// 输出字符串内容,支持链式调用
QTextStream & operator<<(const QString & string)
// 有数据可读时发射
void QIODevice::readyRead()[signal]
// 读取最大长度为maxlen个QChar的内容,返回值为读取的字符串
QString QTextStream::read(qint64 maxlen)
// 读取所有字符
QString QTextStream::readAll()
// 一次读取一行文本
// 参数为一行文本的最大字符数
QString QTextStream::readLine(qint64 maxlen = 0)
dialog.cpp 服务端
void Dialog::readyReadSlot()
{
// 创建文本对象
QTextStream input(socket);
// 读取内容
QString text = input.read(128);
ui->textBrowser->append(text);
}
dialog.cpp 客户端
// 发送信息
void Dialog::btnSendClickedSlot()
{
QString text = ui->lineEditSend->text();
if(text == "")
{
QMessageBox::warning(this,"提示","请输入发送的内容");
return;
} // 方法一:QTextStream 文本流
// QTextStream直接使用Unicode编码,适合Qt与Qt之间通信。
// 简化文本的读写操作
QTextStream output(socket);
// 发送数据
output << text;
}
dialog.cpp 服务端
void Dialog::readyReadSlot()
{
// // 创建文本对象
// QTextStream input(socket);
// // 读取内容
// QString text = input.read(128);
// ui->textBrowser->append(text); // 读取所有内容
QByteArray bufer = socket->readAll();
// QByteArray -> QString
QString text(bufer);
// 显示
ui->textBrowser->append(text);
}
dialog.cpp 客户端
void Dialog::btnSendClickedSlot()
{
QString text = ui->lineEditSend->text();
if(text == "")
{
QMessageBox::warning(this,"提示","请输入发送的内容");
return;
} // 方法一:QTextStream 文本流
// QTextStream直接使用Unicode编码,适合Qt与Qt之间通信。
// 简化文本的读写操作
// QTextStream output(socket);
// // 发送数据
// output << text; // 方法二、QByteArray
// 以字节为单位,可以与其他编程语言通信
// 可以操作非文本内容
// QString -> QByteArray
QByteArray buffer = text.toUtf8();
socket->write(buffer);
}
相关文章:

QT网络通信
九、网络 基础概念 1.1 TCP/UDP TCP/UDP UDP TCP 协议相同点:都存在于传输层,全双工通信 TCP:全双工通信、面向连接、可靠 TCP(即传输控制协议):是一种面向连接的传输层协议,它能提供高可靠性通…...

案例分析|山西某光伏发电站轨道巡检机器人解决方案
随着光伏发电技术的不断发展,光伏变电站配电室作为能量转换和输送的关键节点,承担着重要的电力分配和保护功能。然而,传统的人工巡检方式存在诸多问题,如巡检周期长、效率低、安全风险高等,已经无法满足光伏变电站配电…...

Apache POl
介绍 Apache POl是一个处理Miscrosoft Ofice各种文件格式的开源项目。简单来说就是,我们可以使用 POI 在 Java 程序中对Miscrosoft Office各种文件进行读写操作,一般情况下,POI都是用于操作 Excel 文件。 Apache POl 的应用场景 1.银行网银系统导出交易…...
高防服务器托管应注意什么
选择高防服务器托管主要考虑的因素:1.服务商的服务器大小。2.服务器的防御值大小。3.服务器机房的位置以及机房的资质。 具体内容如下: 1.服务器大小是按照U来定的,U是一种表示服务器外部尺寸的单位(计量单位:高度或厚…...

swagger-ui.html报错404,解决办法
swagger-ui.html报错404,解决办法!现在后端开发项目中,为了节省时间,使用swagger插件,可以方便的快捷生成接口文档。但是如果你在请求前端页面路径比如:http://127.0.0.1:7777/swagger-ui.html。找不到。那是因为你的配…...
golang 函数式编程库samber/mo使用: Future
golang 函数式编程库samber/mo使用: Future 如果您对samber/mo库不了解, 请先阅读第一篇 Option 本节讲述Future的使用,它可以帮助我们处理异步编程问题。 示例 我们先来看看下面代码的示例, 注释解释了每一步的操作。 packa…...
【Spring连载】使用Spring Data访问 MongoDB(十四)----Mongodb特有的查询方法
【Spring连载】使用Spring Data访问 MongoDB(十四)----Mongodb特有的查询方法 一、定义通用查询方法二、MongoDB特有的查询方法2.1 地理空间查询Geo-spatial Queries2.2 基于JSON的查询方法和字段限制2.3 使用SpEL表达式的基于JSON的查询2.4 全文检索查询…...

消息中间件篇之RabbitMQ-消息重复消费
一、导致重复消费的情况 1. 网络抖动。 2. 消费者挂了。 消费者消费消息后,当确认消息还没有发送到MQ时,就发生网络抖动或者消费者宕机。那当消费者恢复后,由于MQ没有收到消息,而且消费者有重试机制,消费者就会再一次消…...
常见设计模式之单例模式
单例模式 单例模式是一种常用的软件设计模式,主要目的是确保一个类在整个应用程序生命周期中只有一个实例,并提供一个全局访问点以获取该实例。 单例模式分为几种不同的实现方式,包括懒汉模式和饿汉模式。每种方式都有其特点和适用场景。例如…...

VL817-Q7 USB3.0 HUB芯片 适用于扩展坞 工控机 显示器
VL817-Q7 USB3.1 GEN1 HUB芯片 VL817-Q7 USB3.1 GEN1 HUB芯片 VIA Lab的VL817是一款现代USB 3.1 Gen 1集线器控制器,具有优化的成本结构和完全符合USB标准3.1 Gen 1规范,包括ecn和2017年1月的合规性测试更新。VL817提供双端口和双端口4端口配置&…...

【Android安全】Windows 环境下载 AOSP 源码
准备环境 安装 git 安装 Python 硬盘剩余容量最好大于 800G 打开 Git Bash,用 git 克隆源代码仓库 git clone https://android.googlesource.com/platform/manifest.git //没有梯子使用清华源 git clone https://aosp.tuna.tsinghua.edu.cn/platform/manifest.git这…...

Vue.js+SpringBoot开发快递管理系统
目录 一、摘要1.1 项目介绍1.2 项目录屏 二、研究内容2.1 数据中心模块2.2 快递类型模块2.3 快递区域模块2.4 快递货架模块2.5 快递档案模块 三、界面展示3.1 登录注册3.2 快递类型3.3 快递区域3.4 快递货架3.5 快递档案3.6 系统基础模块 四、免责说明 一、摘要 1.1 项目介绍 …...

Linux/Spectra
Enumeration nmap 第一次扫描发现系统对外开放了22,80和3306端口,端口详细信息如下 22端口运行着ssh,80端口还是http,不过不同的是打开了mysql的3306端口 TCP/80 进入首页,点击链接时,提示域名不能解析&…...

C 嵌入式系统设计模式 08:硬件代理模式
本书的原著为:《Design Patterns for Embedded Systems in C ——An Embedded Software Engineering Toolkit 》,讲解的是嵌入式系统设计模式,是一本不可多得的好书。 本系列描述我对书中内容的理解。本文章描述访问硬件的设计模式之一&…...

【k8s配置与存储--持久化存储(PV、PVC、存储类)】
1、PV与PVC 介绍 持久卷(PersistentVolume,PV) 是集群中的一块存储,可以由管理员事先制备, 或者使用存储类(Storage Class)来动态制备。 持久卷是集群资源,就像节点也是集群资源一样…...

【Vite】解决Vite http proxy error: Error: connect ECONNREFUSED
今天写bug,发现了这个问题 我经过我一晚上的搜索努力,在github上找到了解决办法,不得不说,交友网站还是很好用的。 参考 这一行是关键代码。 因为我连的是本地后台服务,所以最后配置成这样 server: {open: true,pro…...

FPGA领域顶级学术会议
FPGA领域顶级学术会议主要有FPGA,FCCM,FPL和FPT。 1 FPGA 会议全名是: ACM/SIGDA International Symposium on Field-Programmable Gate Arrays 网站是:https://dl.acm.org/conference/fpga FPGA常年在美国举办,每年2月,偏FPGA基础研究; 该会议的论文免费下载。这个比…...
罗技鼠标滚轮模式介绍 | 鼠标滚轮异响 - 解决方案
滚轮模式介绍 针对罗技的滚轮模式进行介绍: 普通滚轮:滚动时有明显段落感,无法快速滚动。 智能滚轮:滚动力量较弱时,与普通滚轮无异;滚动力量大时,鼠标会自动减小滚轮阻尼,从而使滚…...
Scrapy与分布式开发(2.2):正则表达式
使用Python的re模块进行正则表达式操作详细讲解 一、引言 正则表达式是一种强大的文本处理工具,它使用特定的模式来搜索、匹配和替换文本。Python的re模块(正则表达式模块)提供了正则表达式匹配操作的所有功能。下面我们将详细讲解如何使用re模块进行正则表达式的操作。 …...

今年“全国爱耳日”主题确定!立聪堂助听器组织社区义诊
2024年3月3日是第二十五个“全国爱耳日”。 今年的活动主题定为“科技助听,共享美好生活”,旨在倡导全社会关注听力健康,并鼓励听力受损人群利用科技手段实现早期康复。 为响应这一主题,知名助听器验配连锁机构立聪堂迅速行动起…...
后进先出(LIFO)详解
LIFO 是 Last In, First Out 的缩写,中文译为后进先出。这是一种数据结构的工作原则,类似于一摞盘子或一叠书本: 最后放进去的元素最先出来 -想象往筒状容器里放盘子: (1)你放进的最后一个盘子(…...
[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?
🧠 智能合约中的数据是如何在区块链中保持一致的? 为什么所有区块链节点都能得出相同结果?合约调用这么复杂,状态真能保持一致吗?本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里…...

国防科技大学计算机基础课程笔记02信息编码
1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制,因此这个了16进制的数据既可以翻译成为这个机器码,也可以翻译成为这个国标码,所以这个时候很容易会出现这个歧义的情况; 因此,我们的这个国…...
【Linux】C语言执行shell指令
在C语言中执行Shell指令 在C语言中,有几种方法可以执行Shell指令: 1. 使用system()函数 这是最简单的方法,包含在stdlib.h头文件中: #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...
Leetcode 3577. Count the Number of Computer Unlocking Permutations
Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接:3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯,要想要能够将所有的电脑解锁&#x…...
【磁盘】每天掌握一个Linux命令 - iostat
目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat(I/O Statistics)是Linux系统下用于监视系统输入输出设备和CPU使…...

Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具
文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...
oracle与MySQL数据库之间数据同步的技术要点
Oracle与MySQL数据库之间的数据同步是一个涉及多个技术要点的复杂任务。由于Oracle和MySQL的架构差异,它们的数据同步要求既要保持数据的准确性和一致性,又要处理好性能问题。以下是一些主要的技术要点: 数据结构差异 数据类型差异ÿ…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)
宇树机器人多姿态起立控制强化学习框架论文解析 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一) 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...
土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等
🔍 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术,可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势,还能有效评价重大生态工程…...