select系統呼叫的目的是:在一段指定時間內,監聽使用者感興趣的檔案描述符上的可讀、可寫和異常事件。poll和select應該被歸類為這樣的系統 呼叫,它們可以阻塞地同時探測一組支援非阻塞的io裝置,直至某乙個裝置觸發了事件或者超過了指定的等待時間——也就是說它們的職責不是做io,而是幫助 呼叫者尋找當前就緒的裝置。
#include #include int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
引數nfds是需要監視的檔案描述符數,要監視的檔案描述符值為0 ~ nfds-1。
引數readfds指定需要監視的可讀檔案描述符集合,當這個集合中的乙個描述符上有資料到達時,系統將通知呼叫select函式的程式。
引數writefds指定需要監視的可寫檔案描述符集合,當這個集合中某個描述符可以傳送資料時,程式將收到通知。
引數exceptfds指定需要監視的異常檔案描述符集合,當這個集合中的乙個描述符發生異常時,程式將受到通知。
引數timeout指定了阻塞的時間,如果在這段時間內監視的檔案描述符上沒有事件發生,則函式select()將返回0。
struct fd_set可以理解為乙個集合,這個集合中存放的是檔案描述符,可通過以下四個巨集進行設定:
void fd_zero(fd_set *fdset); //清空集合
void fd_set(int fd, fd_set *fdset); //將乙個給定的檔案描述符加入集合之中
void fd_clr(int fd, fd_set *fdset); //將乙個給定的檔案描述符從集合中刪除
int fd_isset(int fd, fd_set *fdset); // 檢查集合中指定的檔案描述符是否可以讀寫
fd_set結構體是檔案描述符集,該結構體實際上是乙個整型陣列,陣列中的每個元素的每一位標記乙個檔案描述符。fd_set能容納的檔案描述符 數量由fd_setsize指定,一般情況下,fd_setsize等 於1024,這就限制了select能同時處理的檔案描述符的總量。
timeout告知核心等待所指定描述字中的任何乙個就緒可花多少時間。其timeval結構用於指定這段時間的秒數和微秒數。
struct timeval;
select()返回值情況:
超時時間內,如果檔案描述符就緒,select返回就緒的檔案描述符總數(包括可讀、可寫和異常),如果沒有檔案描述符就緒,select返回0;
select呼叫失敗時,返回 -1並設定errno,如果收到訊號,select返回 -1並設定errno為eintr。
檔案描述符的就緒條件:
在網路程式設計中,
1)下列情況下socket可讀:
a) socket核心接收緩衝區的位元組數大於或等於其低水位標記so_rcvlowat;
b) socket通訊的對方關閉連線,此時該socket可讀,但是一旦讀該socket,會立即返回0(可以用這個方法判斷client端是否斷開連線);
c) 監聽socket上有新的連線請求;
d) socket上有未處理的錯誤。
2)下列情況下socket可寫:
a) socket核心傳送緩衝區的可用位元組數大於或等於其低水位標記so_sndlowat;
b) socket的讀端關閉,此時該socket可寫,一旦對該socket進行操作,該程序會收到sigpipe訊號;
c) socket使用connect連線成功之後;
d) socket上有未處理的錯誤。
select優點
解決了讀寫阻塞的問題,一程序一線程就可完成資料間的傳輸,避免了多程序多執行緒的消耗,提高了資源利用率。
select缺點
select支援的檔案描述符數量最大1024
每次呼叫select都需要把fd集合從使用者態拷貝到核心態,這個開銷在fd很多時會很大
每次呼叫select都需要在核心遍歷傳遞進來的所有fd,檢視有沒有就緒的fd,這個開銷在fd很多時也很大,效率隨fd數目增加而線性下降
#include #include #include #include #include #include #include const int backlog = 8; //最大監聽數
int fds[32];
int fds_nums = sizeof(fds) / sizeof(*fds);
#define buffsize 1024
int m_listen(char *ip, int port)
//設定套接字屬性
int optval = 1;
if(setsockopt(sock_fd, sol_socket, so_reuseaddr, (void *)&optval, sizeof(int)) == -1)
//繫結埠
if(bind(sock_fd, (struct sockaddr*)&ser, sizeof(struct sockaddr)) == -1)
//監聽
if(listen(sock_fd, backlog) == -1)
return sock_fd;
}int main(int argc, char *argv)
int sock_fd = m_listen(argv[1], atoi(argv[2]));
fd_set readfds;
//清空讀取檔案描述符集
fd_zero(&readfds);
int i;
for(i = 0; i < fds_nums; ++i)
fds[i] = -1;
//將監聽套接字加入0號位
fds[0] = sock_fd;
while(1)
switch(select(max_fd + 1, &readfds, null, null, null))
fprintf(stderr, "accept a new client, ip:%s port:%d\n", inet_ntoa(cli.sin_addr),\
ntohs(cli.sin_port));
int j;
for(j = 0; j < fds_nums; ++j)
if(fds[j] == -1)
if(j == fds_nums)
close(conn_fd);
}else
;if((ret = recv(fds[i], buff, buffsize - 1, 0)) == -1)
else if(ret)
else}}
}break;}}
return 0;
}
IO多路復用 select
出自朱有鵬老師的課堂 include include include include include include include include include int main void 當前有兩個fd,乙個是fd乙個是0 處理 myset fd zero myset 全部清零 fd set ...
I O多路復用 select
int select int n,fd set readfds,fd set writefds,fd set exceptfds,struct timeval timeout 有三種型別的描述符型別 readset writeset exceptset,分別對應讀 寫 異常條件的描述符集合。fd s...
IO多路復用 select模型
客戶端 見 c s通訊 伺服器阻塞型使用 伺服器端 include include include include include include include include include include include include int main set local address m...