三种网络 API 选择
| 类 | 协议 | 层级 | 场景 |
|---|
QTcpSocket | TCP | 传输层 | 自定义协议、长连接 |
QUdpSocket | UDP | 传输层 | 实时音视频、广播 |
QNetworkAccessManager | HTTP/HTTPS | 应用层 | REST API、下载 |
QTcpSocket — TCP 客户端
#include <QTcpSocket>
class TcpClient : public QObject {
QTcpSocket *m_socket;
public:
TcpClient() {
m_socket = new QTcpSocket(this);
connect(m_socket, &QTcpSocket::connected, []() {
qDebug() << "Connected";
});
connect(m_socket, &QTcpSocket::readyRead, [this]() {
QByteArray data = m_socket->readAll();
// 处理数据
});
connect(m_socket, &QTcpSocket::disconnected, []() {
qDebug() << "Disconnected";
});
// 错误处理(QOverload 解决重载歧义)
connect(m_socket,
QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error),
[](QAbstractSocket::SocketError err) {
qWarning() << "Error:" << err;
});
}
void connectToHost(const QString &host, quint16 port) {
m_socket->connectToHost(host, port);
}
void send(const QByteArray &data) {
m_socket->write(data);
}
};
QTcpServer — TCP 服务端
class TcpServer : public QObject {
QTcpServer *m_server;
QList<QTcpSocket *> m_clients;
public:
TcpServer() {
m_server = new QTcpServer(this);
connect(m_server, &QTcpServer::newConnection, [this]() {
while (QTcpSocket *client = m_server->nextPendingConnection()) {
m_clients.append(client);
connect(client, &QTcpSocket::readyRead, [this, client]() {
QByteArray data = client->readAll();
// 处理 + 回复
client->write("ACK");
});
connect(client, &QTcpSocket::disconnected, [this, client]() {
m_clients.removeOne(client);
client->deleteLater();
});
}
});
}
bool listen(quint16 port) {
return m_server->listen(QHostAddress::Any, port);
}
};
TCP 粘包/拆包处理
// TCP 是流式协议,需要自己定界
// 方案 1: 固定长度
// 方案 2: 分隔符(如 \r\n)
// 方案 3: 帧头 + 长度(PulseQt 的做法)
class FrameParser {
QByteArray m_buffer;
public:
void feed(const QByteArray &data) {
m_buffer.append(data);
while (m_buffer.size() >= 4) { // 帧头 4 字节
quint32 len = *reinterpret_cast<const quint32 *>(m_buffer.constData());
if (m_buffer.size() < 4 + len) break; // 不够一帧
QByteArray frame = m_buffer.mid(4, len);
m_buffer.remove(0, 4 + len);
emit frameReady(frame);
}
}
};
QUdpSocket — UDP
class UdpPeer : public QObject {
QUdpSocket *m_socket;
public:
UdpPeer() {
m_socket = new QUdpSocket(this);
connect(m_socket, &QUdpSocket::readyRead, [this]() {
while (m_socket->hasPendingDatagrams()) {
QByteArray datagram;
datagram.resize(m_socket->pendingDatagramSize());
QHostAddress sender;
quint16 senderPort;
m_socket->readDatagram(datagram.data(), datagram.size(),
&sender, &senderPort);
// 处理...
}
});
}
// 绑定端口接收
bool bind(quint16 port) {
return m_socket->bind(QHostAddress::Any, port);
}
// 发送
void send(const QByteArray &data, const QString &host, quint16 port) {
m_socket->writeDatagram(data, QHostAddress(host), port);
}
};
QNetworkAccessManager — HTTP
#include <QNetworkAccessManager>
#include <QNetworkReply>
class HttpClient : public QObject {
QNetworkAccessManager *m_mgr;
public:
HttpClient() { m_mgr = new QNetworkAccessManager(this); }
void get(const QString &url) {
QNetworkRequest request(QUrl(url));
QNetworkReply *reply = m_mgr->get(request);
connect(reply, &QNetworkReply::finished, [reply]() {
if (reply->error() == QNetworkReply::NoError) {
QByteArray data = reply->readAll();
// 处理 data
} else {
qWarning() << reply->errorString();
}
reply->deleteLater(); // ⚠️ 必须释放
});
}
void post(const QString &url, const QByteArray &body) {
QNetworkRequest request(QUrl(url));
request.setHeader(QNetworkRequest::ContentTypeHeader,
"application/json");
QNetworkReply *reply = m_mgr->post(request, body);
// ...
}
};
注意事项
// ⚠️ QTcpSocket 是异步的 — write() 不是立即发送
socket->write(data); // 数据进入发送缓冲区,不阻塞
// ⚠️ connected 信号不代表数据可以立即发送
// 连接建立成功就发射,TCP 握手完成后就能写
// ⚠️ QNetworkReply 必须 deleteLater()
// 在 finished 信号里调 reply->deleteLater()
// ⚠️ 不要在 GUI 线程做同步网络操作
// 用异步 API,或把同步操作放到 Worker 线程
// ⚠️ 跨线程使用 socket 时注意线程亲和性
// socket 必须在它使用的线程中创建