首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

Muduo網絡編程示例之二:TCP 分包與聊天服務器實作

     編輯:關於C語言

TCP 分包:從「偽問題」到核心需求

TCP 是位元組流協議,並不保証應用層訊息的邊界。所謂「黏包問題」其實是偽命題,真正的課題是如何在位元組流中識別並還原出完整的訊息(分包)。對於短連線,連線關閉即為訊息結束;但長連線必須採用顯式分包機制,常見四種做法:

  1. 固定長度訊息(如 Muduo roundtrip 示例的 16 位元組)
  2. 特殊分隔符(如 HTTP Header 的 \r\n\r\n)
  3. 長度前綴(最常見,本文採用)
  4. 自描述格式(XML/JSON 配對解析,需狀態機)

聊天服務設計

本示例實作一個簡單的廣播式聊天室:伺服端監聽埠,多客戶端連接;任一客戶端發送訊息,伺服端廣播給所有連接(含發送者)。協議採用「4 位元組網路序長度 + 字串內容」的二進制格式,例如 hello (5 字節) 與 chenshuo (8 字節) 打包後為:

0x00,0x00,0x00,0x05, h,e,l,l,o, 0x00,0x00,0x00,0x08, c,h,e,n,s,h,u,o

打包與分包核心代碼

打包發送

void send(TcpConnection* conn, const string& message)
{
    Buffer buf;
    buf.append(message.data(), message.size());
    int32_t len = sockets::hostToNetwork32(static_cast<int32_t>(message.size()));
    buf.prepend(&len, sizeof len); // 利用 Buffer 預留頭部空間,零拷貝
    conn->send(&buf);
}

Buffer::prepend() 利用內部預留的 8 位元組頭部空間,避免移動已有資料,效能極佳。

分包接收

void onMessage(const TcpConnectionPtr& conn, Buffer* buf, Timestamp receiveTime)
{
    while (buf->readableBytes() >= kHeaderLen) // 必須用 while
    {
        const void* data = buf->peek();
        int32_t tmp = *static_cast<const int32_t*>(data);
        int32_t len = sockets::networkToHost32(tmp);
        if (len > 65536 || len < 0) { /* 錯誤長度,關閉連線 */ }
        else if (buf->readableBytes() >= len + kHeaderLen)
        {
            buf->retrieve(kHeaderLen);
            string message(buf->peek(), len);
            buf->retrieve(len);
            messageCallback_(conn, message, receiveTime); // 通知上層
        }
        else break; // 資料不完整,等待下次
    }
}

關鍵點:必須使用 while 迴圈,因一次 onMessage 可能包含多條完整訊息;若改用 if,訊息會堆積導致處理延遲甚至錯誤。

編解碼器:LengthHeaderCodec

Muduo 的 TcpConnection 僅提供「資料到達」回呼,若想讓業務邏輯只關心「訊息到達」,可封裝一層 LengthHeaderCodec,內部處理分包,對外暴露 StringMessageCallback。這是典型的「中介層」模式,解耦網絡 IO 與應用邏輯。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved