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

多工 I/O 模型 (select) 實作高併發 TCP 伺服器架構設計

     編輯:關於VC++

多工 I/O (Multiplexed I/O) 是 Unix/Linux 高併發網路伺服器的經典模型,核心機制為 select() 系統呼叫搭配 FD_SET/FD_CLR/FD_ISSET 巨集操作檔案描述符集合。單進程可支援的客戶端上限由 FD_SETSIZE 決定 (Solaris 10/Linux 預設 1024,Windows 2000 僅 64)。本文展示 C++ 封裝的 Reactor 模式實作。

架構類別設計

┌─────────────┐       ┌─────────────┐       ┌─────────────┐
│  tcp_listen │◄─────►│  tcp_comm   │◄─────►│  sock_list  │
│  (監聽)     │       │  (通訊)     │       │  (連線管理) │
└─────────────┘       └─────────────┘       └─────────────┘
                            ▲
                            │
                     ┌──────┴──────┐
                     │    recv     │
                     │ (Select 迴圈)│
                     └─────────────┘

連線管理器 sock_list


class sock_list {
public:
    void insert(tcp_comm* t);           // 新連線加入
    void remove(tcp_comm* t);           // 連線移除
    void get_all(std::vector& vec); // 取得所有連線指標
private:
    std::list _sock_list;
};

接收引擎 recv


class recv {
public:
    bool start();                       // 啟動服務
private:
    int do_select();                    // 核心 Select 迴圈
    void init_readfds(const std::vector& vec, fd_set* set);
private:
    sock_list* _list;
    tcp_listen* _listen;                // 監聽 Socket
};

核心 Select 迴圈實作


int recv::do_select() {
    fd_set readfds;
    int listen_fd = _listen->get_handle();
    
    while (true) {
        std::vector vec;
        _list->get_all(vec);                    // 取得所有通訊 Socket
        init_readfds(vec, &readfds);            // 建構 fd_set (含 listen_fd)
        
        // select() 監控讀就緒,最後參數 NULL 表無限等待
        int ret = select(0, &readfds, NULL, NULL, NULL);
        if (ret <= 0) continue;                 // 錯誤或中斷
        
        // 1. 新連線到達
        if (FD_ISSET(listen_fd, &readfds)) {
            tcp_comm* client = new tcp_comm;
            _listen->accept(*client);           // 接受連線
            _list->insert(client);              // 納入管理
        }
        
        // 2. 現有連線有資料可讀
        for (auto it = vec.begin(); it != vec.end(); ++it) {
            if (FD_ISSET((*it)->get_handle(), &readfds)) {
                // 呼叫 (*it)->recv() 處理資料...
            }
        }
    }
    return 0;
}

初始化 fd_set 輔助函數


void recv::init_readfds(const std::vector& vec, fd_set* set) {
    FD_ZERO(set);
    FD_SET(_listen->get_handle(), set);         // 監聽 Socket
    for (auto p : vec) {
        FD_SET(p->get_handle(), set);           // 所有通訊 Socket
    }
}

擴展性策略:多執行緒 + Select 協作

單進程受限於 FD_SETSIZE,可採用 多執行緒各跑一個 Select 迴圈:

  • Solaris 10:100 執行緒 × 1024 = 10 萬並發連線
  • 前端負載均衡器 (Layer 4/7) 分發新連線至不同執行緒
  • 挑戰:執行緒同步開銷、CPU 緩存失效、鎖競爭

結論與替代方案

  • Select 模型優點:單執行緒事件驅動,邏輯清晰,無鎖競爭,易除錯維護
  • 缺點:FD_SETSIZE 硬性限制、線性掃描 fd_set 效能隨連線數遞減 (O(n))
  • 現代替代:Linux epoll、FreeBSD/macOS kqueue、Windows IOCP — 事件通知機制,O(1) 複雜度,支援百萬級並發

本文程式碼展示了 Reactor 模式的經典實作,適合中小型併發場景或教學用途;生產環境高併發建議遷移至平台原生高效能 I/O 多工機制。

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