摘要
在linux環境下多路複用模型一共有三種,分別是select、poll、epoll。本文主要記錄select模型實現網絡socket服務器多路併發的相關要點和知識。在寫這篇文章之前,已經使用多進程和多線程實現服務器多路併發。多進程和多線程多線程對於內核的負擔和內存的開銷都是巨大的,原因是內核都會爲新來的客戶端創建新進程和新線程,會導致內核的工作效率降低。select的最大優勢在於它可以在一個線程中同時處理多個socket的IO請求。
select工作機制
-
Linux下select調用的過程:
用戶層應用程序調用select(),底層調用poll();
核心層調用sys_select() ------> do_select(); -
select首先循環遍歷fd_set裏面的文件描述符對應的驅動程序的poll函數,每個設備驅動poll函數會將調用select的用戶進程插入到對應資源的等待隊列中,然後poll函數返回bitmask告訴當前哪些資源可用,當遍歷完fd_set文件描述集後如果有資源可讀或可寫,則返回可讀或可寫資源的文件描述個數,沒有就進入睡眠狀態,直到有資源利用時才喚醒。
-
select返回時,會把fd_set描述符集中沒有發生事件的描述符清空,所以一般寫程序時會定義一個數組暫存文件描述符,第二次select監測時再加入fd_set描述符集。
-
在Linux內核有個參數__FD_SETSIZE定義了每個FD_SET的句柄個數中,這也意味着select所用到的FD_SET是有限的,也正是這個原因select()默認只能同時處理1024個客戶端的連接請求:
c /linux/posix_types.h: #define __FD_SETSIZE 1024
實現select服務器併發的函數總結
1、一個重要函數
int select(int max_fd, fd_set *readset, fd_set *writeset, fd_set *exceptset, struct timeval *timeout);
參數解析:
- max_fd:指待測試的fd的總個數,值等於最大描述符+1(select監測的文件描述從0開始的)。
- readset:監測可讀,不監測設置爲NULL。
- writeset:監測可寫,不監測設置爲NULL。
- exceptset:監測異常,不監測設置爲NULL。
- timeout:設置select的超時時間,如果設置爲NULL則永不超時;
2、兩個結構體
(1)、struct fd_set:可以理解爲一個集合,這個集合中存放的是文件描述符。
(2)、struct timeval:時間值。
struct timeval
{
time_t tv_sec;//second
time_t tv_usec;//minisecond
};
3、四個宏定義
-
FD_CLR(inr fd,fd_set* set);用來清除set中相關fd 的位
-
FD_ISSET(int fd,fd_set *set);用來測試set中相關fd 的位是否爲真
-
FD_SET(int fd,fd_set *set);用來設置set中相關fd的位
-
FD_ZERO(fd_set *set);用來清除set的全部位
程序流程圖
實現代碼
這裏只貼出server_main.c,更多代碼:
select服務器端:點擊查看
客戶端:點擊查看
server_main.c
#include "server.h"
int main(int argc,char **argv)
{
int server_port;
int sockfd = -1;
int clienfd = -1;
int re_val = -1;
//參數解析
re_val = argument_parse(argc, argv, &server_port);
if(re_val < 0)
{
printf("參數解析錯誤!\n");
exit(0);
}
//套接字初始化
re_val = socket_init(server_port,&sockfd);
if(re_val < 0)
{
printf("套接字初始化錯誤!\n");
}
//select服務器開始工作
server_select_create(sockfd);
return 0;
}
測試
第一次測試:出現亂碼
解決方案:數據處理之前未清空數據緩衝區——memset(buf,0,sizeof(buf));
第二次測試:成功
clienfd[4]:代表第一個客戶端連接上來
clienfd[5]:代表第二個客戶端連接上來