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

Qt5网络编程详细讲解

个人博客:blogs.wurp.top

一、 Qt 网络模块概述

  1. 核心特性

    • 跨平台:在 Windows、Linux、macOS 等系统上提供一致的 API。
    • 事件驱动:与 Qt 的信号槽机制无缝集成,采用异步非阻塞 I/O,不会阻塞主线程(GUI线程)。
    • 协议支持:提供了对 TCP、UDP、HTTP、HTTPS、FTP 等协议的高级封装。
    • 抽象层次:既提供了低级的套接字类(如 QTcpSocket),也提供了高级的网络请求类(如QNetworkAccessManager)。
  2. 在项目中使用网络模块
    在你的项目文件(.pro)中,需要添加一行来引入网络模块:

    QT += network
    

    在源代码中,包含必要的头文件,例如:

    #include <QTcpSocket>
    #include <QTcpServer>
    #include <QUdpSocket>
    #include <QNetworkAccessManager>
    #include <QNetworkReply>
    

二、 核心类详解

我们将从底层到高层依次讲解最重要的几个类。

1. TCP 编程

TCP 是一种面向连接、可靠的、基于字节流的传输层通信协议。

  • QTcpServer:用于监听和接受传入的 TCP 连接。

    • 核心方法
      • listen(const QHostAddress &address = QHostAddress::Any, quint16 port = 0):开始在指定地址和端口监听。成功返回 true
      • bool isListening():判断是否正在监听。
      • void close():停止服务器监听。
    • 重要信号
      • void newConnection():当有新的客户端连接时发射此信号。
  • QTcpSocket:用于建立 TCP 连接,进行数据的发送和接收。它继承自 QAbstractSocket,再继承自 QIODevice,因此可以像操作文件一样读写数据。

    • 核心方法
      • connectToHost(const QString &hostName, quint16 port, OpenMode mode = ReadWrite):异步连接到主机。
      • qint64 write(const QByteArray &data):发送数据。
      • QByteArray readAll() / qint64 read(char *data, qint64 maxSize):读取所有/指定大小的数据。
      • qint64 bytesAvailable():返回可读的字节数。
      • void disconnectFromHost():断开连接。
    • 重要信号
      • void connected():成功连接到服务器后发射。
      • void disconnected():连接断开时发射。
      • void readyRead():当有新的数据可供读取时发射。这是处理接收数据最常用的信号
      • void error(QAbstractSocket::SocketError socketError):发生错误时发射。
      • void stateChanged(QAbstractSocket::SocketState socketState):状态改变时发射。

TCP 服务器编程流程

  1. 创建 QTcpServer 对象。
  2. 调用 listen() 开始监听。
  3. 连接 newConnection() 信号到一个槽函数。
  4. 在槽函数中,调用 nextPendingConnection() 获取连接的 QTcpSocket
  5. 保存或使用这个 QTcpSocket 对象,连接其 readyRead(), disconnected() 等信号到对应的槽函数进行数据读写和连接管理。
  6. readyRead() 的槽函数中读取数据,使用 write() 发送数据。

TCP 客户端编程流程

  1. 创建 QTcpSocket 对象。
  2. (可选)连接 connected(), readyRead(), disconnected(), error() 等信号到槽函数。
  3. 调用 connectToHost() 连接服务器。
  4. 连接成功后,使用 write() 发送数据。
  5. readyRead() 的槽函数中处理服务器返回的数据。

示例:一个简单的 TCP 客户端连接和发送

// 在类头文件中声明
QTcpSocket *tcpSocket;// 在构造函数中初始化
tcpSocket = new QTcpSocket(this);
connect(tcpSocket, &QTcpSocket::connected, this, &MyClass::onConnected);
connect(tcpSocket, &QTcpSocket::readyRead, this, &MyClass::onReadyRead);
connect(tcpSocket, &QTcpSocket::errorOccurred, this, &MyClass::onError);// 连接服务器
tcpSocket->connectToHost("127.0.0.1", 8888);// 槽函数
void MyClass::onConnected() {qDebug() << "Connected to server!";tcpSocket->write("Hello, Server!");
}void MyClass::onReadyRead() {QByteArray data = tcpSocket->readAll();qDebug() << "Received from server:" << data;
}void MyClass::onError(QAbstractSocket::SocketError error) {qDebug() << "Error occurred:" << tcpSocket->errorString();
}

2. UDP 编程

UDP 是一种无连接、不可靠的报文传输协议。

  • QUdpSocket:用于发送和接收 UDP 数据报。
    • 核心方法
      • bool bind(quint16 port, ...):绑定到一个本地端口来接收数据报。
      • qint64 writeDatagram(const QByteArray &datagram, const QHostAddress &host, quint16 port):向指定地址和端口发送数据报。
      • qint64 readDatagram(char *data, qint64 maxSize, QHostAddress *address = nullptr, quint16 *port = nullptr):读取一个数据报及其发送者的信息。
      • bool hasPendingDatagrams():判断是否有待处理的数据报。
      • qint64 pendingDatagramSize():返回下一个待处理数据报的大小。

UDP 编程流程

  1. 创建 QUdpSocket 对象。
  2. 调用 bind() 绑定一个端口(如果只想发送,可以不绑定;如果想接收,必须绑定)。
  3. 连接 readyRead() 信号到槽函数,处理接收到的数据报。
  4. 使用 writeDatagram() 发送数据。
  5. readyRead() 的槽函数中,使用 readDatagram() 或循环 hasPendingDatagrams() 来读取所有数据报。

示例:接收 UDP 数据报

QUdpSocket *udpSocket = new QUdpSocket(this);
// 绑定到 7755 端口接收所有网卡的数据
if (udpSocket->bind(7755)) {connect(udpSocket, &QUdpSocket::readyRead, this, &MyClass::readPendingDatagrams);
}void MyClass::readPendingDatagrams() {while (udpSocket->hasPendingDatagrams()) {QByteArray datagram;datagram.resize(udpSocket->pendingDatagramSize());QHostAddress sender;quint16 senderPort;udpSocket->readDatagram(datagram.data(), datagram.size(), &sender, &senderPort);qDebug() << "Received from" << sender.toString() << ":" << senderPort<< "Data:" << datagram;}
}// 发送 UDP 数据报
QByteArray data = "Hello UDP!";
udpSocket->writeDatagram(data, QHostAddress("192.168.1.100"), 7755);

3. HTTP 编程 (高级 API)

Qt 使用 QNetworkAccessManager (NAM) 来处理 HTTP、HTTPS 等协议请求,它是异步且高度封装的。

  • QNetworkAccessManager:协调网络操作的中心类。它本身不处理数据,而是创建 QNetworkReply 对象来代表一个网络请求。

    • 核心方法
      • QNetworkReply *get(const QNetworkRequest &request):发起 GET 请求。
      • QNetworkReply *post(const QNetworkRequest &request, const QByteArray &data):发起 POST 请求,可提交数据。
      • QNetworkReply *put(const QNetworkRequest &request, const QByteArray &data):发起 PUT 请求。
      • QNetworkReply *deleteResource(const QNetworkRequest &request):发起 DELETE 请求。
    • 重要信号
      • void finished(QNetworkReply *reply):当一个网络请求完成时发射(无论成功失败)。
  • QNetworkRequest:封装一个网络请求的信息,如 URL、头信息(Header)、属性等。

    • 常用设置:setUrl(const QUrl &url), setHeader(QNetworkRequest::KnownHeaders header, const QVariant &value)
  • QNetworkReply:代表一个正在进行的或已完成的网络请求。它继承自 QIODevice,所以数据也是异步读取的。

    • 核心方法/属性
      • QByteArray readAll() / read():读取服务器返回的数据。
      • QUrl url():返回请求的 URL。
      • QVariant attribute(QNetworkRequest::Attribute code):获取请求的属性(如状态码)。
      • QNetworkReply::NetworkError error():返回错误类型。
      • QString errorString():返回错误描述。
    • 重要信号
      • void readyRead():有数据可读时发射(适合流式数据)。
      • void downloadProgress(qint64 bytesReceived, qint64 bytesTotal):下载进度变化时发射。
      • void uploadProgress(qint64 bytesSent, qint64 bytesTotal):上传进度变化时发射。
      • void finished():请求完成时发射(与 QNetworkAccessManager::finished 同步)。

HTTP 编程流程 (GET 示例)

  1. 创建一个 QNetworkAccessManager 对象(通常一个应用一个实例即可)。
  2. 构建一个 QNetworkRequest,设置其 URL 和头信息。
  3. 调用 QNetworkAccessManager::get() 等方法,发起请求,并获取返回的 QNetworkReply 对象指针。
  4. QNetworkReplyfinished(), readyRead(), errorOccurred() 等信号连接到槽函数。
  5. finished() 的槽函数中,检查回复是否有错误,然后读取数据。注意: QNetworkReply 对象需要在槽函数中自行删除,调用 deleteLater() 是安全的做法。

示例:发起一个简单的 GET 请求

// 在类头文件中声明
QNetworkAccessManager *manager;// 在构造函数中初始化
manager = new QNetworkAccessManager(this);
connect(manager, &QNetworkAccessManager::finished, this, &MyClass::onReplyFinished);// 发起请求
QUrl url("https://XXX.com/data");
QNetworkRequest request(url);
// 可以设置头信息,例如 User-Agent
request.setHeader(QNetworkRequest::UserAgentHeader, "MyQtApp/1.0");
QNetworkReply *reply = manager->get(request);// 可选的:连接reply本身的信号来跟踪进度
connect(reply, &QNetworkReply::downloadProgress, this, &MyClass::onDownloadProgress);// 请求完成的槽函数
void MyClass::onReplyFinished(QNetworkReply *reply) {if (reply->error() == QNetworkReply::NoError) {QByteArray response_data = reply->readAll();// 处理 response_data,可能是 JSON、XML 或 HTMLqDebug() << "Response:" << response_data;} else {qDebug() << "Error:" << reply->errorString();}// 清理 reply 对象reply->deleteLater();
}

POST 请求示例(提交 JSON 数据)

QNetworkRequest request(QUrl("https://XXX.com/submit"));
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");QJsonObject jsonObject;
jsonObject["name"] = "Qt";
jsonObject["version"] = 5;
QJsonDocument doc(jsonObject);
QByteArray jsonData = doc.toJson();QNetworkReply *reply = manager->post(request, jsonData);

三、 高级主题与最佳实践

  1. 线程与网络

    • QNetworkAccessManager 和套接字对象通常生活在创建它们的线程中,并且该线程必须运行事件循环。
    • 对于耗时操作(如处理大量接收到的数据),最好将数据处理工作移到工作线程(QThread)中,避免阻塞主线程(GUI线程)。
    • 可以在工作线程中创建自己的 QNetworkAccessManager,但需要确保该线程运行着事件循环。
  2. 数据编码

    • 网络传输的是字节流。QString 需要转换为 QByteArray 才能发送。
    • 常用转换:QString::toUtf8(), QString::fromUtf8()。确保收发双方使用相同的编码。
  3. 处理粘包(TCP):

    • TCP 是流协议,没有消息边界。readyRead() 信号只表示有数据到达,不保证是一条完整的“消息”。
    • 解决方案:设计应用层协议。常用方法:
      • 定长协议:每条消息固定长度。
      • 分隔符协议:用特殊字符(如 \n)分隔消息。使用 QIODevice::canReadLine()readLine()
      • 定长头+变长体协议:消息由一个固定大小的头部(包含消息体长度)和变长的body组成。先读头部,解析出长度,再读取指定长度的body。
  4. SSL/TLS 支持

    • Qt 提供了 QSslSocket,它是 QTcpSocket 的 SSL 加密版本,用法几乎完全相同。
    • 需要在项目文件中添加 QT += network(已包含)。
    • 使用 QSslSocket::connectToHostEncrypted() 来建立安全连接。
    • 可以处理证书验证相关的信号,如 sslErrors()
  5. 代理支持

    • Qt 网络模块透明地支持系统代理设置。
    • 也可以使用 QNetworkProxy 类来为单个套接字或全局(QNetworkProxy::setApplicationProxy)设置自定义代理。

四、总结

需求/协议推荐使用的Qt类特点
可靠流传输 (TCP)QTcpServer, QTcpSocket面向连接,可靠,需要处理粘包
快速报文传输 (UDP)QUdpSocket无连接,不可靠,效率高,需处理丢包和乱序
Web 请求 (HTTP/HTTPS)QNetworkAccessManager, QNetworkRequest, QNetworkReply高级 API,异步,支持方法、头、Cookie、代理等
安全传输 (SSL/TLS)QSslSocket (替代 QTcpSocket)在 TCP 基础上提供加密和认证

Qt 的网络编程核心思想是 异步事件驱动。熟练掌握信号槽机制,并理解 readyRead(), finished() 等关键信号的触发时机,是编写高效、健壮网络应用的关键。建议从简单的 TCP 回显服务器/客户端或 HTTP GET 请求例子开始实践,逐步深入到更复杂的应用。

http://www.dtcms.com/a/343075.html

相关文章:

  • 僵尸进程和孤儿进程
  • Spring相关知识
  • 解决接口耗时长问题
  • 软考 系统架构设计师系列知识点之杂项集萃(130)
  • 上证50股指期货为何波动很小?
  • AP状态管理中提到的两种“业务逻辑”
  • 34、扩展仓储管理系统 (跨境汽车零部件模拟) - /物流与仓储组件/extended-warehouse-management
  • 家用电器,让现代家庭生活更美好
  • 华为云ModelArts+Dify AI:双剑合璧使能AI应用敏捷开发
  • 红日靶场5
  • 有鹿机器人:智慧清洁新时代的引领者
  • 今天,字节开源Seed-OSS-36B模型,512k上下文
  • es6常用方法来解决功能需求
  • 【LeetCode题解】LeetCode 240. 搜索二维矩阵 II
  • 2025图表制作完全指南:设计规范、工具选型与行业案例
  • sqli-labs通关笔记-第60关 GET字符型报错注入(双引号括号闭合 限制5次探测机会)
  • 打开或者安装Navicat时出现Missing required library libcurl.dll,126报错解决方法(libmysql_e.dll等)
  • Google Chrome V8 <14.1.58 越界写入漏洞
  • Shell 脚本条件测试
  • Chrome/360 浏览器扩展深度解析:内置扩展与普通扩展的实现机制对比
  • 智能求职推荐系统演示说明
  • 亚马逊长尾关键词发掘:从人工苦力到智能闭环的进化之路
  • 零成本加速:EdgeOne免费套餐3分钟接入指南
  • 15-day12LLM结构变化、位置编码和投机采样
  • 带宽评估(二)lossbase v1
  • 【音视频】GOP 的设置和分析
  • C++ 单例模式学习
  • Spark读取MySQL数据库表
  • CSS【详解】性能优化
  • 什么是区块链?从比特币到Web3的演进