fdset 集合:(就是說)
- fd_set是一個位圖(bitmap)結構
- 每個位代表一個文件描述符
- 0表示不在集合中,1表示在集合中
fd_set結構(簡化):
[0][1][2][3][4][5]...[1023]
?0 ?0 ?1 ?0 ?1 ?0 ? ? 0
? ?↑ ? ? ↑
? ?| ? ? |
sockfd ?clientfd
// select方式
fd_set rfds;
select(maxfd+1, &rfds, NULL, NULL, NULL);// epoll方式
struct epoll_event events[1024];
epoll_wait(epfd, events, 1024, -1);
?maxfd就是1023,rfds就是read fds 讀事件,
select的返回值是就緒事件操作如下:
int ret = select(nfds, &readfds, &writefds, &exceptfds, &timeout);
if (ret > 0) {// 有事件發生printf("有 %d 個文件描述符就緒\n", ret);// 檢查讀事件if (FD_ISSET(sockfd, &readfds)) {// 處理讀事件}// 檢查寫事件if (FD_ISSET(sockfd, &writefds)) {// 處理寫事件}// 檢查異常事件if (FD_ISSET(sockfd, &exceptfds)) {// 處理異常事件}
}
?fd操作如下:
// 清空集合
FD_ZERO(&rfds);
// 結果:[0][0][0][0][0]...[0]// 添加fd到集合
FD_SET(sockfd, &rfds);
// 例如sockfd=2:[0][0][1][0][0]...[0]// 從集合中刪除fd
FD_CLR(sockfd, &rfds);
// 例如sockfd=2:[0][0][0][0][0]...[0]// 檢查fd是否在集合中
FD_ISSET(sockfd, &rfds);
// 返回1表示在集合中,0表示不在
a) 一個連接一個線程:
優點:
- 實現簡單
- 邏輯清晰
- 適合并發量小的場景
缺點:
- 線程資源消耗大
- 線程切換開銷大
- 不適合高并發
select
優點:
- 單線程處理多連接
- 資源消耗小
- 適合中等并發
缺點:
- 效率較低
- 連接數有限
- 需要輪詢
epoll
優點:
- 高效的事件通知
- 適合高并發
- 資源消耗小
缺點:
- 實現較復雜
- 需要系統支持
// 多線程方式
void *client_thread(void *arg) {int clientfd = *(int *)arg;while (1) {recv(clientfd, buffer, 1024, 0);// 處理數據}
}// epoll方式
while (1) {epoll_wait(epfd, events, 1024, -1);for (i = 0; i < nready; i++) {if (events[i].data.fd == sockfd) {// 處理新連接} else {// 處理數據}}
}
// 方式1:一個連接一個線程
while (1) {int clientfd = accept(sockfd, ...);pthread_t thid;pthread_create(&thid, NULL, client_thread, &clientfd);
}// 方式2:單線程處理多個連接(select)
while (1) {select(maxfd+1, &rset, NULL, NULL, NULL);// 處理多個連接
}// 方式3:單線程處理多個連接(epoll)
while (1) {epoll_wait(epfd, events, 1024, -1);// 處理多個連接
}