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)三部分构成


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

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

相关推荐
wusong9991 小时前
mongoDB回顾笔记(一)
数据库·笔记·mongodb
猫爪笔记1 小时前
前端:HTML (学习笔记)【1】
前端·笔记·学习·html
Resurgence031 小时前
【计组笔记】习题
笔记
pq113_61 小时前
ftdi_sio应用学习笔记 3 - GPIO
笔记·学习·ftdi_sio
澄澈i2 小时前
设计模式学习[8]---原型模式
学习·设计模式·原型模式
爱米的前端小笔记2 小时前
前端八股自学笔记分享—页面布局(二)
前端·笔记·学习·面试·求职招聘
alikami3 小时前
【前端】前端学习
学习
一只小菜鸡..3 小时前
241118学习日志——[CSDIY] [ByteDance] 后端训练营 [06]
学习
「QT(C++)开发工程师」4 小时前
【qt版本概述】
开发语言·qt
Hacker_Oldv4 小时前
网络安全的学习路线
学习·安全·web安全