I/O多路轉接之select(只負責等)
成都創(chuàng)新互聯(lián)公司專業(yè)為企業(yè)提供赤坎網站建設、赤坎做網站、赤坎網站設計、赤坎網站制作等企業(yè)網站建設、網頁設計與制作、赤坎企業(yè)網站模板建站服務,十余年赤坎做網站經驗,不只是建網站,更提供有價值的思路和整體網絡服務。
系統(tǒng)提供select函數(shù)來實現(xiàn)多路復用輸入/輸出模型。

傳向select的參數(shù)告訴內核:
1)我們所關心的文件描述符。
參數(shù)nfds是需要監(jiān)視的最大的文件描述符值+1;
2)對每個描述符,我們所關心的狀態(tài)。
rdset,wrset,exset分別對應于需要檢測的可讀文件描述符的集合,可寫文件描述符的集合及異常文件描述符的集合。
監(jiān)視的文件描述符分為三類set,每一種對應等待不同的事件。readfds中列出的文件描述符被監(jiān)視是否有數(shù)據可供讀取(如果讀取操作完成則不會 阻 塞)。writefds中列出的文件描述符則被監(jiān)視是否寫入操作完成而不阻塞后,exceptfds中列出的文件描述符則被監(jiān)視是否發(fā)生異常,或者無 法控制的數(shù)據是否可用(這些狀態(tài)僅僅應用于套接字)。這三類set可以是NULL,這種情況下select()不監(jiān)視這一類事件。
下面的宏提供了處理這三種描述詞組的方式:
FD_ZERO移除指定set中的所有文件描述符。每一次調用select()之前都應該先調用它;
FD_CLR則從指定的set中移除一個文件描述符;
FD_SET添加一個文件描述符到指定的set中;
FD_ISSET測試一個文件描述符是否指定set的一部分。如果文件描述符在set中則返回一個非0整數(shù),不在則返0,F(xiàn)D_ISSET在調用select() 返回之后使用,測試指定的文件描述符是否準備好相關動作。
3)我們要等待多長時間。(我們可以等待無限長的時間,等待固定的一段時間,或者根本就不等待)
timeout參數(shù)是一個指向timeval結構體的指針,timeval定義如下:

struct timeval結構用于描述一段時間長度,如果在這個時間內,需要監(jiān)視的描述符沒有事件發(fā)生則函數(shù)返回,返回值為0。
從 select函數(shù)返回后,內核告訴我們以下信息:
1)已經做好準備的描述符的個數(shù)。
2)對于三種條件哪些描述符已經做好準備。(讀,寫,異常)
執(zhí)成功則返回件描述詞狀態(tài)已改變的個數(shù);如果返回0代表在描述詞狀態(tài)改變前已超過timeout時間,沒有返回;
當有錯誤發(fā)時則返回-1,錯誤原因存于errno,此時參數(shù)readfds,writefds,exceptfds和timeout的值變成不可預測。錯誤值可能為:EBADF 件描述詞為效的或該件已關閉 ,EINTR 此調被信號所中斷 EINVAL 參數(shù)n 為負值,ENOMEM 核內存不。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<errno.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<arpa/inet.h>
int fds[64];
const fds_nums=sizeof(fds)/sizeof(fds[0]);
static int startup(const char* _ip,int _port)
{
int sock=socket(AF_INET,SOCK_STREAM,0);
if(sock<0)
{
perror("socket");
exit(2);
}
int opt=1;
setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
struct sockaddr_in local;
local.sin_family=AF_INET;
local.sin_port=htons(_port);
local.sin_addr.s_addr=inet_addr(_ip);
if(bind(sock,(struct sockaddr*)&local, sizeof(local))<0)
{
perror("bind");
exit(3);
}
if(listen(sock,5)<0)
{
perror("listen");
exit(4);
}
return sock;
}
static void usage(const char* _proc)
{
printf("Usage:%s[IP] [PORT]\n",_proc);
}
int main(int argc,char* argv[])
{
if(argc!=3)
{
usage(argv[0]);
exit(1);
}
int i=0;
for(i=0;i<fds_nums;++i)
{
fds[i]=-1;
}
int listen_sock=startup(argv[1],atoi(argv[2]));
fd_set rset;
FD_ZERO(&rset);
FD_SET(listen_sock,&rset);
fds[0]=listen_sock;
int done=0;
while(!done)
{
int max_fd=-1;
for(i=0;i<fds_nums;++i)
{
if(fds[i]>0)
{
FD_SET(fds[i],&rset);
max_fd=max_fd<fds[i]?fds[i]:max_fd;
}
}
struct timeval timeout={0,0};
switch(select(max_fd+1,&rset,NULL,NULL,NULL/*&timeout*/))
{
case 0:
printf("timeout..\n");
break;
case 1:
perror("select");
break;
default:
for(i=0;i<fds_nums;++i)
{
if(i=0&& FD_ISSET(listen_sock,&rset))
{
struct sockaddr_in peer;
socklen_t len=sizeof(peer);
int new_fd=accept(listen_sock,(struct sockaddr*)&peer,&len);
if(new_fd>0)
{
printf("get a new client:socket->%s:%d\n",inet_ntoa(peer.sin_addr),ntohs(peer.sin_port));
int j=0;
for(j=0;j<fds_nums;j++)
{
if(fds[i]==-1)
{
fds[j]=new_fd;
break;
}
}
if(j==fds_nums)
{
close(new_fd);
}
}
else
{
if(FD_ISSET(fds[i],&rset))
{
char buf[1024];
memset(buf,'\0',sizeof(buf));
ssize_t _s=read(fds[i],buf,sizeof(buf)-1);
if(_s>0)
{
printf("client# %s\n",buf);
}
else if(_s==0)
{
printf("client close...\n");
close(fds[i]);
}
else
{
perror("read");
}
}
}
}
}
break;
}
}
return 0;
}select優(yōu)點:
(1)相較于之前多線程的方法,使用select不用創(chuàng)建線程,更方便
(2)select目前幾乎在所有的平臺上都支持,其良好跨平臺支持也是它的一個優(yōu)點
select缺點:
(1)每次調用select,都需要把fd集合從用戶態(tài)拷貝到內核態(tài),這個開銷在fd很多時會很大
(2)同時每次調用select都需要在內核遍歷傳遞進來的所有fd,這個開銷在fd很多時也很大
(3)能夠監(jiān)視的文件描述符的數(shù)量存在最大限制,在Linux上一般為1024,因為它依賴于文件系統(tǒng)
(4)select()所維護的文件描述符的數(shù)據結構,隨著文件描述符數(shù)量的增大,其復制的開銷也線性增長。
(5)由于網絡響應時間的延遲使得大量TCP連接處于非活躍狀態(tài),但調用select()會對所有socket進行一次線性掃描,這也會有一些開銷
網站標題:I/O多路轉接之select
文章網址:http://chinadenli.net/article36/gphgsg.html
成都網站建設公司_創(chuàng)新互聯(lián),為您提供域名注冊、軟件開發(fā)、小程序開發(fā)、電子商務、微信公眾號、ChatGPT
聲明:本網站發(fā)布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創(chuàng)新互聯(lián)