要使用Qt的网络通信,要先引入Network
模块
需要在CMake
中添加如下语句:
cmake
find_package(Qt6 COMPONENTS Network REQUIRED)
target_link_libraries(your_target_name PRIVATE Qt6::Network)
UDP
主要的类有两个:
QUdpSocket
:表示一个UDP的socket文件
名称 | 类型 | 说明 | 对标原生 API |
---|---|---|---|
bind(const QHostAddress&, quint16) | 方法 | 绑定指定的端口号. | bind |
receiveDatagram() | 方法 | 返回 QNetworkDatagram,读取一个 UDP 数据报. | recvfrom |
writeDatagram(const QNetworkDatagram&) | 方法 | 发送一个 UDP 数据报. | sendto |
readyRead | 信号 | 在收到数据并准备就绪后触发. | 无 (类似于 IO 多路复用的通知机制) |
QNetworkDataGram
:表示一个UDP的数据包
名称 | 类型 | 说明 | 对标原生 API |
---|---|---|---|
QNetworkDatagram(const QByteArray&, const QHostAddress&, quint16) |
构造函数 | 通过 QByteArray 、目标 IP 地址、目标端口号构造一个 UDP 数据报,通常用于发送数据时 |
无 |
data() |
方法 | 获取数据报内部持有的数据,返回 QByteArray |
无 |
senderAddress() |
方法 | 获取数据报中包含的对端的 IP 地址 | 无,recvfrom 包含了该功能 |
senderPort() |
方法 | 获取数据报中包含的对端的端口号 | 无,recvfrom 包含了该功能 |
利用上面的接口,实现一个带有图形化界面的Echo
服务器和客户端:
服务器:
cpp
// widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QUdpSocket>
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE
class Widget : public QWidget {
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
void handlerRequest();
private:
bool process(const QString& request, QString* response);
private:
Ui::Widget *ui;
QUdpSocket* socket_ = nullptr;
};
#endif // WIDGET_H
// widget.cpp
#include "widget.h"
#include <QMessageBox>
#include <QNetworkDatagram>
#include "./ui_widget.h"
Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) {
ui->setupUi(this);
this->setWindowTitle("服务器");
ui->listWidget->setWindowTitle("服务器日志");
socket_ = new QUdpSocket(this); // 创建UDP套接字
// connect
connect(socket_, &QUdpSocket::readyRead, this, &Widget::handlerRequest);
// 进行端口绑定
if (!socket_->bind(QHostAddress::Any, 8080)) {
QMessageBox::critical(this, "服务器启动失败", socket_->errorString());
return;
}
}
Widget::~Widget() { delete ui; }
void Widget::handlerRequest() {
const QNetworkDatagram data = socket_->receiveDatagram();
QString request = data.data();
QString response;
if (!process(request, &response)) {
//...
}
QNetworkDatagram send_data(response.toUtf8(), data.senderAddress(), data.senderPort());
socket_->writeDatagram(send_data);
QString log = "[" + data.senderAddress().toString() + ":" + QString::number(data.senderPort()) +
"]: " + request;
ui->listWidget->addItem(log);
}
// 请求处理函数
bool Widget::process(const QString &request, QString *response) {
*response = request;
return true;
}
客户端:
cpp
// widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QUdpSocket>
#include <QWidget>
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 handlerResponse();
private:
Ui::Widget *ui;
QUdpSocket *socket_ = nullptr;
};
#endif // WIDGET_H
// widget.cpp
#include "widget.h"
#include <QNetworkDatagram>
#include "./ui_widget.h"
const QString SERVER_IP = "127.0.0.1";
const uint16_t SERVER_PORT = 8080;
Widget::Widget(QWidget* parent) : QWidget(parent), ui(new Ui::Widget) {
ui->setupUi(this);
this->setWindowTitle("客户端");
ui->pushButton->setShortcut(Qt::Key_Return);
socket_ = new QUdpSocket(this); // 创建UDP套接字
// connect
connect(socket_, &QUdpSocket::readyRead, this, &Widget::handlerResponse);
}
Widget::~Widget() { delete ui; }
void Widget::on_pushButton_clicked() {
QString text = this->ui->lineEdit->text();
this->ui->lineEdit->clear();
const QNetworkDatagram send_data(text.toUtf8(), QHostAddress(SERVER_IP), SERVER_PORT);
socket_->writeDatagram(send_data);
}
void Widget::handlerResponse() {
const QNetworkDatagram& data = socket_->receiveDatagram();
this->ui->listWidget->addItem("RECV: " + data.data());
}
效果:

TCP
核心的两个类:
QTcpServer
,用于监听端口,获取客户端连接:
名称 | 类型 | 说明 | 对标原生 API |
---|---|---|---|
listen(const QHostAddress&, quint16 port) |
方法 | 绑定指定的地址和端口号,并开始监听。 | bind 和 listen |
nextPendingConnection() |
方法 | 从系统中获取到一个已经建立好的 TCP 连接。 返回一个 QTcpSocket ,表示这个客户端的连接。 通过这个 socket 对象完成和客户端之间的通信。 |
accept |
newConnection |
信号 | 有新的客户端建立连接好之后触发。 | 无(但类似于 IO 多路复用中的通知机制) |
QTcpSocket
,用于客户端和服务器的连接以及通信
名称 | 类型 | 说明 | 对标原生 API |
---|---|---|---|
readAll() |
方法 | 读取当前接收缓冲区中的所有数据,返回 QByteArray 对象 |
read |
write(const QByteArray&) |
方法 | 把数据写入 socket 中 | write |
deleteLater |
方法 | 暂时把 socket 对象标记为无效,Qt 会在下个事件循环中析构释放该对象 | 无(类似于 "半自动化的垃圾回收") |
connectToHost |
方法 | 客户端连接到服务器,非阻塞 | connect |
waitForConnected |
方法 | 等待,直到连接建立,阻塞 | 无 |
readyRead |
信号 | 有数据到达并准备就绪时触发 | 无(类似于 IO 多路复用的通知机制) |
disconnected |
信号 | 连接断开时触发 | 无(类似于 IO 多路复用的通知机制) |
利用上面的接口,实现一个带有图形化界面的Echo
服务器和客户端:
服务器:
cpp
// widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QTcpServer>
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE
class Widget : public QWidget {
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private:
void handlerConnection();
bool process(const QString& request, QString* response);
private:
Ui::Widget *ui;
QTcpServer* server_ = nullptr;
};
#endif // WIDGET_H
// widget.cpp
#include "widget.h"
#include <QMessageBox>
#include <QTcpSocket>
#include "./ui_widget.h"
Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) {
ui->setupUi(this);
this->setWindowTitle("服务器");
// 创建TCP服务器
server_ = new QTcpServer(this);
// connect
connect(server_, &QTcpServer::newConnection, this, &Widget::handlerConnection);
// 绑定 + 监听
if (!server_->listen(QHostAddress::Any, 8080)) {
QMessageBox::critical(this, "服务器启动错误", server_->errorString());
exit(-1);
}
}
Widget::~Widget() { delete ui; }
void Widget::handlerConnection() {
// 获得与客户端通信的socket
QTcpSocket *socket = server_->nextPendingConnection();
const QString log = "[" + socket->peerAddress().toString() + ":" +
QString::number(socket->peerPort()) + "] : " + "连接建立";
this->ui->listWidget->addItem(log);
// connect 消息处理
connect(socket, &QTcpSocket::readyRead, this, [=]() {
const QString &data = socket->readAll(); // 接收请求
QString response;
// 处理请求,构造响应
if (!process(data, &response)) {
//...
}
// 返回响应
socket->write(response.toUtf8());
const QString log = "[" + socket->peerAddress().toString() + ":" +
QString::number(socket->peerPort()) + "] : " + "request: " + data +
", response: " + response;
this->ui->listWidget->addItem(log);
});
// connect 连接断开
connect(socket, &QTcpSocket::disconnected, this, [=]() {
const QString log = "[" + socket->peerAddress().toString() + ":" +
QString::number(socket->peerPort()) + "] : " + "连接断开";
this->ui->listWidget->addItem(log);
// 下一个事件循环,自动释放 TcpSocket
socket->deleteLater();
});
}
bool Widget::process(const QString &request, QString *response) {
*response = request;
return true;
}
客户端:
cpp
// widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QTcpSocket>
#include <QWidget>
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();
private:
Ui::Widget *ui;
QTcpSocket *client_ = nullptr;
};
#endif // WIDGET_H
// widget.cpp
#include "widget.h"
#include <QMessageBox>
#include "./ui_widget.h"
const QString SERVER_IP = "127.0.0.1";
const uint16_t SERVER_PORT = 8080;
Widget::Widget(QWidget* parent) : QWidget(parent), ui(new Ui::Widget) {
ui->setupUi(this);
this->setWindowTitle("客户端");
ui->pushButton->setShortcut(Qt::Key_Return);
ui->lineEdit->setPlaceholderText("请输入请求信息");
// 创建客户端
client_ = new QTcpSocket(this);
// connect 消息处理
connect(client_, &QTcpSocket::readyRead, this, [=]() {
const QString& data = client_->readAll();
this->ui->listWidget->addItem("RECV: " + data);
});
// 连接服务器,并等待建立成功
client_->connectToHost(QHostAddress(SERVER_IP), SERVER_PORT);
if (!client_->waitForConnected()) {
QMessageBox::critical(this, "连接服务器失败", client_->errorString());
exit(-1);
}
}
Widget::~Widget() { delete ui; }
void Widget::on_pushButton_clicked() {
const QString& send_data = ui->lineEdit->text();
ui->lineEdit->clear();
client_->write(send_data.toUtf8());
}
效果:

HTTP
关键类:
QNetworkAccessManage
,提供了HTTP的核心操作:
方法 | 说明 |
---|---|
get(const QNetworkRequest& ) |
发起一个 HTTP GET 请求,返回 QNetworkReply 对象 |
post(const QNetworkRequest& , const QByteArray& ) |
发起一个 HTTP POST 请求,返回 QNetworkReply 对象 |
QNetworkRequest
,表示一个HTTP请求 ,不包含body
:
方法 | 说明 |
---|---|
QNetworkRequest(const QUrl& ) |
通过 URL 构造一个 HTTP 请求 |
setHeader(QNetworkRequest::KnownHeaders header, const QVariant &value) |
设置请求头 |
QNetworkReply
,表示一个HTTP响应
方法 | 说明 |
---|---|
error() |
获取出错状态 |
errorString() |
获取出错原因的文本 |
readAll() |
读取响应 body |
header(QNetworkRequest::KnownHeaders header) |
读取响应指定 header 的值 |
deleteLater |
暂时把对象标记为无效,Qt 会在下个事件循环中析构释放该对象 |
其还有一个信号finished
,会在客户端收到完整的响应数据后触发
例如:
cpp
#ifndef WIDGET_H
#define WIDGET_H
#include <QNetworkAccessManager>
#include <QWidget>
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();
private:
Ui::Widget *ui;
QNetworkAccessManager *manager_ = nullptr;
};
#endif // WIDGET_H
// widget.cpp
#include "widget.h"
#include <QNetworkReply>
#include <QNetworkRequest>
#include "./ui_widget.h"
Widget::Widget(QWidget* parent) : QWidget(parent), ui(new Ui::Widget) {
ui->setupUi(this);
this->setWindowTitle("HTTP Client");
ui->lineEdit->setPlaceholderText("请输入要获取的 URL");
ui->pushButton->setShortcut(Qt::Key_Return);
ui->plainTextEdit->setReadOnly(true);
// 创建 http 管理器
manager_ = new QNetworkAccessManager(this);
}
Widget::~Widget() { delete ui; }
void Widget::on_pushButton_clicked() {
const QString& url = ui->lineEdit->text();
ui->lineEdit->clear();
// 构造请求
QNetworkRequest request{QUrl(url)};
// 发送get请求
QNetworkReply* reply = manager_->get(request);
// connect 相应获取处理方法
connect(reply, &QNetworkReply::finished, this, [=]() {
if (reply->error() == QNetworkReply::NoError) {
QString data = reply->readAll();
this->ui->plainTextEdit->setPlainText(data);
} else {
this->ui->plainTextEdit->setPlainText(reply->errorString());
}
// 获取到响应后,就需要释放资源
reply->deleteLater();
});
}
效果:
