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(水平触发)模式

     

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值