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

基於 libuv 的高性能 TCP 封裝:支援萬級併發的 Server/Client 完整實現

     編輯:C++ 入門

技術選型:為什麼選 libuv?

庫評價
Boost.Asio / ACE功能強但過於龐大,編譯依賴重
libevent成熟穩定,但 API 風格較 C 風格
libev輕量快速,但功能相對簡單
libuvNode.js 底層引擎,跨平台、異步 IO 完備、活躍維護

架構設計

  • TCPServer:基於 uv_tcp_t + uv_loop_t,管理客戶端映射 map<int, clientdata*>
  • TCPClient:獨立連接,支援連接超時檢測、發送鎖
  • 回調驅動:newconnect、server_recvcb、client_recvcb
  • 日誌整合:log4z 異步日誌
  • 內存管理:RAII 風格,uv_close 回調中釋放 clientdata

核心類介面

TCPServer

class TCPServer {
public:
    TCPServer(uv_loop_t* loop = uv_default_loop());
    ~TCPServer();

    bool Start(const char* ip, int port);   // IPv4
    bool Start6(const char* ip, int port);  // IPv6
    void close();

    int send(int clientid, const char* data, size_t len);
    void setnewconnectcb(newconnect cb);
    void setrecvcb(int clientid, server_recvcb cb);

    const char* GetLastErrMsg() const;
private:
    uv_tcp_t server_;
    map clients_list_;
    uv_mutex_t mutex_handle_;
    uv_loop_t* loop_;
    newconnect newconcb_;
    bool isinit_;
    // 靜態回調:acceptConnection, AfterServerRecv, AfterSend, onAllocBuffer...
};

TCPClient

class TCPClient {
public:
    TCPClient(uv_loop_t* loop = uv_default_loop());
    ~TCPClient();

    bool connect(const char* ip, int port);   // 阻塞直到連接完成
    bool connect6(const char* ip, int port);
    int send(const char* data, size_t len);
    void setrecvcb(client_recvcb cb, void* userdata);
    void close();

    bool setNoDelay(bool enable);
    bool setKeepAlive(int enable, unsigned int delay);

private:
    uv_tcp_t client_;
    uv_loop_t* loop_;
    uv_thread_t connect_threadhanlde_;  // 連接線程
    uv_mutex_t write_mutex_handle_;     // 發送串行化
    enum { CONNECT_TIMEOUT, CONNECT_FINISH, CONNECT_ERROR, CONNECT_DIS };
    int connectstatus_;
    client_recvcb recvcb_;
    void* userdata_;
    // 靜態回調:AfterConnect, AfterClientRecv, AfterSend, onAllocBuffer...
};

關鍵實現細節

1. 服務端接受新連接

void TCPServer::acceptConnection(uv_stream_t* server, int status) {
    TCPServer* tcpsock = (TCPServer*)server->data;
    int clientid = tcpsock->GetAvailaClientID();  // 原子遞增
    clientdata* cdata = new clientdata(clientid);
    cdata->tcp_server = tcpsock;

    uv_tcp_init(tcpsock->loop_, cdata->client_handle);
    uv_accept((uv_stream_t*)&tcpsock->server_, (uv_stream_t*)cdata->client_handle);

    tcpsock->clients_list_[clientid] = cdata;
    if (tcpsock->newconcb_) tcpsock->newconcb_(clientid);

    uv_read_start((uv_stream_t*)cdata->client_handle, onAllocBuffer, AfterServerRecv);
}

2. 內存緩衝區管理

  • onAllocBuffer 直接返回預分配的 readbuffer/writebuffer (1MB)
  • 發送時 realloc 擴容,**生命週期由發送者控制**,直到 AfterSend 回調
  • Client 發送用 write_mutex_handle_ 串行化,避免併發寫亂序

3. 客戶端異步連接模式

bool TCPClient::connect(const char* ip, int port) {
    close(); init();
    connectip_ = ip; connectport_ = port;
    uv_thread_create(&connect_threadhanlde_, ConnectThread, this);
    while (connectstatus_ == CONNECT_DIS) Sleep(100);  // 等待連接完成
    return connectstatus_ == CONNECT_FINISH;
}

void TCPClient::ConnectThread(void* arg) {
    TCPClient* pclient = (TCPClient*)arg;
    sockaddr_in bind_addr;
    uv_ip4_addr(pclient->connectip_.c_str(), pclient->connectport_, &bind_addr);
    uv_tcp_connect(&pclient->connect_req_, &pclient->client_, (sockaddr*)&bind_addr, AfterConnect);
    pclient->run();  // 在連接線程運行事件循環
}

**設計亮點**:連接過程在獨立線程,主線程可做其他事;AfterConnect 回調中啟動 uv_read_start 並設置狀態。

4. 優雅關閉與資源釋放

  • Server:遍歷客戶端 uv_close(AfterClientClose),回調中 delete clientdata
  • Client:uv_close(AfterClose),析構函數釋放緩衝區
  • 互斥鎖 uv_mutex_destroy 在 close() 中調用

已知問題與優化方向

作者提到:「客戶端並發 20 多路超過 uv_write 會 assert 出錯,未找到原因;萬級併發未測試。」

可能原因分析

  1. 發送鎖競爭:單 Client 實例共用一個 write_req_ 和 write_mutex,並發調用 send() 會阻塞或衝突
  2. 寫緩衝區共享:writebuffer_ 為成員變量,併發 send 會覆蓋數據
  3. uv_write 請求結構復用:同一 uv_write_t 未完成前不可重複提交

建議改進

  • Client send 內部分配獨立 uv_write_t + 緩衝區(或使用寫隊列)
  • Server 端已為每客戶端分配獨立 write_req/writebuffer,設計較合理
  • 參考 libuv 設計文檔 關於寫請求的最佳實踐

項目地址

GitHub: https://github.com/wqvbjhc/libuv_tcp

總結

這是一個工程化程度較高的 libuv TCP 封裝,展示了異步網絡編程的核心模式:事件循環、回調驅動、資源生命週期管理、線程安全。適合作為高性能網絡服務的基礎框架,解決 Client 併發寫問題後可支撐更大規模併發。

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