欢迎光临
我们一直在努力

Qt 网络进阶|TCP 通信 + 粘包彻底解决 + WebSocket 实时通信

一、模块引入

pro

QT += network

cmake

find_package(Qt6 REQUIRED COMPONENTS Network)
target_link_libraries(App PRIVATE Qt6::Network)


第一部分:TCP 通信 + 粘包问题解决

1. TCP 基础说明

  • TCP 是面向字节流协议,无数据包边界;
  • 发送端多次小数据,内核会合并发送 → 粘包;
  • 接收端一次读取可能包含多条数据。

粘包本质:数据之间没有边界,接收方无法区分一条完整消息。

2. 通用解决方案(工业常用 2 种)

  • 包头 + 包体:固定 4 字节表示数据长度,先读长度,再读对应字节数(推荐)
  • 特殊分隔符:末尾加 \\r\\n、### 等标记(文本协议适用)
  • 下文使用 「长度头」 方案实现完整 TCP 客户端 / 服务端,并彻底解决粘包。


    3. TCP 服务端(QTcpServer + QTcpSocket)

    cpp

    运行

    #include <QApplication>
    #include <QMainWindow>
    #include <QTcpServer>
    #include <QTcpSocket>
    #include <QVBoxLayout>
    #include <QTextEdit>
    #include <QByteArray>

    class TcpServer : public QMainWindow
    {
    Q_OBJECT
    public:
    TcpServer()
    {
    resize(500,300);
    m_server = new QTcpServer(this);
    m_recvEdit = new QTextEdit;
    setCentralWidget(m_recvEdit);

    // 监听本机 8888 端口
    if(m_server->listen(QHostAddress::Any, 8888))
    m_recvEdit->append("服务端启动成功,端口:8888");
    else
    m_recvEdit->append("服务端启动失败");

    connect(m_server, &QTcpServer::newConnection, this, &TcpServer::onNewConn);
    }

    private slots:
    void onNewConn()
    {
    m_socket = m_server->nextPendingConnection();
    m_recvEdit->append("客户端已连接");

    connect(m_socket, &QTcpSocket::readyRead, this, &TcpServer::onReadData);
    connect(m_socket, &QTcpSocket::disconnected, m_socket, &QTcpSocket::deleteLater);
    }

    // 核心:拆包、解决粘包
    void onReadData()
    {
    m_buffer += m_socket->readAll();

    // 至少4字节(长度头) + 一条数据
    while(m_buffer.size() >= 4)
    {
    // 读取前4字节 = 数据长度
    quint32 len = *(quint32*)m_buffer.data();
    if(m_buffer.size() < 4 + len)
    break; // 数据不完整,等待下一次接收

    // 截取完整数据包
    QByteArray data = m_buffer.mid(4, len);
    m_recvEdit->append("收到: " + QString(data));

    // 移除已处理数据
    m_buffer = m_buffer.mid(4 + len);
    }
    }

    private:
    QTcpServer *m_server;
    QTcpSocket *m_socket = nullptr;
    QTextEdit *m_recvEdit;
    QByteArray m_buffer; // 接收缓冲区
    };

    int main(int argc, char *argv[])
    {
    QApplication a(argc, argv);
    TcpServer w;
    w.show();
    return a.exec();
    }
    #include "main.moc"

    4. TCP 客户端(组包:添加长度头)

    cpp

    运行

    #include <QApplication>
    #include <QMainWindow>
    #include <QTcpSocket>
    #include <QPushButton>
    #include <QLineEdit>
    #include <QVBoxLayout>
    #include <QByteArray>

    class TcpClient : public QMainWindow
    {
    Q_OBJECT
    public:
    TcpClient()
    {
    resize(400,200);
    m_socket = new QTcpSocket(this);
    m_edit = new QLineEdit;
    QPushButton *btnSend = new QPushButton("发送");

    QVBoxLayout *lay = new QVBoxLayout;
    lay->addWidget(m_edit);
    lay->addWidget(btnSend);
    QWidget *cen = new QWidget;
    cen->setLayout(lay);
    setCentralWidget(cen);

    // 连接服务端
    m_socket->connectToHost("127.0.0.1", 8888);

    connect(btnSend, &QPushButton::clicked, this, &TcpClient::sendMsg);
    }

    private slots:
    void sendMsg()
    {
    QString text = m_edit->text();
    QByteArray data = text.toUtf8();

    // 组包:前4字节长度 + 原始数据
    quint32 len = data.size();
    QByteArray pkg;
    pkg.append((char*)&len, 4);
    pkg.append(data);

    m_socket->write(pkg);
    m_edit->clear();
    }

    private:
    QTcpSocket *m_socket;
    QLineEdit *m_edit;
    };

    int main(int argc, char *argv[])
    {
    QApplication a(argc, argv);
    TcpClient w;
    w.show();
    return a.exec();
    }
    #include "main.moc"

    5. 粘包方案总结

    • 长度头方案:二进制通信、协议交互首选,稳定性最强;
    • 分隔符方案:HTTP、串口、文本指令场景使用;
    • 必须设置接收缓冲区,循环拆包,保证每条消息完整解析。

    第二部分:WebSocket 实时双向通信

    WebSocket 基于 HTTP 握手,实现长连接、全双工实时通信,适合聊天室、物联网推送、网页 + Qt 交互。

    1. 模块与类

    • 模块:network
    • 服务端:QWebSocketServer
    • 客户端:QWebSocket

    2. WebSocket 服务端

    cpp

    运行

    #include <QApplication>
    #include <QMainWindow>
    #include <QWebSocketServer>
    #include <QWebSocket>
    #include <QTextEdit>
    #include <QVBoxLayout>

    class WsServer : public QMainWindow
    {
    Q_OBJECT
    public:
    WsServer()
    {
    resize(500,300);
    m_wsServer = new QWebSocketServer("WsServer", QWebSocketServer::NonSecureMode, this);
    m_edit = new QTextEdit;
    setCentralWidget(m_edit);

    if(m_wsServer->listen(QHostAddress::Any, 9999))
    m_edit->append("WebSocket 服务端启动,端口9999");

    connect(m_wsServer, &QWebSocketServer::newConnection, this, &WsServer::onNewConn);
    }

    private slots:
    void onNewConn()
    {
    QWebSocket *socket = m_wsServer->nextPendingConnection();
    m_edit->append("客户端接入");

    connect(socket, &QWebSocket::textMessageReceived, this, [=](const QString &msg){
    m_edit->append("收到消息: " + msg);
    // 回显
    socket->sendTextMessage("服务端已收到:" + msg);
    });
    connect(socket, &QWebSocket::disconnected, socket, &QWebSocket::deleteLater);
    }

    private:
    QWebSocketServer *m_wsServer;
    QTextEdit *m_edit;
    };

    int main(int argc, char *argv[])
    {
    QApplication a(argc, argv);
    WsServer w;
    w.show();
    return a.exec();
    }
    #include "main.moc"

    3. WebSocket 客户端

    cpp

    运行

    #include <QApplication>
    #include <QMainWindow>
    #include <QWebSocket>
    #include <QLineEdit>
    #include <QPushButton>
    #include <QTextEdit>
    #include <QVBoxLayout>

    class WsClient : public QMainWindow
    {
    Q_OBJECT
    public:
    WsClient()
    {
    resize(400,300);
    m_ws = new QWebSocket;
    m_recvEdit = new QTextEdit;
    m_sendEdit = new QLineEdit;
    QPushButton *btnSend = new QPushButton("发送");

    QVBoxLayout *lay = new QVBoxLayout;
    lay->addWidget(m_recvEdit);
    lay->addWidget(m_sendEdit);
    lay->addWidget(btnSend);
    QWidget *cen = new QWidget;
    cen->setLayout(lay);
    setCentralWidget(cen);

    // 连接服务端
    m_ws->open(QUrl("ws://127.0.0.1:9999"));

    connect(m_ws, &QWebSocket::textMessageReceived, this, [=](const QString &msg){
    m_recvEdit->append("服务端: " + msg);
    });
    connect(btnSend, &QPushButton::clicked, this, &WsClient::sendMsg);
    }

    private slots:
    void sendMsg()
    {
    QString text = m_sendEdit->text();
    m_ws->sendTextMessage(text);
    m_sendEdit->clear();
    }

    private:
    QWebSocket *m_ws;
    QTextEdit *m_recvEdit;
    QLineEdit *m_sendEdit;
    };

    int main(int argc, char *argv[])
    {
    QApplication a(argc, argv);
    WsClient w;
    w.show();
    return a.exec();
    }
    #include "main.moc"


    小结

  • TCP:面向字节流,重点解决粘包,长度头是工业标准方案,适合二进制私有协议;
  • WebSocket:长连接实时通信,兼容网页端,适合推送、聊天、物联网;
  • 两类接口均为异步信号驱动,禁止使用阻塞式 waitForXXX,避免 UI 卡顿。
  • 赞(0)
    未经允许不得转载:171主机测评 » Qt 网络进阶|TCP 通信 + 粘包彻底解决 + WebSocket 实时通信
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址