输入关键词开始搜索

Qt 网络编程

三种网络 API 选择

协议层级场景
QTcpSocketTCP传输层自定义协议、长连接
QUdpSocketUDP传输层实时音视频、广播
QNetworkAccessManagerHTTP/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 必须在它使用的线程中创建