Qt 与 GTK 图形框架教程 / 05 - Qt 网络编程 / Qt Network
Qt 网络编程 / Qt Network Programming
掌握 Qt 网络模块:TCP/UDP Socket、HTTP 客户端、WebSocket、JSON 和 REST API。 Master Qt Network: TCP/UDP sockets, HTTP client, WebSocket, JSON, and REST APIs.
5.1 网络模块总览 / Network Module Overview
Qt 网络类层次 / Qt Network Class Hierarchy
| 类别 / Category | 类 / Class | 用途 / Purpose |
|---|---|---|
| TCP | QTcpServer, QTcpSocket | TCP 服务端/客户端 |
| UDP | QUdpSocket | UDP 数据报通信 |
| HTTP | QNetworkAccessManager | HTTP/HTTPS 请求 |
| WebSocket | QWebSocket | WebSocket 双向通信 |
| SSL | QSslSocket | 安全套接字 / Secure socket |
| DNS | QDnsLookup | DNS 查询 |
| 网络信息 | QNetworkInterface | 网络接口信息 |
| 代理 | QNetworkProxy | 代理配置 |
# CMakeLists.txt
find_package(Qt6 REQUIRED COMPONENTS Network)
target_link_libraries(myapp PRIVATE Qt6::Network)
5.2 TCP 通信 / TCP Communication
TCP 服务端 / TCP Server
// tcpserver.h
#ifndef TCPSERVER_H
#define TCPSERVER_H
#include <QObject>
#include <QTcpServer>
#include <QTcpSocket>
#include <QMap>
class TcpServer : public QObject {
Q_OBJECT
public:
explicit TcpServer(quint16 port, QObject *parent = nullptr);
bool start();
void stop();
int clientCount() const { return m_clients.size(); }
signals:
void clientConnected(const QString &address);
void clientDisconnected(const QString &address);
void messageReceived(const QString &from, const QString &message);
void errorOccurred(const QString &error);
public slots:
void broadcast(const QString &message);
private slots:
void onNewConnection();
void onReadyRead();
void onDisconnected();
private:
QTcpServer *m_server;
QMap<QTcpSocket*, QString> m_clients; // socket -> address
quint16 m_port;
};
#endif
// tcpserver.cpp
#include "tcpserver.h"
#include <QDebug>
TcpServer::TcpServer(quint16 port, QObject *parent)
: QObject(parent), m_server(new QTcpServer(this)), m_port(port)
{
connect(m_server, &QTcpServer::newConnection,
this, &TcpServer::onNewConnection);
}
bool TcpServer::start()
{
if (!m_server->listen(QHostAddress::Any, m_port)) {
emit errorOccurred(m_server->errorString());
return false;
}
qDebug() << "TCP Server listening on port" << m_port;
return true;
}
void TcpServer::stop()
{
for (auto *client : m_clients.keys()) {
client->disconnectFromHost();
}
m_server->close();
}
void TcpServer::onNewConnection()
{
while (QTcpSocket *socket = m_server->nextPendingConnection()) {
QString addr = QStringLiteral("%1:%2")
.arg(socket->peerAddress().toString())
.arg(socket->peerPort());
m_clients.insert(socket, addr);
connect(socket, &QTcpSocket::readyRead, this, &TcpServer::onReadyRead);
connect(socket, &QTcpSocket::disconnected, this, &TcpServer::onDisconnected);
// 欢迎消息
socket->write("Welcome! You are connected.\n");
emit clientConnected(addr);
qDebug() << "Client connected:" << addr;
}
}
void TcpServer::onReadyRead()
{
auto *socket = qobject_cast<QTcpSocket*>(sender());
if (!socket) return;
while (socket->canReadLine()) {
QString line = QString::fromUtf8(socket->readLine()).trimmed();
if (!line.isEmpty()) {
emit messageReceived(m_clients.value(socket), line);
// 广播给其他客户端
QByteArray msg = QStringLiteral("[%1]: %2\n")
.arg(m_clients.value(socket), line).toUtf8();
for (auto *client : m_clients.keys()) {
if (client != socket) {
client->write(msg);
}
}
}
}
}
void TcpServer::onDisconnected()
{
auto *socket = qobject_cast<QTcpSocket*>(sender());
if (!socket) return;
QString addr = m_clients.take(socket);
emit clientDisconnected(addr);
socket->deleteLater();
qDebug() << "Client disconnected:" << addr;
}
void TcpServer::broadcast(const QString &message)
{
QByteArray data = message.toUtf8() + "\n";
for (auto *client : m_clients.keys()) {
client->write(data);
}
}
TCP 客户端 / TCP Client
// tcpclient.cpp - 简单 TCP 客户端
#include <QTcpSocket>
#include <QDebug>
class TcpClient : public QObject {
Q_OBJECT
public:
explicit TcpClient(QObject *parent = nullptr)
: QObject(parent), m_socket(new QTcpSocket(this))
{
connect(m_socket, &QTcpSocket::connected, this, [this]() {
qDebug() << "Connected to server";
emit connected();
});
connect(m_socket, &QTcpSocket::readyRead, this, [this]() {
while (m_socket->canReadLine()) {
QString line = QString::fromUtf8(m_socket->readLine()).trimmed();
emit messageReceived(line);
}
});
connect(m_socket, &QTcpSocket::disconnected, this, &TcpClient::disconnected);
connect(m_socket, &QAbstractSocket::errorOccurred, this, [this]() {
emit errorOccurred(m_socket->errorString());
});
}
void connectToHost(const QString &host, quint16 port) {
m_socket->connectToHost(host, port);
}
void send(const QString &message) {
if (m_socket->state() == QAbstractSocket::ConnectedState) {
m_socket->write((message + "\n").toUtf8());
}
}
signals:
void connected();
void disconnected();
void messageReceived(const QString &msg);
void errorOccurred(const QString &error);
private:
QTcpSocket *m_socket;
};
Python 示例 (PySide6)
#!/usr/bin/env python3
"""TCP 服务端示例 - PySide6"""
import sys
from PySide6.QtCore import QObject, Signal, Slot
from PySide6.QtNetwork import QTcpServer, QTcpSocket, QHostAddress
class ChatServer(QObject):
client_connected = Signal(str)
client_disconnected = Signal(str)
message_received = Signal(str, str)
def __init__(self, port: int = 9999, parent=None):
super().__init__(parent)
self._server = QTcpServer()
self._clients: dict[QTcpSocket, str] = {}
self._port = port
self._server.newConnection.connect(self._on_new_connection)
def start(self) -> bool:
if not self._server.listen(QHostAddress.SpecialAddress.Any, self._port):
print(f"Error: {self._server.errorString()}")
return False
print(f"Server listening on port {self._port}")
return True
def stop(self):
for socket in self._clients:
socket.disconnectFromHost()
self._server.close()
def broadcast(self, message: str, exclude: QTcpSocket = None):
data = (message + "\n").encode()
for socket in self._clients:
if socket != exclude:
socket.write(data)
@Slot()
def _on_new_connection(self):
while (socket := self._server.nextPendingConnection()):
addr = f"{socket.peerAddress().toString()}:{socket.peerPort()}"
self._clients[socket] = addr
socket.readyRead.connect(lambda s=socket: self._on_ready_read(s))
socket.disconnected.connect(lambda s=socket: self._on_disconnected(s))
socket.write(b"Welcome!\n")
self.client_connected.emit(addr)
print(f"Client connected: {addr}")
def _on_ready_read(self, socket: QTcpSocket):
while socket.canReadLine():
line = socket.readLine().data().decode().strip()
if line:
addr = self._clients.get(socket, "unknown")
self.message_received.emit(addr, line)
self.broadcast(f"[{addr}]: {line}", exclude=socket)
def _on_disconnected(self, socket: QTcpSocket):
addr = self._clients.pop(socket, "unknown")
self.client_disconnected.emit(addr)
socket.deleteLater()
print(f"Client disconnected: {addr}")
if __name__ == "__main__":
from PySide6.QtCore import QCoreApplication
app = QCoreApplication(sys.argv)
server = ChatServer(9999)
if server.start():
sys.exit(app.exec())
5.3 UDP 通信 / UDP Communication
// udpsocket 示例
#include <QUdpSocket>
class UdpHandler : public QObject {
Q_OBJECT
public:
explicit UdpHandler(QObject *parent = nullptr)
: QObject(parent), m_socket(new QUdpSocket(this))
{
connect(m_socket, &QUdpSocket::readyRead,
this, &UdpHandler::onReadyRead);
}
bool bind(quint16 port) {
return m_socket->bind(QHostAddress::Any, port);
}
void send(const QHostAddress &addr, quint16 port, const QByteArray &data) {
m_socket->writeDatagram(data, addr, port);
}
// 广播
void broadcast(quint16 port, const QByteArray &data) {
m_socket->writeDatagram(data, QHostAddress::Broadcast, port);
}
private slots:
void onReadyRead() {
while (m_socket->hasPendingDatagrams()) {
QByteArray data;
data.resize(m_socket->pendingDatagramSize());
QHostAddress sender;
quint16 port;
m_socket->readDatagram(data.data(), data.size(), &sender, &port);
qDebug() << "UDP from" << sender.toString() << ":" << port
<< "data:" << data;
}
}
private:
QUdpSocket *m_socket;
};
5.4 HTTP 客户端 / HTTP Client
QNetworkAccessManager 示例
// httpclient.h
#ifndef HTTPCLIENT_H
#define HTTPCLIENT_H
#include <QObject>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
class HttpClient : public QObject {
Q_OBJECT
public:
explicit HttpClient(QObject *parent = nullptr)
: QObject(parent), m_manager(new QNetworkAccessManager(this)) {}
// GET 请求
void get(const QUrl &url) {
QNetworkRequest request(url);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QNetworkReply *reply = m_manager->get(request);
connect(reply, &QNetworkReply::finished, this, [this, reply]() {
reply->deleteLater();
if (reply->error() == QNetworkReply::NoError) {
QByteArray data = reply->readAll();
emit getFinished(QJsonDocument::fromJson(data));
} else {
emit errorOccurred(reply->errorString());
}
});
}
// POST 请求 (JSON)
void post(const QUrl &url, const QJsonObject &json) {
QNetworkRequest request(url);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QJsonDocument doc(json);
QNetworkReply *reply = m_manager->post(request, doc.toJson());
connect(reply, &QNetworkReply::finished, this, [this, reply]() {
reply->deleteLater();
if (reply->error() == QNetworkReply::NoError) {
emit postFinished(QJsonDocument::fromJson(reply->readAll()));
} else {
emit errorOccurred(reply->errorString());
}
});
}
// PUT 请求
void put(const QUrl &url, const QJsonObject &json) {
QNetworkRequest request(url);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QJsonDocument doc(json);
QNetworkReply *reply = m_manager->put(request, doc.toJson());
connect(reply, &QNetworkReply::finished, this, [this, reply]() {
reply->deleteLater();
if (reply->error() == QNetworkReply::NoError) {
emit putFinished(QJsonDocument::fromJson(reply->readAll()));
} else {
emit errorOccurred(reply->errorString());
}
});
}
// DELETE 请求
void deleteResource(const QUrl &url) {
QNetworkRequest request(url);
QNetworkReply *reply = m_manager->deleteResource(request);
connect(reply, &QNetworkReply::finished, this, [this, reply]() {
reply->deleteLater();
if (reply->error() == QNetworkReply::NoError) {
emit deleteFinished(true);
} else {
emit errorOccurred(reply->errorString());
}
});
}
signals:
void getFinished(const QJsonDocument &response);
void postFinished(const QJsonDocument &response);
void putFinished(const QJsonDocument &response);
void deleteFinished(bool success);
void errorOccurred(const QString &error);
private:
QNetworkAccessManager *m_manager;
};
Python HTTP 客户端
#!/usr/bin/env python3
"""HTTP REST 客户端 - PySide6"""
import json
import sys
from PySide6.QtCore import QObject, Signal, Slot, QUrl
from PySide6.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply
class RestClient(QObject):
finished = Signal(int, dict) # status_code, body
error = Signal(str)
def __init__(self, base_url: str = "", parent=None):
super().__init__(parent)
self._manager = QNetworkAccessManager()
self._base_url = base_url.rstrip("/")
def get(self, path: str):
request = QNetworkRequest(QUrl(f"{self._base_url}{path}"))
request.setHeader(
QNetworkRequest.KnownHeaders.ContentTypeHeader, "application/json"
)
reply = self._manager.get(request)
reply.finished.connect(lambda: self._handle_reply(reply))
def post(self, path: str, data: dict):
request = QNetworkRequest(QUrl(f"{self._base_url}{path}"))
request.setHeader(
QNetworkRequest.KnownHeaders.ContentTypeHeader, "application/json"
)
body = json.dumps(data).encode()
reply = self._manager.post(request, body)
reply.finished.connect(lambda: self._handle_reply(reply))
def _handle_reply(self, reply: QNetworkReply):
reply.deleteLater()
status = reply.attribute(
QNetworkRequest.Attribute.HttpStatusCodeAttribute
)
if reply.error() == QNetworkReply.NetworkError.NoError:
body = json.loads(reply.readAll().data())
self.finished.emit(status or 200, body)
else:
self.error.emit(reply.errorString())
# 使用示例
def main():
from PySide6.QtCore import QCoreApplication, QTimer
app = QCoreApplication(sys.argv)
client = RestClient("https://jsonplaceholder.typicode.com")
client.finished.connect(
lambda code, body: print(f"Status: {code}, Data: {json.dumps(body, indent=2)[:200]}")
)
client.error.connect(lambda err: print(f"Error: {err}"))
QTimer.singleShot(0, lambda: client.get("/posts/1"))
QTimer.singleShot(2000, app.quit)
app.exec()
5.5 WebSocket / WebSocket
// websocketclient.h
#include <QWebSocket>
class WebSocketClient : public QObject {
Q_OBJECT
public:
explicit WebSocketClient(QObject *parent = nullptr)
: QObject(parent), m_socket(new QWebSocket)
{
connect(m_socket, &QWebSocket::connected, this, [this]() {
qDebug() << "WebSocket connected";
emit connected();
});
connect(m_socket, &QWebSocket::textMessageReceived,
this, [this](const QString &msg) {
emit messageReceived(msg);
});
connect(m_socket, &QWebSocket::disconnected, this, &WebSocketClient::disconnected);
connect(m_socket, &QWebSocket::errorOccurred, this, [this]() {
emit errorOccurred(m_socket->errorString());
});
}
void connectToServer(const QUrl &url) {
m_socket->open(url);
}
void send(const QString &message) {
if (m_socket->state() == QAbstractSocket::ConnectedState) {
m_socket->sendTextMessage(message);
}
}
void close() { m_socket->close(); }
signals:
void connected();
void disconnected();
void messageReceived(const QString &message);
void errorOccurred(const QString &error);
private:
QWebSocket *m_socket;
};
5.6 JSON 处理 / JSON Handling
// JSON 操作速查 / JSON Quick Reference
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
void jsonExamples()
{
// 1. 创建 JSON 对象
QJsonObject person;
person["name"] = "张三";
person["age"] = 30;
person["active"] = true;
QJsonArray hobbies;
hobbies << "读书" << "编程" << "跑步";
person["hobbies"] = hobbies;
// 嵌套对象
QJsonObject address;
address["city"] = "北京";
address["zip"] = "100000";
person["address"] = address;
// 序列化
QJsonDocument doc(person);
QByteArray json = doc.toJson(QJsonDocument::Indented);
qDebug().noquote() << json;
// 2. 解析 JSON
QJsonDocument parsed = QJsonDocument::fromJson(json);
QJsonObject root = parsed.object();
QString name = root["name"].toString();
int age = root["age"].toInt();
QJsonArray h = root["hobbies"].toArray();
for (const auto &hobby : h) {
qDebug() << "Hobby:" << hobby.toString();
}
// 3. 深层访问
QString city = root["address"].toObject()["city"].toString();
qDebug() << "City:" << city;
}
Python JSON
"""Python JSON 处理"""
import json
from PySide6.QtCore import QJsonDocument, QJsonObject, QJsonArray
# 方式1: 直接用 Python json 模块 (推荐)
data = {
"name": "张三",
"age": 30,
"hobbies": ["读书", "编程", "跑步"],
"address": {"city": "北京", "zip": "100000"}
}
json_str = json.dumps(data, ensure_ascii=False, indent=2)
parsed = json.loads(json_str)
# 方式2: 使用 Qt JSON API
doc = QJsonDocument.fromJson(json_str.encode())
obj = doc.object()
name = obj["name"].toString()
注意事项 / Important Notes
⚠️ 异步编程 / Asynchronous Programming
Qt 网络操作都是异步的,不要在信号槽回调之外阻塞等待。 所有结果通过信号返回。
Qt network operations are asynchronous. Don’t block-wait outside callbacks. All results come via signals.
⚠️ SSL 配置 / SSL Configuration
HTTPS 请求需要 SSL 库支持。确保 OpenSSL 已安装。 某些平台需要手动部署 SSL 库。
HTTPS requires SSL library support. Ensure OpenSSL is installed.
⚠️ 错误处理 / Error Handling
始终检查
QNetworkReply::error()。网络请求失败是常态。 Always checkQNetworkReply::error(). Network failures are normal.
扩展阅读 / Further Reading
| 资源 / Resource | 链接 / Link |
|---|---|
| Qt Network 文档 | https://doc.qt.io/qt-6/qtnetwork-index.html |
| QNetworkAccessManager | https://doc.qt.io/qt-6/qnetworkaccessmanager.html |
| JSON 文档 | https://doc.qt.io/qt-6/json.html |
| WebSocket 示例 | https://doc.qt.io/qt-6/qtnetwork-websockets-example.html |