在网络编程领域,跨平台兼容性和 API 易用性是开发者的核心诉求。Qt 作为经典的跨平台框架,对底层网络 API 进行了高度封装,推出了一套统一、高效的网络编程接口,让开发者无需关注 Windows、Linux、macOS 的底层差异,仅凭一套代码就能实现各类网络通信功能。本文将聚焦 Qt 网络编程的三大核心场景 ——UDP Socket、TCP Socket、HTTP Client,从基础 API 解析到实战,手把手带你吃透 Qt 网络编程,轻松应对数据传输、服务器开发、接口调用等各类需求!下面就让我们正式开始吧!
传统网络编程(如原生 Socket)面临三大痛点:
WSAStartup与 Linux 的socket函数接口不同,需大量条件编译适配;而 Qt 网络 API 完美解决了这些问题:
QUdpSocket、QTcpSocket等类封装 Socket 操作,接口直观易用;在进行 Qt 网络编程前,需完成两个关键准备:
添加网络模块:在项目的.pro文件中添加QT += network,否则无法使用 Qt 网络相关类;
QT += core gui network // 新增network模块了解核心类关系:Qt 网络模块的核心类围绕 “Socket” 和 “请求 - 响应” 模型设计,关键类如下:
QUdpSocket(UDP Socket)、QNetworkDatagram(UDP 数据报);QTcpServer(TCP 服务器)、QTcpSocket(TCP 客户端 / 服务端通信 Socket);QNetworkAccessManager(HTTP 请求管理器)、QNetworkRequest(HTTP 请求)、QNetworkReply(HTTP 响应)。readyRead信号通知数据到达,connected信号通知连接建立; UDP(User Datagram Protocol)是一种无连接、不可靠的传输协议,适用于对实时性要求高、可容忍少量数据丢失的场景(如语音通话、视频流、游戏数据传输)。Qt 通过QUdpSocket和QNetworkDatagram类实现 UDP 通信。
方法 | 功能说明 | 对标原生 API |
|---|---|---|
bool bind(const QHostAddress &address, quint16 port) | 绑定 IP 地址和端口,用于接收数据 | bind |
qint64 writeDatagram(const QNetworkDatagram &datagram) | 发送 UDP 数据报 | sendto |
QNetworkDatagram receiveDatagram() | 接收 UDP 数据报 | recvfrom |
void readyRead() | 信号:有数据到达时触发 | 无(类似 IO 多路复用通知) |
bool hasPendingDatagrams() const | 判断是否有待接收的数据报 | 无 |
qint64 pendingDatagramSize() const | 获取待接收数据报的大小 | 无 |
QNetworkDatagram用于封装 UDP 数据报,包含数据、目标 IP、目标端口等信息:
方法 | 功能说明 |
|---|---|
QNetworkDatagram(const QByteArray &data, const QHostAddress &destAddress, quint16 destPort) | 构造函数:传入数据、目标 IP、目标端口 |
QByteArray data() const | 获取数据报中的数据 |
QHostAddress senderAddress() const | 获取发送方 IP 地址 |
quint16 senderPort() const | 获取发送方端口号 |
QHostAddress destinationAddress() const | 获取目标 IP 地址 |
quint16 destinationPort() const | 获取目标端口号 |
回显服务器是最基础的网络服务器:接收客户端发送的数据,原样返回给客户端。下面实现一个 UDP 回显服务器,支持多客户端同时连接。
QWidget;QListWidget(命名为listWidget),用于显示客户端请求和服务器响应日志;listWidget铺满整个窗口。#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QUdpSocket>
#include <QNetworkDatagram>
#include <QMessageBox>
#include <QDebug>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private slots:
// 处理收到的数据报
void processRequest();
private:
Ui::Widget *ui;
QUdpSocket *udpSocket; // UDP Socket对象
const quint16 SERVER_PORT = 9090; // 服务器端口
};
#endif // WIDGET_H#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
this->setWindowTitle("UDP回显服务器(端口:9090)");
ui->listWidget->setReadOnly(true);
// 1. 实例化UDP Socket
udpSocket = new QUdpSocket(this);
// 2. 绑定端口(监听所有网卡的9090端口)
bool bindSuccess = udpSocket->bind(QHostAddress::Any, SERVER_PORT);
if (!bindSuccess)
{
QMessageBox::critical(this, "启动失败", "端口绑定失败:" + udpSocket->errorString());
return;
}
// 3. 连接信号槽:有数据到达时触发processRequest
connect(udpSocket, &QUdpSocket::readyRead, this, &Widget::processRequest);
}
Widget::~Widget()
{
delete ui;
}
void Widget::processRequest()
{
// 循环接收所有待处理的数据报
while (udpSocket->hasPendingDatagrams())
{
// 1. 接收数据报
QNetworkDatagram datagram = udpSocket->receiveDatagram();
if (datagram.isValid())
{
// 2. 解析数据报信息
QString clientIp = datagram.senderAddress().toString();
quint16 clientPort = datagram.senderPort();
QString requestData = QString::fromUtf8(datagram.data());
// 3. 显示日志
QString log = QString("[%1:%2] 收到请求:%3")
.arg(clientIp)
.arg(clientPort)
.arg(requestData);
ui->listWidget->addItem(log);
// 4. 回显数据(原样返回给客户端)
QNetworkDatagram responseDatagram(
requestData.toUtf8(), // 响应数据
datagram.senderAddress(), // 客户端IP
datagram.senderPort() // 客户端端口
);
udpSocket->writeDatagram(responseDatagram);
// 5. 显示响应日志
QString responseLog = QString("[%1:%2] 发送响应:%3")
.arg(clientIp)
.arg(clientPort)
.arg(requestData);
ui->listWidget->addItem(responseLog);
}
}
}由于这里还没完成客户端的编写,还没办法打印出日志,这里我就先不展示运行效果。
QHostAddress::Any:表示绑定所有网卡的 IP 地址,客户端可通过任意网卡 IP 访问服务器;hasPendingDatagrams():判断是否有待接收的数据报,避免receiveDatagram()阻塞;senderAddress()和senderPort()定位客户端。客户端功能:输入文本并发送给服务器,接收服务器的回显响应并显示。
QLineEdit(命名为lineEdit):输入发送数据;QPushButton(命名为pushButton,文本为 “发送”):触发发送操作;QListWidget(命名为listWidget):显示发送和接收的消息;QLineEdit和pushButton水平布局,下方放置QListWidget。#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QUdpSocket>
#include <QNetworkDatagram>
#include <QDebug>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private slots:
// 发送按钮点击槽函数
void on_pushButton_clicked();
// 处理服务器响应
void processResponse();
private:
Ui::Widget *ui;
QUdpSocket *udpSocket; // UDP Socket对象
const QString SERVER_IP = "127.0.0.1"; // 服务器IP
const quint16 SERVER_PORT = 9090; // 服务器端口
};
#endif // WIDGET_H#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
this->setWindowTitle("客户端");
ui->listWidget->setReadOnly(true);
// 1. 实例化UDP Socket
udpSocket = new QUdpSocket(this);
// 2. 连接信号槽:有响应数据到达时触发processResponse
connect(udpSocket, &QUdpSocket::readyRead, this, &Widget::processResponse);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_clicked()
{
// 1. 获取输入框内容
QString sendData = ui->lineEdit->text().trimmed();
if (sendData.isEmpty())
{
ui->listWidget->addItem("错误:发送数据不能为空!");
return;
}
// 2. 构造UDP数据报
QNetworkDatagram datagram(
sendData.toUtf8(), // 发送数据
QHostAddress(SERVER_IP), // 服务器IP
SERVER_PORT // 服务器端口
);
// 3. 发送数据报
qint64 sendBytes = udpSocket->writeDatagram(datagram);
if (sendBytes != -1)
{
// 4. 显示发送日志
QString log = QString("客户端说:%1").arg(sendData);
ui->listWidget->addItem(log);
// 清空输入框
ui->lineEdit->clear();
}
else
{
ui->listWidget->addItem("发送失败:" + udpSocket->errorString());
}
}
void Widget::processResponse()
{
// 接收服务器响应
while (udpSocket->hasPendingDatagrams())
{
QNetworkDatagram datagram = udpSocket->receiveDatagram();
if (datagram.isValid())
{
QString responseData = QString::fromUtf8(datagram.data());
QString log = QString("[服务器说]:%1").arg(responseData);
ui->listWidget->addItem(log);
}
}
}SERVER_IP为服务器的实际 IP 地址; TCP(Transmission Control Protocol)是一种面向连接、可靠、有序的传输协议,适用于对数据完整性要求高的场景(如文件传输、登录验证、数据同步)。Qt 通过QTcpServer(服务器端)和QTcpSocket(客户端 / 服务器端通信)实现 TCP 通信。
QTcpServer用于监听端口、接收客户端连接,核心接口如下:
接口类型 | 名称 | 功能说明 | 对标原生 API |
|---|---|---|---|
方法 | bool listen(const QHostAddress &address, quint16 port) | 绑定 IP 和端口,开始监听客户端连接 | bind+listen |
方法 | QTcpSocket *nextPendingConnection() | 获取待处理的客户端连接 Socket | accept |
信号 | void newConnection() | 信号:有新客户端连接时触发 | 无(类似 IO 多路复用通知) |
方法 | bool isListening() const | 判断是否正在监听 | 无 |
方法 | void close() | 关闭服务器,停止监听 | close |
QTcpSocket用于 TCP 连接的建立、数据收发、连接关闭,核心接口如下:
接口类型 | 名称 | 功能说明 | 对标原生 API |
|---|---|---|---|
方法 | void connectToHost(const QString &hostName, quint16 port) | 连接到指定服务器(客户端用) | connect |
方法 | qint64 write(const QByteArray &data) | 发送数据 | send/write |
方法 | QByteArray readAll() | 读取接收缓冲区中的所有数据 | recv/read |
方法 | qint64 bytesAvailable() const | 获取接收缓冲区中可读取的字节数 | 无 |
信号 | void connected() | 信号:连接建立成功时触发 | 无 |
信号 | void readyRead() | 信号:有数据到达时触发 | 无 |
信号 | void disconnected() | 信号:连接断开时触发 | 无 |
信号 | void errorOccurred(QAbstractSocket::SocketError socketError) | 信号:发生错误时触发 | 无 |
方法 | void close() | 关闭连接 | close |
TCP 回显服务器功能:监听端口,接收客户端连接,接收客户端数据并原样返回,支持多客户端同时连接。
QListWidget(命名为listWidget):显示服务器日志(客户端上线、下线、请求、响应);QListWidget铺满整个窗口。#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QTcpServer>
#include <QTcpSocket>
#include <QMessageBox>
#include <QDebug>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private slots:
// 处理新客户端连接
void onNewConnection();
// 处理客户端发送的数据
void onReadyRead();
// 处理客户端断开连接
void onDisconnected();
private:
Ui::Widget *ui;
QTcpServer *tcpServer; // TCP服务器对象
const quint16 SERVER_PORT = 9090; // 服务器端口
};
#endif // WIDGET_H#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
this->setWindowTitle("服务器");
ui->listWidget->setReadOnly(true);
// 1. 实例化TCP服务器
tcpServer = new QTcpServer(this);
// 2. 监听所有网卡的9090端口
bool listenSuccess = tcpServer->listen(QHostAddress::Any, SERVER_PORT);
if (!listenSuccess)
{
QMessageBox::critical(this, "启动失败", "服务器启动失败:" + tcpServer->errorString());
return;
}
// 3. 连接信号槽:有新客户端连接时触发onNewConnection
connect(tcpServer, &QTcpServer::newConnection, this, &Widget::onNewConnection);
}
Widget::~Widget()
{
// 关闭所有客户端连接
foreach (QTcpSocket *socket, tcpServer->findChildren<QTcpSocket*>())
{
socket->close();
socket->deleteLater();
}
// 关闭服务器
tcpServer->close();
delete ui;
}
void Widget::onNewConnection()
{
// 1. 获取新客户端连接的Socket
QTcpSocket *clientSocket = tcpServer->nextPendingConnection();
if (!clientSocket)
{
ui->listWidget->addItem("警告:获取客户端连接失败!");
return;
}
// 2. 获取客户端信息
QString clientIp = clientSocket->peerAddress().toString();
quint16 clientPort = clientSocket->peerPort();
QString log = QString("[%1:%2] 客户端上线!").arg(clientIp).arg(clientPort);
ui->listWidget->addItem(log);
// 3. 连接客户端Socket的信号槽
// 数据到达时触发onReadyRead
connect(clientSocket, &QTcpSocket::readyRead, this, &Widget::onReadyRead);
// 连接断开时触发onDisconnected
connect(clientSocket, &QTcpSocket::disconnected, this, &Widget::onDisconnected);
// 自动销毁Socket对象
connect(clientSocket, &QTcpSocket::destroyed, [=]() {
ui->listWidget->addItem(QString("[%1:%2] Socket对象已销毁").arg(clientIp).arg(clientPort));
});
}
void Widget::onReadyRead()
{
// 1. 获取发送数据的客户端Socket
QTcpSocket *clientSocket = qobject_cast<QTcpSocket*>(sender());
if (!clientSocket)
return;
// 2. 读取数据(UTF-8编码)
QByteArray data = clientSocket->readAll();
QString requestData = QString::fromUtf8(data);
// 3. 获取客户端信息
QString clientIp = clientSocket->peerAddress().toString();
quint16 clientPort = clientSocket->peerPort();
// 4. 显示请求日志
QString requestLog = QString("[%1:%2] 收到请求:%3").arg(clientIp).arg(clientPort).arg(requestData);
ui->listWidget->addItem(requestLog);
// 5. 回显数据(原样返回)
clientSocket->write(data);
// 6. 显示响应日志
QString responseLog = QString("[%1:%2] 发送响应:%3").arg(clientIp).arg(clientPort).arg(requestData);
ui->listWidget->addItem(responseLog);
}
void Widget::onDisconnected()
{
// 1. 获取断开连接的客户端Socket
QTcpSocket *clientSocket = qobject_cast<QTcpSocket*>(sender());
if (!clientSocket)
return;
// 2. 获取客户端信息
QString clientIp = clientSocket->peerAddress().toString();
quint16 clientPort = clientSocket->peerPort();
// 3. 显示下线日志
QString log = QString("[%1:%2] 客户端下线!").arg(clientIp).arg(clientPort);
ui->listWidget->addItem(log);
// 4. 释放Socket资源(延迟销毁)
clientSocket->deleteLater();
}由于我们还没实现客户端,所以就暂时不展示运行效果。
nextPendingConnection():必须在newConnection信号触发后调用,获取新客户端的 Socket;QTcpSocket,服务器通过sender()区分不同客户端;deleteLater()销毁QTcpSocket,避免内存泄漏;TCP 客户端功能:连接服务器,输入文本并发送,接收服务器回显响应,显示连接状态和消息日志。
QLineEdit(命名为lineEdit):输入发送数据;QPushButton(命名为pushButton,文本为 “发送”):触发发送操作;QListWidget(命名为listWidget):显示连接状态、发送和接收日志;QLineEdit和pushButton水平布局,下方放置QListWidget。#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QTcpSocket>
#include <QAbstractSocket>
#include <QDebug>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private slots:
// 发送按钮点击槽函数
void on_pushButton_clicked();
// 连接建立成功槽函数
void onConnected();
// 接收服务器响应槽函数
void onReadyRead();
// 连接断开槽函数
void onDisconnected();
// 错误处理槽函数
void onErrorOccurred(QAbstractSocket::SocketError error);
private:
Ui::Widget *ui;
QTcpSocket *tcpSocket; // TCP Socket对象
const QString SERVER_IP = "127.0.0.1"; // 服务器IP
const quint16 SERVER_PORT = 9090; // 服务器端口
};
#endif // WIDGET_H#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
this->setWindowTitle("客户端");
ui->listWidget->setReadOnly(true);
// 1. 实例化TCP Socket
tcpSocket = new QTcpSocket(this);
// 2. 连接信号槽
connect(tcpSocket, &QTcpSocket::connected, this, &Widget::onConnected);
connect(tcpSocket, &QTcpSocket::readyRead, this, &Widget::onReadyRead);
connect(tcpSocket, &QTcpSocket::disconnected, this, &Widget::onDisconnected);
connect(tcpSocket, &QTcpSocket::errorOccurred, this, &Widget::onErrorOccurred);
// 3. 连接服务器
tcpSocket->connectToHost(SERVER_IP, SERVER_PORT);
}
Widget::~Widget()
{
// 关闭连接
if (tcpSocket->state() == QAbstractSocket::ConnectedState)
{
tcpSocket->disconnectFromHost();
}
delete ui;
}
void on_pushButton_clicked()
{
// 1. 检查连接状态
if (tcpSocket->state() != QAbstractSocket::ConnectedState)
{
ui->listWidget->addItem("连接服务器出错!");
return;
}
// 2. 获取输入框内容
QString sendData = ui->lineEdit->text().trimmed();
if (sendData.isEmpty())
{
ui->listWidget->addItem("错误:发送数据不能为空!");
return;
}
// 3. 发送数据(UTF-8编码)
qint64 sendBytes = tcpSocket->write(sendData.toUtf8());
if (sendBytes != -1)
{
// 4. 显示发送日志
QString log = QString("客户端说:%1").arg(sendData);
ui->listWidget->addItem(log);
// 清空输入框
ui->lineEdit->clear();
}
else
{
ui->listWidget->addItem("发送失败:" + tcpSocket->errorString());
}
}
void Widget::onConnected()
{
// 连接建立成功日志
}
void Widget::onReadyRead()
{
// 1. 读取服务器响应数据
QByteArray data = tcpSocket->readAll();
QString responseData = QString::fromUtf8(data);
// 2. 显示响应日志
QString log = QString("[服务器说]:%1").arg(responseData);
ui->listWidget->addItem(log);
}
void Widget::onDisconnected()
{
ui->listWidget->addItem("连接已断开!");
}
void Widget::onErrorOccurred(QAbstractSocket::SocketError error)
{
// 错误日志
QString errorLog = QString("错误:%1").arg(tcpSocket->errorString());
ui->listWidget->addItem(errorLog);
}connectToHost():异步连接服务器,连接结果通过connected信号通知,不会阻塞 UI;state()判断连接状态,避免在未连接状态下发送数据;errorOccurred信号捕获连接错误(如服务器未启动、网络不通),提升用户体验。特性 | TCP | UDP | 适用场景 |
|---|---|---|---|
连接方式 | 面向连接 | 无连接 | TCP:文件传输、登录验证;UDP:实时通信、游戏数据 |
可靠性 | 可靠(重传、排序、流量控制) | 不可靠(可能丢失、乱序) | TCP:数据完整性要求高;UDP:实时性要求高 |
数据形式 | 字节流 | 数据报 | TCP:大文件传输;UDP:短消息传输 |
效率 | 较低(需维护连接、确认机制) | 较高(无额外开销) | TCP:不追求极致效率;UDP:实时性优先 |
连接数 | 服务器支持多客户端 | 无连接限制 | TCP:多用户交互;UDP:广播 / 组播 |
HTTP 是应用层协议,基于 TCP 实现,广泛用于 Web 接口调用、文件下载、数据提交等场景。Qt 通过QNetworkAccessManager、QNetworkRequest、QNetworkReply类实现 HTTP 客户端功能,支持 GET、POST、PUT、DELETE 等请求方法。
QIODevice,可通过readAll()读取响应数据。方法 | 功能说明 |
|---|---|
QNetworkReply *get(const QNetworkRequest &request) | 发送 HTTP GET 请求,返回响应对象 |
QNetworkReply *post(const QNetworkRequest &request, const QByteArray &data) | 发送 HTTP POST 请求,返回响应对象 |
QNetworkReply *put(const QNetworkRequest &request, const QByteArray &data) | 发送 HTTP PUT 请求,返回响应对象 |
QNetworkReply *deleteResource(const QNetworkRequest &request) | 发送 HTTP DELETE 请求,返回响应对象 |
方法 | 功能说明 |
|---|---|
QNetworkRequest(const QUrl &url) | 构造函数:传入请求 URL |
void setHeader(QNetworkRequest::KnownHeaders header, const QVariant &value) | 设置请求头(如 Content-Type、User-Agent) |
void setUrl(const QUrl &url) | 设置请求 URL |
QUrl url() const | 获取请求 URL |
接口类型 | 名称 | 功能说明 |
|---|---|---|
方法 | QByteArray readAll() | 读取响应体所有数据 |
方法 | QNetworkReply::NetworkError error() const | 获取响应错误状态 |
方法 | QString errorString() const | 获取错误描述 |
方法 | QVariant header(QNetworkRequest::KnownHeaders header) const | 获取响应头 |
信号 | void finished() | 信号:响应接收完成时触发 |
信号 | void downloadProgress(qint64 bytesReceived, qint64 bytesTotal) | 信号:下载进度更新时触发(用于大文件下载) |
枚举值 | 功能说明 |
|---|---|
QNetworkRequest::ContentTypeHeader | 描述请求体 / 响应体的类型(如 application/json、text/html) |
QNetworkRequest::ContentLengthHeader | 描述请求体 / 响应体的长度 |
QNetworkRequest::UserAgentHeader | 设置 User-Agent(模拟浏览器或客户端标识) |
QNetworkRequest::CookieHeader | 设置 Cookie |
实现功能:输入 URL,发送 GET 请求,获取响应数据并显示在文本框中(如调用百度首页接口)。
QLineEdit(命名为lineEdit):输入请求 URL;QPushButton(命名为pushButton,文本为 “发送请求”):触发请求;QPlainTextEdit(命名为plainTextEdit):显示响应数据;QLineEdit和pushButton水平布局,下方放置QPlainTextEdit;#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QUrl>
#include <QDebug>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private slots:
// 发送GET请求按钮点击槽函数
void on_pushButton_clicked();
// 响应接收完成槽函数
void onReplyFinished();
private:
Ui::Widget *ui;
QNetworkAccessManager *networkManager; // HTTP请求管理器
};
#endif // WIDGET_H#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
ui->plainTextEdit->setReadOnly(true);
// 默认URL:百度首页
ui->lineEdit->setText("http://www.baidu.com");
// 1. 实例化QNetworkAccessManager(全局唯一即可)
networkManager = new QNetworkAccessManager(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_clicked()
{
// 1. 获取URL
QString urlStr = ui->lineEdit->text().trimmed();
if (urlStr.isEmpty())
{
ui->plainTextEdit->setPlainText("错误:URL不能为空!");
return;
}
QUrl url(urlStr);
if (!url.isValid())
{
ui->plainTextEdit->setPlainText("错误:无效的URL!");
return;
}
// 2. 构造HTTP请求
QNetworkRequest request(url);
// 设置请求头:模拟浏览器(可选,部分服务器需验证User-Agent)
request.setHeader(QNetworkRequest::UserAgentHeader,
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36");
// 3. 发送GET请求
QNetworkReply *reply = networkManager->get(request);
// 连接信号槽:响应接收完成时触发onReplyFinished
connect(reply, &QNetworkReply::finished, this, &Widget::onReplyFinished);
// 自动销毁reply对象
connect(reply, &QNetworkReply::destroyed, [=]() {
ui->statusbar->clearMessage();
});
}
void Widget::onReplyFinished()
{
// 1. 获取响应对象
QNetworkReply *reply = qobject_cast<QNetworkReply*>(sender());
if (!reply)
return;
// 2. 处理响应
QString responseData;
if (reply->error() == QNetworkReply::NoError)
{
// 响应成功:读取响应体(UTF-8编码)
QByteArray data = reply->readAll();
responseData = QString::fromUtf8(data);
}
else
{
// 响应失败:显示错误信息
responseData = QString("请求失败:%1").arg(reply->errorString());
}
// 3. 显示响应数据
ui->plainTextEdit->setPlainText(responseData);
// 4. 释放资源
reply->deleteLater();
}http://www.baidu.com),点击 “发送请求”;QPlainTextEdit显示百度首页的 HTML 源码;QNetworkAccessManager是线程安全的,一个应用程序只需创建一个实例,避免重复创建;QTextCodec);readAll(),应分块读取(通过readyRead信号),避免占用过多内存。问题:发送或接收中文时出现乱码。
原因:编码不一致(如发送方使用 GBK,接收方使用 UTF-8)。
解决方案:
统一使用 UTF-8 编码:发送时用QString::toUtf8(),接收时用QString::fromUtf8();
若服务器使用 GBK 编码,需手动转换:
#include <QTextCodec>
// GBK转UTF-8
QTextCodec *codec = QTextCodec::codecForName("GBK");
QString gbkStr = codec->toUnicode(data);
QByteArray utf8Data = gbkStr.toUtf8();问题:网络操作导致 UI 界面卡顿。
原因:在 UI 线程中执行耗时网络操作(如大文件下载、长时间连接)。
解决方案:
waitForReadyRead()等阻塞函数。问题:TCP 客户端连接服务器失败,提示 “连接超时” 或 “无法连接到远程服务器”。
常见原因与解决方案:
问题:UDP 客户端发送数据后,服务器未收到,或服务器发送响应后,客户端未收到。
原因:UDP 是无连接、不可靠协议,数据可能在传输过程中丢失。
解决方案:
问题:发送 HTTP 请求时返回 403 Forbidden 或 404 Not Found。
解决方案:
问题:频繁发送网络请求后,内存占用持续增加。
原因:未释放QNetworkReply、QTcpSocket等对象资源。
解决方案:
QNetworkReply:在finished信号触发后调用deleteLater();QTcpSocket:客户端断开连接后调用deleteLater();QNetworkAccessManager,一个应用程序只需一个实例。Qt 网络编程通过高度封装的 API,让跨平台网络开发变得简单高效。掌握 Qt 网络编程,能让你轻松应对各类网络相关开发需求。建议多动手实践,结合实际项目场景灵活运用不同的协议和 API。如果你有任何问题或需要进一步探讨高级场景,欢迎在评论区留言交流!