I O多路轉接之select

2021-09-23 10:21:06 字數 4936 閱讀 9685

什麼是多路轉接?什麼是select?

多路轉接就是一次等待多個檔案描述符;

簡單來說,select只做一件事,那就是等,等至少乙個檔案描述符的讀寫時間就緒。

具體來說,系統提供select來實現多路復用輸入/輸出模型。

select系統呼叫可以讓程式監聽多個檔案描述符的狀態變化。

程式會在select在這裡等待,直到被監視的檔案描述符至少有乙個發生了狀態改變。

select函式宣告

int select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds, struct timeval* timeout);
引數解釋:

fd_set的結構

fd_set就是乙個整形陣列,更嚴格的說,乙個「位圖」,點陣圖中的位置代表對應的檔案描述符,用0或1來控制。

但是,我們不能直接操作fd_set,而是要呼叫函式;

void fd_zero(fd_set& set);  //清楚set的全部位

void fd_set(int fd, fd_set& set) //把fd設定進set的相關位

void fd_clr(int fd, fd_set& set) //把fd在set的相關位清楚

int fd_isset(int fd, fd_set& set); //判斷fd是否被設定相關位

fd_set作為輸入輸出型引數:

timeval的結構:

struct timeval

;

返回值解釋:執行成功則返回已經改變狀態的檔案描述符的個數;

返回0則表示在描述符狀態改變前已超過timeout超時,沒有返回;

返回-1表示發生了錯誤,錯誤原因存於errno,此時引數readfds,writefds,exceptfds和timeout的值變成不可**;

錯誤值可能為:

socket就緒條件

sochet核心中,接收緩衝區中的位元組數,大於等於低水位標記so_recvlowat,此時可以無阻塞的讀該檔案描述符,並且返回值大於0

socket tcp通訊中,對端關閉連線,此時對該socket讀,則返回0

監聽的socket上有新的連線請求時

socket上有未處理的錯誤時

socket核心中,傳送緩衝區中的可用位元組數(傳送緩衝區的空閒位置大小),大於等於低水位標記,so_sndlowat,此時可以無阻塞的寫,並且返回值大於0

socket的寫操作被關閉(close或者shutdown),對於乙個寫操作被關閉的socket進行寫操作,會觸發sigpipe訊號

socket使用非阻塞connect連線成功或失敗之後

socket上有未讀取的錯誤

socket上收到帶外資料(關於帶外資料,和tcp緊急模式相關,在tcp報頭中有乙個緊急指標的字段)

簡述select的執行過程為了方便說明,假設fd_set的長度為1位元組,則一位元組最大可以對應8個檔案描述符。

1、fd_set set; fd_zero(&set); 此時set為0000 0000;

2、若fd=5,執行fd_set(&set);此時set變為0001 0000 (第5位置為1);

3、在加上 fd=1 和 fd=2 檔案描述符,則set變為0001 0011;

4、執行select(6, &set, 0, 0,0) 阻塞等待;

5、若 fd=1 和 fd=2 發生了可讀事件,則select返回,此時set變為0000 0011。注意:因為 fd=5 沒有發生事件,所以對應位置被清空;

編寫select**

一、檢測標準輸入:

1 #include2 #include3 #include4 #include5 int main()

6 22 if(fd_isset(0, &read_fds));

24 read(0, buf, sizeof(buf)-1);

25 printf("input:%s\n", buf);

26 }

27 else

30 fd_zero(&read_fds);

31 fd_set(0, &read_fds);

32 }

33 return 0;

34 }

此時的執行的效果為,找到在標準輸入上輸入之前,select都會一直阻塞等待。

二、模擬實現select伺服器

伺服器

#include2 #include3 #include4 #include5 #include6 #include7 #include8 #include9

10 int main(int argc, char* ar**)

11 17 int listen_sock = socket(af_inet, sock_stream, 0);

18 if(listen_sock < 0)

22 struct sockaddr_in local;

23 local.sin_family = af_inet;

24 local.sin_addr.s_addr = inet_addr(ar**[1]);

25 local.sin_port = htons(atoi(ar**[2]));

26 if(bind(listen_sock, (struct sockaddr*)&local, sizeof(local)) < 0)

30 if(listen(listen_sock, 5) < 0)

35 //陣列的大小就是fd_set這個位圖能夠存多少個fd,因為需要用陣列記錄

36 int fdarray[sizeof(fd_set)*8];

37 38 //把listen_sock給陣列的第乙個元素

39 fdarray[0] = listen_sock;

40 41 int num = sizeof(fdarray)/sizeof(fdarray[0]);//最多存在1024個fd

42 43 //然後初始化陣列為-1

44 for(int i=0; i= 0)

60 }

61 switch(select(maxfd+1, &rfds, null, null, null))

105 else if(s == 0)

110 else

113 }

114 }

115 }

116 }

117 }

118 return 0;

119 }

客戶端:

#include5 #include6 #include7 #include8 #include9

10 int main(int argc, char* ar**)

11 16 struct sockaddr_in server;

17 server.sin_family = af_inet;

18 server.sin_addr.s_addr = inet_addr(ar**[1]);

19 server.sin_port = htons(atoi(ar**[2]));

20 21 int fd = socket(af_inet, sock_stream, 0);

22 if(fd < 0)

26 27 int ret = connect(fd, (struct sockaddr*)&server, sizeof(server));

28 if(ret < 0)

32 33 while(1);

37 read(0, buf, sizeof(buf)-1);

38 39 ssize_t s = write(fd, buf, strlen(buf));

40 if(s < 0)

44 }

45 close(fd);

46 return 0;

47 }

select的特點

一是用於在select返回後,array作為源資料和fd_set進行fd_isset判斷

二是select返回後會把以前加入的但並沒有事件發生的fd清空,則每次開始 select 前都要重新從array中取得fd逐一加入,掃瞄array的同時取得fd的最大值maxfd,用於select的第乙個引數

select的缺點

I O多路轉接之select

什麼是i o多路轉接技術 先構建一張有關描述符的列表,然後呼叫乙個函式,直到這些描述符中的乙個已準備好進行i o時,該函式才返回,在返回時,他告訴程序哪些描述符已準備好可以進行i o。上述呼叫的函式,有select,poll,pselect及poll的增強版epoll等,本文主要介紹select。函...

I O多路轉接之select

下面的巨集提供了處理fd set的這三種描述集的方式 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 用來...

IO多路轉接之select

系統提供select函式來實現多路轉接。呼叫select函式介面的特點 一次需要等待多個檔案描述符。select函式原型 include include include int select int nfds,fd set readfds,fd set writefds,fd set exceptf...