多工 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
};
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;
}
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
}
}
單進程受限於 FD_SETSIZE,可採用 多執行緒各跑一個 Select 迴圈:
FD_SETSIZE 硬性限制、線性掃描 fd_set 效能隨連線數遞減 (O(n))epoll、FreeBSD/macOS kqueue、Windows IOCP — 事件通知機制,O(1) 複雜度,支援百萬級並發本文程式碼展示了 Reactor 模式的經典實作,適合中小型併發場景或教學用途;生產環境高併發建議遷移至平台原生高效能 I/O 多工機制。