当前位置: 首页 > article >正文

Qt学习笔记(三)网络编程

系列文章目录

Qt开发笔记(一)Qt的基础知识及环境编译(泰山派)
Qt学习笔记(二)Qt 信号与槽
Qt学习笔记(三)网络编程


文章目录

  • 系列文章目录
  • 前言
  • 一、Qt网络模块
  • 二、TCP网络的编写
    • 2.1 服务端
      • 2.1.1 编写思路
      • 2.1.2 实现代码
    • 2.2 客户端
      • 2.2.1 编写思路
      • 2.2.2 实现代码
  • 三、 浅谈MQTT
    •   常见的MQTT报文有以下15种,其中可以分为三大类:**连接、发布和订阅**,对应MQTT的实现流程。而一个**MQTT数据包由固定头(Fixed header)、可变头(Variable header)、消息体(payload)三部分构成**。 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/70716e1d80094d73a1c8ad57606ef727.png)   知道以上这部分其实就差不多了,毕竟我们不是专业搞信息工程或者网络专业的,剩下的很琐碎的东西学起来就很费时费力了。


前言

  之前我们在Linux应用层的学习时提到了网络编程,本节是进行Qt下TCP / IP 客户端的编写。


一、Qt网络模块

  Qt 网络模块为我们提供了编写 TCP / IP 客户端和服务器的类。它提供了较低级别的类,例如代表低级网络概念的 QTcpSocket,QTcpServer 和QUdpSocket,以及诸如 QNetworkRequest,QNetworkReply 和QNetworkAccessManager 之类的高级类来执行使用通用协议的网络操作。它还提供了诸如QNetworkConfiguration,QNetworkConfigurationManager,QNetworkSession等类,实现承载管理。
在这里插入图片描述

二、TCP网络的编写

2.1 服务端

  所谓服务端就是指通过监听某个端口指令来判断是否有客户端连接,如果有则建立新的socket,之后客户端负责输入ip和port就可以完成连接了。在QT中,socket被视为输入输出流,数据的收发是通过read()和write()来实现。
在这里插入图片描述

2.1.1 编写思路

 1. 创建QTcpServer服务端对象
  这部分就是简单的对象实例化和槽函数的链接。
 2. 开始/停止监听
  在这个过程中最主要的是listen()函数,它可以设置监听的IP地址和端口,一般一个服务器端程序只监听某个端口的网络连接。

//   函数返回 true 时,表示监听成功
bool QTcpServer::listen(const QHostAddress &address = QHostAddress::Any, quint16 port = 0)

 3. 动态创建QTcpSocket对象
   这里主要利用nextPendingConnection()函数,它用于有新的客户端接入时,创建一个与客户端连接的QTcpSocket对象,然后发射 newConnection() 信号,通常我们在槽函数中使用它来进行新链接的创建。
 4. 接受消息并处理或者判断连接状态并连接\断开
   这里就很简单,可以创建类似用下文的receiveMessege_Slot()进行数据处理,或者是错误码检测之类的。

2.1.2 实现代码

   记得编译前,在.pro文件中修改 QT += network ,这里注意下。

// mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QtMqtt/qmqttclient.h>
#include <QObject>
#include <QTcpServer>
#include <QTcpSocket>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

signals:

private slots:
    void netConnect_Slot(void);
    void receiveMessege_Slot(void);
    void disConnect_Slot(void);

private:
    Ui::MainWindow *ui;
    QTcpServer *m_server;
    QList<QTcpSocket*> m_clients;
};
#endif // MAINWINDOW_H

// mainwindow.c
#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    m_server = new QTcpServer(this);
    connect(m_server, SIGNAL(newConnection()), this, SLOT(netConnect_Slot()));

    if (!m_server->listen(QHostAddress::Any, 1883)) {
        qDebug() << "Failed to Connect!";
    } else {
        qDebug() << "Succeed to Connect!";
    }
}

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

void MainWindow::netConnect_Slot()
{
    QTcpSocket *m_socket = m_server->nextPendingConnection();
    connect(m_socket, SIGNAL(readyRead()), this, SLOT(receiveMessege_Slot()));
    connect(m_socket, SIGNAL(disconnected()), this, SLOT(disConnect_Slot()));
    m_clients.append(m_socket);
    qDebug() << "New client connected";
}

void MainWindow::receiveMessege_Slot()
{
    QTcpSocket *m_socket = qobject_cast<QTcpSocket *>(sender());
    if (m_socket) {
        QByteArray requestData = m_socket->readAll();
        qDebug() << "Received MQTT message:" << requestData;
    }
}

void MainWindow::disConnect_Slot()
{
    QTcpSocket *m_socket = qobject_cast<QTcpSocket *>(sender());
    if (m_socket) {
        m_clients.removeOne(m_socket);
        m_socket->deleteLater();
        qDebug() << "Client disconnected";
    }
}

2.2 客户端

   这部分的内容十分重要,涉及到我们后期使用mqtt协议,通常情况下我们不主动搭建服务器,仅调用客户端就满足对“上云”的要求了。

2.2.1 编写思路

 1. QTcpSocket 实例化并与信号和槽连接
 2. 连接到服务器(指定的 IP 和端口)
  这里主要是利用tcpSocket->connectToHost("服务器地址", 端口号);进行连接,注意和上面的listen区分一下。
 3. 消息通过 TCP 套接字发送到服务器
  利用write和readAll即可完成数据的读写。

2.2.2 实现代码

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QTcpSocket>
#include <QTextEdit>
#include <QLineEdit>
#include <QPushButton>

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

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

private slots:
    void onConnectButtonClicked();
    void onSendButtonClicked();
    void onReadyRead();
    void onError(QAbstractSocket::SocketError socketError);

private:
    Ui::MainWindow *ui;
    QTcpSocket *tcpSocket;
    QTextEdit *logTextEdit;
    QLineEdit *messageLineEdit;
    QPushButton *connectButton;
    QPushButton *sendButton;
};

#endif // MAINWINDOW_H

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
#include <QMessageBox>

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow),
    tcpSocket(new QTcpSocket(this))
{
    ui->setupUi(this);

    // 获取控件
    logTextEdit = findChild<QTextEdit*>("logTextEdit");
    messageLineEdit = findChild<QLineEdit*>("messageLineEdit");
    connectButton = findChild<QPushButton*>("connectButton");
    sendButton = findChild<QPushButton*>("sendButton");

    // 连接信号和槽
    connect(connectButton, &QPushButton::clicked, this, &MainWindow::onConnectButtonClicked);
    connect(sendButton, &QPushButton::clicked, this, &MainWindow::onSendButtonClicked);
    connect(tcpSocket, &QTcpSocket::readyRead, this, &MainWindow::onReadyRead);
    connect(tcpSocket, &QTcpSocket::errorOccurred, this, &MainWindow::onError);
}

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

void MainWindow::onConnectButtonClicked()
{
    // 连接到服务器
    tcpSocket->connectToHost("127.0.0.1", 1234);

    if (!tcpSocket->waitForConnected(3000)) {
        QMessageBox::critical(this, "错误", "连接服务器失败: " + tcpSocket->errorString());
    } else {
        logTextEdit->append("已连接到服务器!");
    }
}

void MainWindow::onSendButtonClicked()
{
    // 发送消息到服务器
    QString message = messageLineEdit->text();
    if (message.isEmpty()) {
        return;
    }

    tcpSocket->write(message.toUtf8());

    if (!tcpSocket->waitForBytesWritten(3000)) {
        QMessageBox::critical(this, "错误", "发送失败: " + tcpSocket->errorString());
    } else {
        logTextEdit->append("发送消息: " + message);
    }
}

void MainWindow::onReadyRead()
{
    // 读取服务器的响应
    QByteArray response = tcpSocket->readAll();
    logTextEdit->append("收到服务器响应: " + response);
}

void MainWindow::onError(QAbstractSocket::SocketError socketError)
{
    // 处理连接错误
    Q_UNUSED(socketError);
    QMessageBox::critical(this, "错误", "发生错误: " + tcpSocket->errorString());
}

三、 浅谈MQTT

  老粉丝可能知道,笔者钟情于MQTT作为远距离形象传递的手段(主要是目前这种资料比较多,笔者太菜了学不会别的),本小节就简单讲一下我的一些理解。MQTT是一种基于TCP/IP协议的"轻量级"通讯协议,该协议主要是发布/订阅(publish/subscribe)模式的。在通讯过程中, MQTT协议中有三种身份: 发布者(Publish)、代理(Broker)(服务器)、订阅者(Subscribe)。 其中,消息的发布者和订阅者都是客户端,消息代理是服务器,消息发布者可以同时是订阅者。
在这里插入图片描述
  MQTT传输的消息分为由主题(Topic)和负载(payload)两部分组成,也就我们常见的“Topic+data”的结构。除此之外,消息服务质量(QoS)管理是MQTT保证可靠传输的一大依仗,它有三种消息发布服务质量:

  1. QoSO:“至多一次”,消息发布完全依赖底层TCP/IP网络,会发生消息丢失或重复。
  2. QoS1:“至少—次”,确保消息到达,但消息重复可能会发生。
  3. QoS2:“只有一次”,确保消息到达一次。

  常见的MQTT报文有以下15种,其中可以分为三大类:连接、发布和订阅,对应MQTT的实现流程。而一个MQTT数据包由固定头(Fixed header)、可变头(Variable header)、消息体(payload)三部分构成
在这里插入图片描述
  知道以上这部分其实就差不多了,毕竟我们不是专业搞信息工程或者网络专业的,剩下的很琐碎的东西学起来就很费时费力了。

免责声明:本文参考了网上公开的部分资料,仅供学习参考使用,若有侵权或勘误请联系笔者


http://www.kler.cn/a/386358.html

相关文章:

  • 【项目组件】第三方库——websocketpp
  • 【JavaEE初阶 — 多线程】生产消费模型 阻塞队列
  • 当你想要conda安装遇到UnavailableInvalidChannel: HTTP 404 NOT FOUND for channel的问题
  • C++STL容器——map和set
  • (六)Spark大数据开发实战:豆瓣电影数据处理与分析(scala版)
  • Spring Boot 2.x 和 Druid 多数据源整合 dm
  • DevExpress中文教程 - 如何使用AI模型检查HTML编辑中的语法?
  • RabbitMQ客户端应用开发实战
  • 一文读懂系列:结合抓包分析,详解SSH协议通信原理
  • Node.js-API 限流与日志优化
  • 【Threejs】相机控制器动画
  • php伪协议介绍
  • 树莓派开发相关知识十 -小试服务器
  • 智能电网能源优化管理系统(Smart Grid Energy Optimization Management System, SGEOMS)
  • jupyter notebook启动和单元格cell
  • java ssm 网上蛋糕店 在线蛋糕甜品管理 网上蛋糕管理 源码 jsp
  • MySQL的约束和三大范式
  • C# NUnit 框架:高效使用指南
  • Spring Boot 集成JWT实现Token验证详解
  • clickhouse 安装配置
  • react动态路由
  • 【重装系统后重新配置2】pycharm 终端无法激活conda环境
  • ORACLE的完全检查点和增量检查点
  • FPGA实现串口升级及MultiBoot(六)ICAPE2原语实例讲解
  • 计算机网络 TCP/IP体系 数据链路层
  • qt QLocale详解