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

系列文章目录

Qt开发笔记(一)Qt的基础知识及环境编译(泰山派)

Qt学习笔记(二)Qt 信号与槽

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


文章目录

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

前言

之前我们在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地址和端口,一般一个服务器端程序只监听某个端口的网络连接。

cpp 复制代码
//   函数返回 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 ,这里注意下。

cpp 复制代码
// 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 实现代码

cpp 复制代码
#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)三部分构成


  知道以上这部分其实就差不多了,毕竟我们不是专业搞信息工程或者网络专业的,剩下的很琐碎的东西学起来就很费时费力了。

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

相关推荐
刘婉晴6 分钟前
【信息安全工程师备考笔记】第三章 密码学基本理论
笔记·安全·密码学
球求了24 分钟前
C++:继承机制详解
开发语言·c++·学习
时光追逐者1 小时前
MongoDB从入门到实战之MongoDB快速入门(附带学习路线图)
数据库·学习·mongodb
一弓虽1 小时前
SpringBoot 学习
java·spring boot·后端·学习
晓数2 小时前
【硬核干货】JetBrains AI Assistant 干货笔记
人工智能·笔记·jetbrains·ai assistant
我的golang之路果然有问题2 小时前
速成GO访问sql,个人笔记
经验分享·笔记·后端·sql·golang·go·database
genggeng不会代码2 小时前
用于协同显著目标检测的小组协作学习 2021 GCoNet(总结)
学习
lwewan2 小时前
26考研——存储系统(3)
c语言·笔记·考研
机器视觉知识推荐、就业指导3 小时前
开源QML控件:进度条滑动控件(含源码下载链接)
前端·qt·开源·qml
搞机小能手3 小时前
六个能够白嫖学习资料的网站
笔记·学习·分类