QT 中基于 TCP 的网络通信 (备查)

基础

基于 TCP 的套接字通信需要用到两个类:
1)QTcpServer:服务器类,用于监听客户端连接以及和客户端建立连接。
2)QTcpSocket:通信的套接字类,客户端、服务器端都需要使用。

这两个套接字通信类都属于网络模块 network

TCP协议是 双向连接,双向断开,即三次握手,四次挥手

QTcpServer 类 API

QTcpServer 类用于监听客户端连接以及和客户端建立连接

公共成员函数
//构造函数
QTcpServer::QTcpServer(QObject *parent = Q_NULLPTR);

//给监听的套接字设置监听
bool QTcpServer::listen(const QHostAddress &address = QHostAddress::Any, quint16 port = 0);
参数:
	address:通过类QHostAddress可以封装IPv4、IPv6格式的IP地址,QHostAddress::Any表示自动绑定
	port:如果指定为0表示随机绑定一个可用端口。
	返回值:绑定成功返回true,失败返回false

// 判断当前对象是否在监听, 是返回true,没有监听返回false
bool QTcpServer::isListening() const;

// 如果当前对象正在监听返回监听的服务器地址信息, 否则返回 QHostAddress::Null
QHostAddress QTcpServer::serverAddress() const;

// 如果服务器正在侦听连接,则返回服务器的端口; 否则返回0
quint16 QTcpServer::serverPort() const

//和客户端建立连接之后用于通信的QTcpSocket套接字对象,它是QTcpServer的一个子对象(会自动析构)
QTcpSocket *QTcpServer::nextPendingConnection();

//阻塞等待客户端发起的连接请求,不推荐在单线程程序中使用,建议使用非阻塞方式处理新连接,即使用信号 newConnection()
bool QTcpServer::waitForNewConnection(int msec = 0, bool *timedOut = Q_NULLPTR);
参数:
	msec:指定阻塞的最大时长,单位为毫秒(ms)
	timeout:传出参数,如果操作超时timeout为true,没有超时timeout为false
信号
//当接受新连接导致错误时,将发射如下信号。socketError参数描述了发生的错误相关的信息。
[signal] void QTcpServer::acceptError(QAbstractSocket::SocketError socketError);

//每次有新连接可用时都会发出 newConnection() 信号。
[signal] void QTcpServer::newConnection();

QTcpSocket 类 API

Qt中发送和接收数据也属于IO操作(网络IO),这个类的继承关系:

在这里插入图片描述

公共成员函数
//构造函数
QTcpSocket::QTcpSocket(QObject *parent = Q_NULLPTR);

//连接服务器,需要指定服务器端绑定的IP和端口信息。
[virtual] void QAbstractSocket::connectToHost(const QString &hostName, quint16 port, OpenMode openMode = ReadWrite, NetworkLayerProtocol protocol = AnyIPProtocol);

[virtual] void QAbstractSocket::connectToHost(const QHostAddress &address, quint16 port, OpenMode openMode = ReadWrite);

//接收数据
// 指定可接收的最大字节数 maxSize 的数据到指针 data 指向的内存中
qint64 QIODevice::read(char *data, qint64 maxSize);

// 指定可接收的最大字节数 maxSize,返回接收的字符串
QByteArray QIODevice::read(qint64 maxSize);

// 将当前可用操作数据全部读出,通过返回值返回读出的字符串
QByteArray QIODevice::readAll();

//发送数据
// 发送指针 data 指向的内存中的 maxSize 个字节的数据
qint64 QIODevice::write(const char *data, qint64 maxSize);

// 发送指针 data 指向的内存中的数据,字符串以 \0 作为结束标记
qint64 QIODevice::write(const char *data);

// 发送参数指定的字符串
qint64 QIODevice::write(const QByteArray &byteArray);
信号
//通信的过程中,如果该类对象发射出readyRead()信号,说明对端发送的数据达到了,之后就可以调用 read 函数接收数据了。
[signal] void QIODevice::readyRead();

//调用connectToHost()函数并成功建立连接之后发出connected()信号。
[signal] void QAbstractSocket::connected();

//在套接字断开连接时发出disconnected()信号。
[signal] void QAbstractSocket::disconnected();
其他

1)在Qt中不管调用读操作函数接收数据,还是调用写函数发送数据,操作的对象都是本地的由Qt框架维护的一块内存。
2)因此,调用了发送函数数据不一定会马上被发送到网络中,调用了接收函数也不是直接从网络中接收数据。

QT 套接字通信流程

服务器端

1)创建套接字服务器 QTcpServer 对象

2)通过 QTcpServer 对象设置监听,即:QTcpServer::listen()

3)基于 QTcpServer::newConnection() 信号检测是否有新的客户端连接

4)如果有新的客户端连接调用 QTcpSocket *QTcpServer::nextPendingConnection() 得到通信的套接字对象

5)使用通信的套接字对象 QTcpSocket 和客户端进行通信

客户端

1)创建通信的套接字类 QTcpSocket 对象

2)使用服务器端绑定的IP和端口连接服务器 QAbstractSocket::connectToHost()

3)使用 QTcpSocket 对象和服务器进行通信

服务器/客户端 TCP通信案列

服务器端
//头文件
class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private slots:
    void on_startServer_clicked();

    void on_sendMsg_clicked();

private:
    Ui::MainWindow *ui;
    QTcpServer* m_server;
    QTcpSocket* m_tcp;
};
//源文件
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    setWindowTitle("TCP - 服务器");
    // 创建 QTcpServer 对象
    m_server = new QTcpServer(this);
    // 检测是否有新的客户端连接
    connect(m_server, &QTcpServer::newConnection, this, [=]()
    {
        m_tcp = m_server->nextPendingConnection();
        ui->record->append("成功和客户端建立了新的连接...");
        m_status->setPixmap(QPixmap(":/connect.png").scaled(20, 20));
        // 检测是否有客户端数据
        connect(m_tcp, &QTcpSocket::readyRead, this, [=]()
        {
            // 接收数据
            QString recvMsg = m_tcp->readAll();
            ui->record->append("客户端Say: " + recvMsg);
        });
        // 客户端断开了连接
        connect(m_tcp, &QTcpSocket::disconnected, this, [=]()
        {
            ui->record->append("客户端已经断开了连接...");
            m_tcp->deleteLater();
            m_status->setPixmap(QPixmap(":/disconnect.png").scaled(20, 20));
        });
    });
}

MainWindow::~MainWindow()
{
    delete ui;
}

// 启动服务器端的服务按钮
void MainWindow::on_startServer_clicked()
{
    unsigned short port = ui->port->text().toInt();
    // 设置服务器监听
    m_server->listen(QHostAddress::Any, port);
    ui->startServer->setEnabled(false);
}

// 点击发送数据按钮
void MainWindow::on_sendMsg_clicked()
{
    QString sendMsg = ui->msg->toPlainText();
    m_tcp->write(sendMsg.toUtf8());
    ui->record->append("服务器Say: " + sendMsg);
    ui->msg->clear();
}
客户端
//头文件
class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

private slots:
    void on_connectServer_clicked();

    void on_sendMsg_clicked();

    void on_disconnect_clicked();

private:
    Ui::MainWindow *ui;
    QTcpSocket* m_tcp;
};
//源文件
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    setWindowTitle("TCP - 客户端");

    // 创建通信的套接字对象
    m_tcp = new QTcpSocket(this);
    // 检测服务器是否回复了数据
    connect(m_tcp, &QTcpSocket::readyRead, [=]()
    {
        // 接收服务器发送的数据
        QByteArray recvMsg = m_tcp->readAll();
        ui->record->append("服务器Say: " + recvMsg);
    });
        
    // 检测是否和服务器是否连接成功了
    connect(m_tcp, &QTcpSocket::connected, this, [=]()
    {
        ui->record->append("恭喜, 连接服务器成功!!!");
        m_status->setPixmap(QPixmap(":/connect.png").scaled(20, 20));
    });
        
    // 检测服务器是否和客户端断开了连接
    connect(m_tcp, &QTcpSocket::disconnected, this, [=]()
    {
        ui->record->append("服务器已经断开了连接, ...");
        ui->connectServer->setEnabled(true);
        ui->disconnect->setEnabled(false);
    });
}

MainWindow::~MainWindow()
{
    delete ui;
}

// 连接服务器按钮按下之后的处理动作
void MainWindow::on_connectServer_clicked()
{
    QString ip = ui->ip->text();
    unsigned short port = ui->port->text().toInt();
    // 连接服务器
    m_tcp->connectToHost(QHostAddress(ip), port);
    ui->connectServer->setEnabled(false);
    ui->disconnect->setEnabled(true);
}

// 发送数据按钮按下之后的处理动作
void MainWindow::on_sendMsg_clicked()
{
    QString sendMsg = ui->msg->toPlainText();
    m_tcp->write(sendMsg.toUtf8());
    ui->record->append("客户端Say: " + sendMsg);
    ui->msg->clear();
}

// 断开连接按钮被按下之后的处理动作
void MainWindow::on_disconnect_clicked()
{
    m_tcp->close();
    ui->connectServer->setEnabled(true);
    ui->disconnect->setEnabled(false);
}

作者: 苏丙榅
链接: https://subingwen.cn/qt/socket-tcp/?highlight=%E5%A5%97%E6%8E%A5%E5%AD%97
来源: 爱编程的大丙
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

详细教程可转

爱编程的大丙

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于PicGo实现Typora图片自动上传GitHub

文章目录 一. 引言二. 原理三. 配置3.1 GitHub 设置3.2 下载配置 PicGo3.3 配置 Typora3.4 使用 一. 引言 Typora是一款非常好的笔记软件,但是有一个比较不好的地方:默认图片是存放在本地缓存中。这就会导致文件夹一旦被误删或电脑系统重装而忘记备份文件…

6.1810: Operating System Engineering 2023 <Lab4 traps: Traps>

一、本节任务 二、要点(Traps and system calls) 有三种事件会使 CPU 暂停当前的指令执行,并强制将控制转移到处理该事件的特殊代码中: 系统调用(ecall);异常(如非法指令&#xff…

VSCode之C++ CUDA入门:reduce的N+1重境界

背景 Reduce是几乎所有多线程技术的基础和关键,同样也是诸如深度学习等领域的核心,简单如卷积运算,复杂如梯度聚合、分布式训练等,了解CUDA实现reduce,以及优化reduce是理解CUDA软硬件连接点的很好切入点。 硬件环境&…

JVM 分析GC日志

GC日志参数 -verbose:gc 输出gc日志信息,默认输出到标准输出 -XX:PrintGC 输出GC日志。类似:-verbose:gc -XX:PrintGCDetails 在发生垃圾回收时打印内存回收详细的日志,并在进程退出时输出当前内存各区域分配情况 -XX:PrintGCTimeStam…

【TiDB理论知识10】TiDB6.0新特性

新特性 Placement Rules in SQL 小表缓存 内存悲观锁 Top SQL TiDB Enterprise Manager 一 Placement Rules in SQL Placement Rules in SQL 之前会遇到的问题 比如 北京的业务需要访问 T2 和 T3表 ,但是T3表的数据在纽约 纽约的业务需要问访T4 T5 T6表…

2023 金砖国家职业技能大赛网络安全省赛理论题样题(金砖国家未来技能挑战赛)

2023 金砖国家职业技能大赛网络安全省赛理论题样题(金砖国家未来技能挑战赛) 一、参加比赛的形式 团队参与,每队2名选手(设队长1名)。 二、项目项目阶段简介 项目由四个阶段组成,将按顺序完成。向参与者…

Notes数据直接在Excel中统计

大家好,才是真的好。 我希望你看过前面两篇内容《Domino REST API安装和运行》和《Domino REST API安装和运行》,因为今天我们正是使用REST API方式在Excel中查询和统计Notes数据。 不过首先你得知道一个OData协议,全名Open Data Protocol(…

Leetcode1038. 从二叉搜索树到更大和树

Every day a Leetcode 题目来源:1038. 从二叉搜索树到更大和树 解法1:中序遍历 观察示例 1,我们发现了规律: 二叉搜索树的中序遍历是一个单调递增的有序序列。 本题中要求我们将每个节点的值修改为原来的节点值加上所有大于它…

CSS——选择器、PxCook软件、盒子模型

1、选择器 1.1 结构伪类选择器 作用&#xff1a;根据元素的结构关系查找元素。 1.1.1 :nth-child&#xff08;公式&#xff09; 作用&#xff1a;根据元素的结构关系查找多个元素 <!DOCTYPE html> <html lang"en"> <head><meta charset"…

编程过程中出现bug如何应对?

编程过程中出现bug如何应对&#xff1f; 1.找错误原因 如果完全不知道出错的原因&#xff0c;或者说存在着很多错误的有原因&#xff0c;----》控制变量法 例如&#xff0c;昨天我在使用torchrun 多卡并行一个程序的时候&#xff0c;出现了大量的bug, 于是我将报错信息放在网…

Java动态代理实现与原理详细分析

Java动态代理实现与原理详细分析 关于Java中的动态代理&#xff0c;我们首先需要了解的是一种常用的设计模式–代理模式&#xff0c;而对于代理&#xff0c;根据创建代理类的 时间点&#xff0c;又可以分为静态代理和动态代理。 1、代理模式 代理模式是常用的java设计模式&…

kafka学习笔记--基础知识概述

本文内容来自尚硅谷B站公开教学视频&#xff0c;仅做个人总结、学习、复习使用&#xff0c;任何对此文章的引用&#xff0c;应当说明源出处为尚硅谷&#xff0c;不得用于商业用途。 如有侵权、联系速删 视频教程链接&#xff1a;【尚硅谷】Kafka3.x教程&#xff08;从入门到调优…

Kafka 的消息格式:了解消息结构与序列化

Kafka 作为一款高性能的消息中间件系统&#xff0c;其消息格式对于消息的生产、传输和消费起着至关重要的作用。本篇博客将深入讨论 Kafka 的消息格式&#xff0c;包括消息的结构、序列化与反序列化&#xff0c;以及一些常用的消息格式选项。通过更丰富的示例代码和深入的解析&…

【Quasar】暗黑主题随系统切换部分组件无法随系统切换

问题描述 Quasar部分组件无法随系统切换主题 。 假如系统、Quasar主题为白天模式。Quasar设置主题随系统切换&#xff0c;当系统切换暗黑模式时&#xff0c;Quasar导航栏无法正常切换为暗黑模式&#xff0c;此时背景还是白天模式&#xff0c;如图 正常切换参考图 正常暗黑…

【musl-pwn】msul-pwn 刷题记录 -- musl libc 1.2.2

前言 本文不分析 musl libc 相关源码&#xff0c;仅仅为刷题记录&#xff0c;请读者自行学习相关知识&#xff08;看看源码就行了&#xff0c;代码量也不大&#xff09; starCTF2022_babynote 保护&#xff1a;保护全开 程序与漏洞分析&#xff1a; 程序实现了一个菜单堆&…

第3章:知识表示:概述、符号知识表示、向量知识表示

&#x1f497;&#x1f497;&#x1f497;欢迎来到我的博客&#xff0c;你将找到有关如何使用技术解决问题的文章&#xff0c;也会找到某个技术的学习路线。无论你是何种职业&#xff0c;我都希望我的博客对你有所帮助。最后不要忘记订阅我的博客以获取最新文章&#xff0c;也欢…

Python 从入门到精通 学习笔记 Day01

Python 从入门到精通 第一天 今日目标 计算机组成原理、编程语言、Python环境安装 第一个Python程序、PyCharm的安装与使用 Python的基础语法、Python的基本数据类型 一、计算机组成原理 计算机的组成 计算机硬件通常由以下几个部分组成: 1.中央处理器(CPU):负责执行计算机…

HarmonyOS4.0从零开始的开发教程03初识ArkTS开发语言(中)

HarmonyOS&#xff08;二&#xff09;初识ArkTS开发语言&#xff08;中&#xff09;之TypeScript入门 浅析ArkTS的起源和演进 1 引言 Mozilla创造了JS&#xff0c;Microsoft创建了TS&#xff0c;Huawei进一步推出了ArkTS。 从最初的基础的逻辑交互能力&#xff0c;到具备类…

Docker-多容器应用

一、概述 到目前为止&#xff0c;你一直在使用单个容器应用。但是&#xff0c;现在您将 MySQL 添加到 应用程序堆栈。经常会出现以下问题 - “MySQL将在哪里运行&#xff1f;将其安装在同一个 容器还是单独运行&#xff1f;一般来说&#xff0c;每个容器都应该做一件事&#x…

题目分析,高度理解一维二维数组的申请和[]是什么运算符

第0题: 动态申请二维数组并输出非负数和 和负数出现次数 思路:输入数组大小,然后申请内存并不对其初始化,提高速度,传入数据到申请的数组中,判断如果数组中有元素小于0对其进行计数,否则加上非0数最后输出答案,释放内存 第一题: 解答: 运行结果: 思路分析: 创建长度为20的…