中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

高級I/O---多路復用---select

發布時間:2020-07-02 01:15:31 來源:網絡 閱讀:422 作者:wpfbcr 欄目:網絡安全

多路復用之select

   之前在套接字編程中我們用了多線程和多進程的方法來編寫,用它們編寫的好處自然是穩定,而卻非常耗資源,在前面的高級I/O博客中說到了另外一種方式那便是高效的多路復用方式了,它會一次等待多個滿足條件的文件描述符,這里先介紹第一種多路復用方式select后面還會介紹比它更好的epoll和最好的多路復用方式epoll。


select的多路復用方式是這樣的:它需要一個buf來存放需要監聽的文件描述符,先要把所關心的文件描述符放入到所對應的讀事件集合或者寫事件集合中,一旦其中發生變化會以參數反應出來。

先來看看select的函數

       /* According to POSIX.1-2001 */
       #include <sys/select.h>

       /* According to earlier standards */
       #include <sys/time.h>
       #include <sys/types.h>
       #include <unistd.h>

       int select(int nfds, fd_set *readfds, fd_set *writefds,
                  fd_set *exceptfds, struct timeval *timeout);

       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);
       
          struct timeval {
               long    tv_sec;         /* seconds */
               long    tv_usec;        /* microseconds */
           };

nfds:是所要監控的文件描述符的個數加一,注意,一定要加一!!!

fd_set *readfds:表示監控的readfds集,這個fd_set是有上限的,我的機子上是128,那么表示最多能監控128*8=1024個。readfds是輸入輸出型的參數,它的每個bit為會對應監控一個文件描述符,select是阻塞式的等待事件的發生的,當某個文件描述符就緒則把它對應的bit為置1,而其它的都要清空,所以每次在select之前都要先執行emty(下面會說到),和set(下面會說到)。

writeds:寫事件文件描述符集

exceptfds:錯誤事件文件描述符集

timeout:設置等待事件,等待事件到了可以做其他的事情,比如提示。。

FD_CLR清空所設置關心的文件描述符集

FD_ISSET:用于檢查該文件描述符是否在對應集合中

FD_SET:用于將所關心的文件描述符添加入對應集合中

FD_ZERO:將對應文件描述符集中的所有bit位置0


下面是我對select的工作流程理解

高級I/O---多路復用---select

下面是一個使用select實現的TCPserver的簡單代碼

  1 #include<stdio.h>
  2 #include<sys/types.h>
  3 #include<sys/socket.h>
  4 #include<sys/select.h>
  5 #include<arpa/inet.h>
  6 #include<netinet/in.h>
  7 #include<string.h>
  8 #include<unistd.h>
  9 
 10 
 11 int fd[64];   //select需要創建一個用于存放socket文件描述符的buf
 12 void usage(char *proc)
 13 {
 14     printf("%s+[ip]+[port]\n",proc);
 15 }
 16 
 17 int listen_sock(const char*ip,int port)
 18     //創建listen套接字前面的socket博客中已經詳細說明了
 19 {
 20     int sock=socket(AF_INET,SOCK_STREAM,0);
 21     if(sock<0)
 22     {
 23         perror("socket");
 24     }
 25     struct sockaddr_in server;
 26     server.sin_family=AF_INET;
 27     server.sin_addr.s_addr=inet_addr(ip);
 28     server.sin_port=htons(port);
 29     if(bind(sock,(struct sockaddr*)&server,sizeof(server))<0)
 30     {
 31 
 32         perror("bind");
 33     }
 34     if(listen(sock,10)<0)
 35     {
 36         perror("listen");
 37     }
 38     return sock;
 39 }
 40 
 41 void select_server(int sock)
 42 {
 43     int i=0;
 44     for(;i<64;i++)  //初始化為無效的文件描述符
 45     {
 46         fd[i]=-1;
 47     }
 48     fd_set reads;  //定義讀文件描述符集
 49     printf("%d\n",sizeof(fd_set));
 50     fd_set writes; //定義寫文件描述符集
 51     int max_fd;    //定義select所需要的參數,比最大的文件描述符大1
 52     int newsock=-1;//定義所需要的accept到的文件描述符
 53     int fd_len=sizeof(fd)/sizeof(fd[0]);//select參數
 54 
 55     struct sockaddr_in routom;
 56     socklen_t routom_len=sizeof(routom);
 57 
 58     struct timeval timeout;//設置最大等待時間用于提醒
 59     fd[0]=sock;    //將buff中的第一個設置為監聽套接字用于監聽
 60     max_fd=sock;   //目前sock是最大的文件描述符
 61 
 62     char buf[1024];
 63     while(1)
 64     {
 65         timeout.tv_sec=5;     //單位為秒
 66         timeout.tv_usec=0;    //單位為毫秒
 67         FD_ZERO(&reads);      //初始化
 68         FD_ZERO(&writes);
 69         FD_SET(sock,&reads);
 70         //將sock添加到reads讀文件描述符集中因為每次我們都需要這個文件描述符
 71         int i=0;
 72         for(;i<fd_len;i++)
 73         {
 74         //尋找有效的文件描述符并添加到reads集中,注意(因為我們所要實現的是
 75         //客戶端發消息,服務器顯示,所以我們不關心寫文件描述符,只有兩種我們
 76         //需要關心的文件描述符1.accept到的需要鏈接服務器的文件描述符.2.有數據
 77         //需要去讀的問家描述符。
 78             if(fd[i]>0)
 79             {
 80                 FD_SET(fd[i],&reads);
 81                 if(fd[i]>max_fd)
 82                 {
 83                     max_fd=fd[i];           //將最大的文件描述符給max_fd
 84                 }
 85             }
 86         }
 87         switch(select(max_fd+1,&reads,&writes,NULL,&timeout)){
 88                 //正式進入select函數
 89                 case -1:
 90                     perror("select");
 91                 case 0 :    //返回值為0代表timeout了
 92                     printf("timeout...\n");
 93                     break;
 94                 default:
 95                     {
 96                      i=0;
 97                     for(;i<fd_len;i++)
 98                     {
 99 
100                         if(fd[i]==sock&&FD_ISSET(fd[i],&reads))
101                         //每次我們都需要監聽一次
102                         {
103 
104                             newsock=accept(sock,(struct sockaddr*)\
105                                             &routom,&routom_len);
106                             printf("%d\n",newsock);
107                             if(newsock<0)
108                             {
109                                 perror("accept");
110                                 continue;
111                             }
112                             for(i=0;i<fd_len;i++)
113                             //監聽到了就把這個套接字給一個有效的buf空間
114                             {
115                                 if(fd[i]==-1)
116                                 {
117                                     fd[i]=newsock;
118                                     printf("[ip]:%s has comming...\n",\
119                                     inet_ntoa(routom.sin_addr));
120                                     break;
121                                 }
122                             }
123                             if(i==max_fd)
124                             //如果滿了就先關閉他
125                             {
126                                 close(newsock);
127                             }
128                         }
129                         else if(fd[i]>0&&FD_ISSET(fd[i],&reads))
130                         //滿足有數據要讀的文件描述符
131                         {
132                             memset(buf,'\0',sizeof(buf));
133                             ssize_t size=read(fd[i],buf,sizeof(buf)-1);
134                             if(size>0)
135                             {
136                                 buf[size]='\0';
137                                 printf("[%s]::%s\n",inet_ntoa\
138                                 (routom.sin_addr),buf);
139                             }else if(size==0){
140                             //size小于零時,則說明遠端已經關閉
141                                 printf("[ip]%s:is shutdown\n",\
142                                 inet_ntoa(routom.sin_addr));
143                                 close(fd[i]);
144                                 fd[i]=-1;
145                             }else{
146                                     ;
147                             }
148                         }
149                     }
150                 }
151             }
152     }
153 }
154 
155 int main(int argc,char *argv[])
156 {
157     if(argc!=3)
158     {
159         usage(argv[0]);
160     }
161     char *ip=argv[1];
162     int port=atoi(argv[2]);
163     int sock=listen_sock(ip,port);
164 
165     select_server(sock);
166     close(sock);
167     return 0;
168 }


總結:select可以一次監聽多個事件,但是它的缺點是很明顯的。

    1、它是有監聽上限的,我的電腦上限是1024個。

    2、它是通過輸出型參數來返回的,這樣我們就不得不每次監聽到都要遍歷        一次整個存放文件描述符的buf。

    3、它會在設置的時候在用戶態和內核態中來回copy這樣開銷很大。


向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

青冈县| 平南县| 景泰县| 固原市| 长兴县| 积石山| 崇礼县| 施秉县| 长乐市| 泸水县| 磐石市| 永平县| 肃北| 和政县| 洞头县| 钟山县| 新营市| 扬中市| 新邵县| 天全县| 龙里县| 宁陕县| 乐至县| 浦县| 芦溪县| 宁乡县| 浏阳市| 泌阳县| 太仓市| 焉耆| 锦州市| 微博| 洛南县| 抚州市| 右玉县| 称多县| 宁海县| 陇西县| 静乐县| 遂平县| 山阳县|