C++ Qt 网络编程
网络编程主要依赖于操作系统提供的 Socket API。需要注意的是,C++ 标准库本身并未封装网络编程相关的 API。
Qt 网络编程基于操作系统 Socket API,涵盖 UDP、TCP 及 HTTP 协议。文章详解 QUdpSocket 与 QNetworkDatagram 构建 UDP 回显服务,利用 QTcpServer 和 QTcpSocket 处理 TCP 连接及粘包问题,并通过 QNetworkAccessManager 封装 HTTP 请求。示例包含完整源码及信号槽机制应用,强调 .pro 模块引入与资源管理,适合 C++ 开发者快速掌握 Qt 网络通信基础。

网络编程主要依赖于操作系统提供的 Socket API。需要注意的是,C++ 标准库本身并未封装网络编程相关的 API。
QUdpSocket 和 QTcpSocket。.pro 文件中添加 network 模块。QtCore 模块(默认已包含)。Qt 模块化处理是为了避免生成过大的可执行程序。默认情况下额外模块不会参与编译,需要在 .pro 文件中引入对应的模块。
主要的类有两个:
QUdpSocket:同 Linux 理解的 socket 概念,本质是打开的一个文件描述符。QNetworkDatagram:Qt 对 UDP 数据报的完整封装,包含二进制数据及发送/接收的地址端口等元信息。| 名称 | 类型 | 说明 | 对标原生 API |
|---|---|---|---|
| bind(const QHostAddress&, quint16) | 方法 | 绑定指定的端口号 | bind |
| receiveDatagram() | 方法 | 返回 QNetworkDatagram 读取一个 UDP 数据报 | recvfrom |
| writeDatagram(const QNetworkDatagram&) | 方法 | 发送一个 UDP 数据报 | sendto |
| readyRead | 信号 | 收到数据并准备就绪后触发 | 无 (类似 IO 多路复用通知机制) |
| 名称 | 类型 | 说明 | 对标原生 API |
|---|---|---|---|
| QNetworkDatagram(const QByteArray&, const QHostAddress& , quint16 ) | 构造函数 | 构造 UDP 数据报 | 无 |
| data() | 方法 | 获取数据报内部持有的数据 | 无 |
| senderAddress() | 方法 | 获取对端的 IP 地址 | 无 |
| senderPort() | 方法 | 获取对端的端口号 | 无 |
实现一个带有界面的 UDP 回显服务器。
QListWidget 表示消息列表。QUdpSocket 成员变量 socket 句柄。readyRead 信号)。注意:先连接信号槽,再绑定端口。socket->bind(QHostAddress::Any, 8081)。QMessageBox::critical 提示错误。receiveDatagram 获取 QNetworkDatagram,调用 data 转换为 QString。toUtf8(),构造新的 QNetworkDatagram(包含 senderAddress 和 senderPort),调用 writeDatagram。主要源码:
// widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QUdpSocket>
#include <QHostAddress>
#include <QMessageBox>
#include <QNetworkDatagram>
#include <QDateTime>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget {
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private:
Ui::Widget *ui;
QUdpSocket* socket;
QString process2(const QString& s);
private slots:
void process();
};
#endif // WIDGET_H
// widget.cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent), ui(new Ui::Widget) {
ui->setupUi(this);
socket = new QUdpSocket(parent);
ui->listWidget->setObjectName(QString("UDP 服务器"));
connect(socket, &QUdpSocket::readyRead, this, &Widget::process);
bool isconnect = socket->bind(QHostAddress::Any, 8081);
if (!isconnect) {
QMessageBox::critical(this, "错误", "服务器启动错误");
return;
}
setWindowTitle("Udp 服务端");
}
Widget::~Widget(){ delete ui; }
QString Widget::process2(const QString& request){ return request; }
void Widget::process(){
QNetworkDatagram requestPacket = socket->receiveDatagram();
QString request = requestPacket.data();
QString response = process2(request);
QNetworkDatagram responsePacket(response.toUtf8(), requestPacket.senderAddress(), requestPacket.senderPort());
socket->writeDatagram(responsePacket);
qint64 msTimestamp = QDateTime::currentMSecsSinceEpoch();
QDateTime dateTime = QDateTime::fromMSecsSinceEpoch(msTimestamp);
QString formatted = dateTime.toString("yyyy-MM-dd hh:mm:ss.zzz");
QString log = "[" + formatted + " : " + requestPacket.senderAddress().toString() + ":" + QString::number(requestPacket.senderPort()) + "] req: " + request + ", resp: " + response;
ui->listWidget->addItem(log);
}
QUdpSocket 成员变量。QNetworkDatagram(转成 toUtf8(),IP 转 QHostAddress)。writeDatagram 发送。readyRead 信号,读取 receiveDatagram,解析数据并显示。主要源码:
// widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QUdpSocket>
#include <QNetworkDatagram>
#include <QHostAddress>
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 *socket;
};
#endif // WIDGET_H
// widget.cpp
#include "widget.h"
#include "ui_widget.h"
QString SERVER_IP = "127.0.0.1";
quint16 SERVER_PORT = 8081;
Widget::Widget(QWidget *parent)
: QWidget(parent), ui(new Ui::Widget) {
ui->setupUi(this);
socket = new QUdpSocket(parent);
setWindowTitle("Udp 客户端");
connect(socket, &QUdpSocket::readyRead, this, &Widget::processResponse);
}
Widget::~Widget(){ delete ui; }
void Widget::on_pushButton_clicked(){
QString context = ui->lineEdit->text();
QNetworkDatagram request(context.toUtf8(), QHostAddress(SERVER_IP), SERVER_PORT);
socket->writeDatagram(request);
ui->listWidget->addItem("客户端说:" + context);
ui->lineEdit->clear();
}
void Widget::processResponse(){
const QNetworkDatagram& packet = socket->receiveDatagram();
QString s = QString::fromUtf8(packet.data());
ui->listWidget->addItem("服务器说" + s);
}
QNetworkDatagram:发送和接收的数据报类。QUdpSocket:完成消息读写操作,服务端需绑定 bind,客户端指定目的地。QUdpSocket::readyRead:消息就绪信号,注意先连接槽函数再绑定端口。.pro 中要添加 network。核心类是两个:QTcpServer(服务端监听)和 QTcpSocket(客户端和服务端交互)。
| 名称 | 类型 | 说明 | 对标原生 API |
|---|---|---|---|
| listen(const QHostAddress&, quint16 port) | 方法 | 绑定地址和端口,开始监听 | bind 和 listen |
| nextPendingConnection() | 方法 | 获取已建立的 tcp 连接,返回 QTcpSocket | accept |
| newConnection | 信号 | 有新客户端建立连接后触发 | 无 |
| 名称 | 类型 | 说明 | 对标原生 API |
|---|---|---|---|
| readAll() | 方法 | 读取当前接收缓冲区中的所有数据 | read |
| write(const QByteArray& ) | 方法 | 把数据写入 socket 中 | write |
| deleteLater | 方法 | 标记为无效,下个事件循环析构 | 无 |
| peerAddress / peerPort | 方法 | 获取对端客户端的 ip/port | 无 |
| readyRead | 信号 | 有数据到达并准备就绪时触发 | 无 |
| disconnected | 信号 | 连接断开时触发 | 无 |
.pro 中添加 network。ListWidget。QTcpServer 成员变量。QTcpServer 实例。newConnection 信号到 processConnection。processConnection 中获取 clientSocket,连接其 readyRead 和 disconnected 信号。server->listen(QHostAddress::Any, 8080)。主要源码:
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent), ui(new Ui::Widget) {
ui->setupUi(this);
setWindowTitle("服务器");
server = new QTcpServer(parent);
connect(server, &QTcpServer::newConnection, this, &Widget::processConnection);
bool ret = server->listen(QHostAddress::Any, 8080);
if (!ret) {
QMessageBox::critical(this, "服务器启动失败", server->errorString());
return;
}
}
Widget::~Widget(){ delete ui; }
QString Widget::process(QString& request){ return request; }
void Widget::processConnection(){
QTcpSocket *clientSocket = server->nextPendingConnection();
QHostAddress add = clientSocket->peerAddress();
quint16 port = clientSocket->peerPort();
ui->listWidget->addItem("[" + add.toString() + ":" + QString::number(port) + "]: 客户端已上线");
connect(clientSocket, &QTcpSocket::readyRead, this, [=](){
QString request = clientSocket->readAll();
const QString& response = process(request);
clientSocket->write(response.toUtf8());
QString log = "[" + add.toString() + ":" + QString::number(port) + "] req:" + request + ",res:" + response;
ui->listWidget->addItem(log);
});
connect(clientSocket, &QTcpSocket::disconnected, this, [=](){
QString log = "[" + add.toString() + ":" + QString::number(port) + "] 已下线";
qDebug() << log;
ui->listWidget->addItem(log);
clientSocket->deleteLater();
});
}
QTcpSocket 对象。connectToHost("127.0.0.1", 8080)。readyRead 信号处理响应。waitForConnected 判断连接是否成功。QByteArray 发送,显示日志,清空输入。主要源码:
// widget.cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent), ui(new Ui::Widget) {
ui->setupUi(this);
socket = new QTcpSocket(parent);
socket->connectToHost("127.0.0.1", 8080);
connect(socket, &QTcpSocket::readyRead, this, [=](){
QString response = socket->readAll();
ui->listWidget->addItem("服务端说:" + response);
});
if (!socket->waitForConnected()){
QMessageBox::critical(this, "client 连接服务器失败", socket->errorString());
exit(0);
}
}
Widget::~Widget(){ delete ui; }
void Widget::on_pushButton_clicked(){
QString request = ui->lineEdit->text();
socket->write(request.toUtf8());
ui->listWidget->addItem("客户端说:" + request);
ui->lineEdit->clear();
}
QTcpServer::newConnection 处理新链接信号。nextPendingConnection() 获取客户端 socket。QTcpSocket 常用函数:peerAddress, peerPort, readAll, write, connectToHost。QTcpSocket::readyRead 消息就绪信号。QTcpSocket::disconnected 用户断开时触发。HTTP 协议基于 TCP 实现。关键类主要是三个:QNetworkAccessManager, QNetworkRequest, QNetworkReply。
setHeader 设置请求头。QIODevice。.pro 中添加 network。QNetworkAccessManager 成员变量。QNetworkRequest。client->get(request),返回 QNetworkReply。response->finished 信号处理响应(非阻塞)。error,读取 readAll 内容显示到界面。response->deleteLater()。源码:
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent), ui(new Ui::Widget) {
ui->setupUi(this);
client = new QNetworkAccessManager(parent);
}
Widget::~Widget(){ delete ui; }
void Widget::on_pushButton_clicked(){
QUrl url = ui->lineEdit->text();
QNetworkRequest request(url);
QNetworkReply *response = client->get(request);
connect(response, &QNetworkReply::finished, this, [=](){
if(response->error() == QNetworkReply::NoError){
QString req = response->readAll();
ui->plainTextEdit->setPlainText(req);
} else {
ui->plainTextEdit->setPlainText(response->errorString());
}
response->deleteLater();
});
}
QNetworkRequest 构造 http 请求,填入 URL。QNetworkAccessManager 中的 get/post 发送请求,接收 QNetworkReply。QNetworkReply::finished 信号槽。error 是否为 NoError 后再处理响应。
微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online
将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online