select服务器
?select介紹
我們先來看一下select的接口。
? ? ? ?int select(int nfds, fd_set *readfds, fd_set *writefds,
? ? ? ? ? ? ? ? ? fd_set *exceptfds, struct timeval *timeout);
1
2
3
4
5
1
2
3
4
5
從上面的這些接口我們應該能有寫認識,首先我么來看select系統調用的參數的含義。
參數 功能
nfds 被監聽的文件描述符的總數。通常是文件描述符最大值加1
readfds 可讀事件的文件描述符集合
writefds 可寫事件的文件描述符集合
exceptfds 異常事件的文件描述符集合
timeout 設定超時時間
值得注意的是,后面的三個參數即是輸入型參數,又是輸出型參數,輸入表示關心那些文件描述符對應的特定事件發生。輸出表示的是那些文件描述符對應的事件就緒。當就緒后,內核將會去修改這些文件描述符的集合。這三個參數都是fd_set結構體類型
typedef struct
{
? ? /*XPG4.2requiresthismembername.Otherwiseavoidthename
? ? fromtheglobalnamespace.*/
? ? #ifdef__USE_XOPEN
? ? __fd_maskfds_bits[__FD_SETSIZE/__NFDBITS];
? ? #define__FDS_BITS(set)((set)->fds_bits)
? ? #else
? ? __fd_mask__fds_bits[__FD_SETSIZE/__NFDBITS];
? ? #define__FDS_BITS(set)((set)->__fds_bits)
? ? #endif
}fd_set;
1
2
3
4
5
6
7
8
9
10
11
12
13
1
2
3
4
5
6
7
8
9
10
11
12
13
可以看出fd_set就是一個結構體數組,這個結構體數組的每一位就是一個文件描述符的標記,配套提供了一些對于fd_set操作的宏。
? ? ? ?void FD_CLR(int fd, fd_set *set);
? ? ? ?int ?FD_ISSET(int fd, fd_set *set);
? ? ? ?void FD_SET(int fd, fd_set *set);
? ? ? ?void FD_ZERO(fd_set *set);
1
2
3
4
1
2
3
4
宏 功能
FD_CLR 進行對應位fd
FD_ISSET 進行判斷對應位fd
FD_SET 設置fd的對應位置
FD_ZERO 進行清空fd_set
最后要說一下的就是timeout參數,這個參數用來設置超時時間,它也是一個結構體,它用來告訴應用程序select等待多久,這里的單位是微秒級別的。
timeout參數 說明
0 立即返回,即輪詢
NULL 阻塞監視文件描述符,當有時間就緒才返回
大于0的時間 超時時間設置
select調用時內核級別的,select的輪詢方式是和非阻塞輪詢方式是不同的,select的輪詢方式是同時可以對多個I/O端口進行監聽,任何一個端口數據好了,這個時候就可以讀了。然后通過系統調用,就可以把數據從內核拷貝到用戶進程。
![enter description here][1]
select缺點
1、 單個進程可監視的fd數量被限制,即能監聽端口的大小有限。
? 一般來說這個數目和系統內存關系很大,具體數目可以cat /proc/sys/fs/file-max查看,有宏FD_SETSIZE進行限制fd的數量。32位機默認是1024個。64位機默認是2048.
2、 對socket進行掃描時是線性掃描,即采用輪詢的方法,效率較低:
? ?當套接字比較多的時候,每次select()都要通過遍歷FD_SETSIZE個Socket來完成調度,不管哪個Socket是活躍的,都遍歷一遍。這會浪費很多CPU時間。如果能給套接字注冊某個回調函數,當他們活躍時,自動完成相關操作,那就避免了輪詢,這正是epoll與kqueue做的。
3、需要維護一個用來存放大量fd的數據結構,這樣會使得用戶空間和內核空間在傳遞該結構時復制開銷大
select代碼示例:
??6 #include<sys/socket.h> ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
? 7 #include<sys/types.h>
? 8 #include<netinet/in.h>
? 9?
?10 void usage(const char* str)
?11 {
?12 ? ? printf("usage:%[local_IP] [localP_port]\n",str);
?13 }
?14?
?15?
?16 int startup(char* IP, int port)
?17 {
?18 ? ?int ?sock=socket(AF_INET,SOCK_STREAM,0);
?19 ? ?if(sock<0){
?20 ? ? ? ?perror("sock");
?21 ? ? ? ?exit(2);
?22 ? ? ? ?}
?23?
?24 ? ? ? ?struct sockaddr_in ser_addr;
?25 ? ? ? ?ser_addr.sin_family=AF_INET;
?26 ? ? ? ?ser_addr.sin_port=htons(port);
?27 ? ? ? ?ser_addr.sin_addr.s_addr=inet_addr(IP);
?28 ? ? ? ?if(bind(sock,(struct sockaddr*)&ser_addr,sizeof(ser_addr))<0){
?29 ? ? ? ? ? ?perror("bind");
?30 ? ? ? ? ? ?exit(3);
?31 ? ? ? ? ? ?}
?32 ? ? ? ??
?33 ? ? ? ? ? ?if(ret<0){ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
?34 ? ? ? ? ? ? ? ?perror("listen");
?35 ? ? ? ? ? ? ? ?exit(4);
?36 ? ? ? ? ? ? ? ?}
?37 ? ? ? ? ? ? ? ?return sock;
?38 }
?39 ?int array_fds[1024];
?40 ?int maxfd;
?41?
?42?
?43 int main(int argc, char* argv[])
?44 {
?45 ? ? if(argc!=3){
?46 ? ? ? ? usage(argv[0]);
?47 ? ? ? ? exit(0);
?48 ? ? ? ? }
?49?
?50 ? ? ? ? int listen_sock=startup(argv[1],atoi(argv[2]));
?51 ? ? ? ? int i=0;
?52 ? ? ? ? for(;i<1024;i++){
?53 ? ? ? ? ? ? array_fds[i]=-1;
?54 ? ? ? ? ? ? }
?55?
?56 ? ? ? ? ? ? array_fds[0]=listen_sock;
?57 ? ? ? ? ? ? fd_set reads;
?58 ? ? // ? ? ?fd_set writes;
?59 ? ? ? ? ? ? struct timeval timeout;
?60 ? ? ? ? ? ? while(1){
?61 ? ? ? ? ? ? ? ? FD_ZERO(&reads);
-- INSERT -- ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?33,1 ? ? ? ? ?28%
60 ? ? ? ? ? ? while(1){ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
?61 ? ? ? ? ? ? ? ? FD_ZERO(&reads);
?62 ? ? // ? ? ? ? ?FD_ZERO(&writes);
?63 ? ? ? ? ? ? ? ? int maxfd=0;
?64 ? ? ? ? ? ? ? ? timeout.tv_sec=10;
?65 ? ? ? ? ? ? ? ? timeout.tv_usec=0;
?66?
?67 ? ? ? ? ? ? ? ? for(i=0;i<1024;i++){
?68 ? ? ? ? ? ? ? ? // ?if(array_fds[i]>0){
?69 ? ? ? ? ? ? ? ? // ? ? ?FD_SET(array_fds[i],&reads);
?70 ? ? ? ? ? ? ? ? // ? ? ?FD_SET(array_fds[i],&writes);
?71 ? ? ? ? ? ? ? ? ? ?if(array_fds[i]==-1){
?72 ? ? ? ? ? ? ? ? ? ? ? ?continue;
?73 ? ? ? ? ? ? ? ? ? ? ? ?}
?74 ? ? ? ? ? ? ? ? ? ? ? ?FD_SET(array_fds[i],&reads);
?75 ? ? ? ? ? ? ? ? ? ? ? ? if(array_fds[i]>maxfd){
?76 ? ? ? ? ? ? ? ? ? ? ? ? ? ? maxfd=array_fds[i];
?77 ? ? ? ? ? ? ? ? ? ? ? ? ? ? }
?78 ? ? ? ? ? ? ? ? ? ? ? ? }//FD
?79 ? ? ? ? ? ? ? ? ? ? }//for
?80 ? ? ? ? ? ? ? ? ? ? int j=0;
?81 ? ? ? ? ? ? ? ? ? ? switch(select(maxfd+1,&reads,/*&writes*/NULL,NULL,&timeout)){
?82 ? ? ? ? ? ? ? ? ? ? ? ? case 0:
?83 ? ? ? ? ? ? ? ? ? ? ? ? printf("time quit.....\n");
?84 ? ? ? ? ? ? ? ? ? ? ? ? break;
?85 ? ? ? ? ? ? ? ? ? ? ? ? case -1:
?86 ? ? ? ?
86 ? ? ? ? ? ? ? ? ? ? ? ? perror("select");
?87 ? ? ? ? ? ? ? ? ? ? ? ? break;
?88 ? ? ? ? ? ? ? ? ? ? ? ? default:{
?89 ? ? ? ? ? ? ? ? ? ? ? ? for(j=0;j<1024;j++){
?90 ? ? ? ? ? ? ? ? ? ? ? ? ? ? if(array_fds[j]<0){
?91 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? continue;
?92 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?}
?93 ? ? ? ? ? ? ? ? ? ? ? ? ? ? if(j==0&&FD_ISSET(array_fds[0],&reads)){
?94 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? struct sockaddr_in client;
?95 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? socklen_t len=sizeof(client);
?96 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? int new_sock=accept(array_fds[0],(struct sockaddr *)&client,&len);
?97 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? if(new_sock<0){
?98 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? perror("accept");
?99 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? continue;
100 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }
101?
102 ? ? ?printf("get a new client :(%s:%d)\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));
103 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? int k=1;
104 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? for(;k<1024;k++){
105 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? if(array_fds[k]<0)
106 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
107 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? break;
108 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }
109 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
110 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? if(j==1024){
111 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? printf("client full\n");
112 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? close(new_sock);
113 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }
114 ? ? ? ? ? ? ? ??
114 ? ? ? ? ? ? ? ? ? ? ? ? ? ? }else{
115 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? if(j!=0&&FD_ISSET(array_fds[j],&reads)){116 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? printf("***read start***");
117 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? char buf[1024];
118 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ssize_t s=read(array_fds[j],buf,sizeof(buf)-1);
119 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? if(s<0){
120 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? perror("read");
121 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? close(array_fds[j]);
122 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? array_fds[j]=-1;
123 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
124 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }else if(s==0){
125 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? printf("client quit...\n");
126 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? close(array_fds[j]);
127 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? array_fds[j]=-1;
128?
129 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }else {
130 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? buf[s]=0;
131 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? printf("client # ?%s\n",buf);
132 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }
133 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }else{
134?
135 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }
136 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }
137 ? ? ? ? ? ? ? ? ? ? ? ? }
138 ? ? ? ? ? ? ? ? ? ? ? ? break;
139 ? ? ? ? ? ? ? ? }//while
140 ? ? ? ? ? ? ? ? ? ? ? ? ? ? }
141 ? ? ? ? ? ? ? ? ? ? ? ? ? ? return 0;
142 } ? ? ? ?
select優點:
1.一次可以等待多個文件描述符,減少了平均等待時間
2.客戶越來越多時,減輕了進程調度的壓力(相較于多進程多線程服務器)
select缺點:
1.能監聽的文件描述符有上限,這個上限是由fd_set決定的。
2.它返回的只是就緒事件的個數,要判斷是那個事件滿足,需要遍歷文件描述符。
3.select監聽的集合是輸入輸出參數,每次監聽都需要重新初始化。
4.每次調用select,都需要把fd集合從用戶態拷貝到內核態,這個開銷在fd很多時會很大
5.內核采用輪詢(遍歷fd集合)的方式來檢測就緒事件,這個開銷在fd很多時也很大
6.select和poll都只能工作在低效的LT(水平觸發)模式
?? ??
總結