您的位置:首页 > 编程语言

select服务器端的代码以及select的优缺点与多线程多进程服务器的比较

2017-07-29 15:51 911 查看
     系统提供select函数来实现多路复用输入输出模型。select系统调用是用来让我们的程序监视多个文件的状态发生变化的。程序会停在select这里等待,直到被监视的文件句柄有一个或多个发生了状态改变。关于文件句柄,其实就是一个整数:

0:代表标准输入(对应的FILE*结构是stdin)

1:是标准输出(对应的FILE*结构是stdout)

2:是标准错误(对应的FILE*结构是stderr)

函数返回值:

=0:表示等待超时

=-1:表示等待出错

>0:表示文件描述符中就绪文件描述符的个数

1、server服务器端:

  1 #include<stdio.h>                                                     

  2 #include<stdlib.h>

  3 #include<sys/types.h>

  4 #include<sys/socket.h>

  5 #include<netinet/in.h>

  6 #include<arpa/inet.h>

  7 #include<string.h>

  8 #include<fcntl.h>

  9 #include<sys/select.h>

 10 #define SIZE sizeof(fd_set)*8

 11         

 12 int fds[SIZE];

 13         

 14 int startup(const char *ip,int port)

 15 {       

 16     int sock=socket(AF_INET,SOCK_STREAM,0);

 17     if(sock<0)

 18     {   

 19     ┊   perror("socket");

 20     ┊   exit(2);

 21     }   

 22     struct sockaddr_in local;

 23     local.sin_family=AF_INET;

 24     local.sin_port=htons(port);

 25     local.sin_addr.s_addr=inet_addr(ip);

 26               

 27     if(bind(sock,(struct sockaddr*)&local,sizeof(local))<0)

 28     {         

 29     ┊   perror("bind");

 30     ┊   exit(3);

 31     }         

 32     if(listen(sock,10)<0)

 33     {         

 34     ┊   perror("listen");

 35     ┊   exit(4);

 36     }         

 37     return sock;

 38 }             

 39 static void usage(const char *proc)                                   

 40 {             

 41     printf("Usage:%s[local_ip][local_port]\n",proc);

 42 }             

 43 int main(int argc,char* argv[])

 44 {           

 45     if(argc!=3)

 46     {       

 47     usage(argv[0]);

 48     return 1;

 49     }       

 50     int listen_sock=startup(argv[1],atoi(argv[2]));

 51     int nums=sizeof(fds)/sizeof(fds[0]);

 52     int i=0;

 53     for(;i<nums;i++)

 54     {       

 55     ┊   fds[i]=-1;

 56     }       

 57     fds[0]=listen_sock;

 58     while(1)

 59     {       

 60     ┊   int max_fd=-1;                                                

 61     ┊   fd_set rfds;

 62     ┊   FD_ZERO(&rfds);

 63     ┊   for(i=0;i<nums;i++)

 64     ┊   {

 65     ┊   ┊   if(fds[i]>0)

 66     ┊   ┊   {

 67     ┊   ┊   ┊   FD_SET(fds[i],&rfds);

 68     ┊   ┊   ┊   if(max_fd<fds[i])

 69     ┊   ┊   ┊   {

 70     ┊   ┊   ┊   ┊   max_fd=fds[i];

 71     ┊   ┊   ┊   }

 72     ┊   ┊   }

 73     ┊   }

 74     ┊   struct timeval timeout={1,0};

 75     ┊   switch(select(max_fd+1,&rfds,NULL,NULL,&timeout))

 76     ┊   {

 77     ┊   ┊   case 0:

 78     ┊   ┊   printf("timeout...\n");

 79     ┊   ┊   break;

 80     ┊   ┊   case -1:

 81     ┊   ┊   perror("select");                                         

 82     ┊  
c85e
┊   default:

 83     ┊   ┊   {

 84     ┊   ┊   ┊   for(i=0;i<nums;i++)

 85     ┊   ┊   ┊   {

 86     ┊   ┊   ┊   ┊   if(fds[i]==-1)

 87     ┊   ┊   ┊   ┊   {

 88     ┊   ┊   ┊   ┊   ┊   continue;                                     

 89     ┊   ┊   ┊   ┊   }

 90     ┊   ┊   ┊   ┊   if(i==0&&FD_ISSET(listen_sock,&rfds))

 91     ┊   ┊   ┊   ┊   {

 92     ┊   ┊   ┊   ┊   ┊   struct sockaddr_in client;

 93     ┊   ┊   ┊   ┊   ┊   socklen_t len= sizeof(client);

 94     ┊   ┊   ┊   ┊   ┊   int new_sock=accept(listen_sock,(struct           sockaddr*)&client,&len);

 95     ┊   ┊   ┊   ┊   ┊   if(new_sock<0)

 96     ┊   ┊   ┊   ┊   ┊   {

 97     ┊   ┊   ┊   ┊   ┊   ┊   perror("accept");

 98     ┊   ┊   ┊   ┊   ┊   ┊   continue;

 99     ┊   ┊   ┊   ┊   ┊   }

100     ┊   ┊   ┊   ┊   ┊   printf("get a new client:[%s:%d\n]",              inet_ntoa(client.sin_addr),ntohs(client.sin_port));

101     ┊   ┊   ┊   ┊   ┊   int j=1;

102     ┊   ┊   ┊   ┊   ┊   for(;j<nums;j++)

103     ┊   ┊   ┊   ┊   ┊   {

104     ┊   ┊   ┊   ┊   ┊   ┊   if(fds[j]==-1)

105     ┊   ┊   ┊   ┊   ┊   ┊   {

106     ┊   ┊   ┊   ┊   ┊   ┊   ┊   break;

107     ┊   ┊   ┊   ┊   ┊   ┊   }

108     ┊   ┊   ┊   ┊   ┊   }

109     ┊   ┊   ┊   ┊   ┊   if(j==nums)

110     ┊   ┊   ┊   ┊   ┊   {

111     ┊   ┊   ┊   ┊   ┊   ┊   printf("client is full\n");

112     ┊   ┊   ┊   ┊   ┊   ┊   close(new_sock);

113     ┊   ┊   ┊   ┊   ┊   }

114     ┊   ┊   ┊   ┊   ┊   else

115     ┊   ┊   ┊   ┊   ┊   {

116     ┊   ┊   ┊   ┊   ┊   ┊   fds[i]=new_sock;

117     ┊   ┊   ┊   ┊   ┊   }

118     ┊   ┊   ┊   ┊   }

119     ┊   ┊   ┊   ┊   else if(i!=0&&FD_ISSET(fds[i],&rfds))

120     ┊   ┊   ┊   ┊   {

121     ┊   ┊   ┊   ┊   ┊   char buf[1024];                               

122     ┊   ┊   ┊   ┊   ┊   ssize_t s=read(fds[i],buf,sizeof(buf)-1);

123     ┊   ┊   ┊   ┊   ┊   if(s>0)

124     ┊   ┊   ┊   ┊   ┊   {

125     ┊   ┊   ┊   ┊   ┊   ┊   buf[s]=0;

126     ┊   ┊   ┊   ┊   ┊   ┊   printf("client#%s\n",buf);

127     ┊   ┊   ┊   ┊   ┊   }

128     ┊   ┊   ┊   ┊   ┊   else

129     ┊   ┊   ┊   ┊   ┊   {

130     ┊   ┊   ┊   ┊   ┊   ┊   perror("read");

131     ┊   ┊   ┊   ┊   ┊   ┊   close(fds[i]);

132     ┊   ┊   ┊   ┊   ┊   ┊   fds[i]=-1;

133     ┊   ┊   ┊   ┊   ┊   }

134     ┊   ┊   ┊   ┊   }

135     ┊   ┊   ┊   ┊   else

136                                {       

137         

138                                }       

139     ┊   ┊   ┊   }

140     ┊   ┊   }

141     ┊   ┊   break;

142     ┊   }                                                             

143     }   

144     return 0;

145 }  

运行结果:


     

2、client客户端:

  1 #include<stdio.h> 

  2 #include<sys/types.h> 

  3 #include<netinet/in.h> 

  4 #include<arpa/inet.h> 

  5 #include<sys/socket.h> 

  6 #include<stdlib.h> 

  7 #include<string.h> 

  8 #include<strings.h> 

  9 #include<sys/stat.h> 

 10 #include<unistd.h> 

 11                  

 12 static void Usage(char *proc) 

 13 {                

 14     printf("Usage:%s[ip][port]\n"); 

 15 }                

 16 int main(int argc,char*argv[]) 

 17 {                

 18     if(argc!=3)       

 19     {            

 20        Usage(argv[0]); 

 21        return 1;

 22     }     

 23     int sock=socket(AF_INET,SOCK_STREAM,0);

 24     struct sockaddr_in peer;

 25     peer.sin_family=AF_INET;

 26     peer.sin_port=htons(atoi(argv[2]));

 27     peer.sin_addr.s_addr=inet_addr(argv[1]);

 28     peer.sin_port=htons(atoi(argv[2]));

 29     peer.sin_addr.s_addr=inet_addr(argv[1]);

 30     if(connect(sock,(struct sockaddr*)&peer,sizeof(peer))<0)

 31     {     

 32     ┊   perror("connect");

 33     ┊   return 2;

 34     }     

 35     char buf[1024];

 36     //int sfd=dup(STDOUT_FILENO);

 37     while(1)

 38     {     

 39     ┊   printf("Please enter:");                                      

 40     ┊   fflush(stdout);

 41     ┊   ssize_t s=read(0,buf,sizeof(buf)-1);

 42     ┊   int sfd=dup(STDOUT_FILENO);

 43     ┊   if(s>0)

 44     ┊   {      

 45     ┊   ┊   buf[s-1]=0;

 46     ┊   ┊   //write(sock,buf,strlen(buf));

 47     ┊   ┊   //shuchuchongdingxiang

 48     ┊   ┊   //close(1);

 49     ┊   ┊   //int new_fd=dup(sock);

 50     ┊   ┊   int new_fd=dup2(sock,1);

 51     ┊   ┊   if(new_fd==-1)

 52     ┊   ┊   { 

 53     ┊   ┊   ┊   perror("dup()");

 54     ┊   ┊   ┊   return -1;

 55     ┊   ┊   } 

 56     ┊   ┊   printf("%s",buf);

 57     ┊   ┊   fflush(stdout);

 58     ┊   ┊   //huifu stdout

 59     ┊   ┊   dup2(sfd,STDOUT_FILENO);

 60     ┊   ┊   ssize_t  _s=read(sock,buf,sizeof(buf)-1);

 61     ┊   ┊   if(_s>0)

 62     ┊   ┊   {

 63     ┊   ┊   ┊   buf[_s]=0;

 64     ┊   ┊   ┊   printf("server#%s\n",buf);

 65     ┊   ┊   }

 66     ┊   }

 67     }   

 68     close(sock);

 69     return 0;

 70 }                                                                     

3、总结:select服务器的优缺点与多进程/多线程服务器进行对比: 
使用select注意事项: 
1>要将sock_fd加入到maxfd+1中,要不就无法检测到网络连接,会一直阻塞在select语句 
2>通过存储每次连接的描述符,设置FD_SET函数,在遍历的去判断FD_ISSET处理。 
3>我们可以看到select每次有数据到来时,需要遍历的去寻找所有可用的描述符,来判断其是否满足处理的条件。 
4>select的通知机制,轮询的去查看是否在maxfd+1内有满足条件的描述符  

与多进程/多线程服务器进行对比 它的优点在于: 

1)不需要建立多个线程、进程就可以实现一对多的通信。 

2)可以同时等待多个文件描述符,效率比起多进程多线程来说要高很多。 

3)select()的可移植性更好,在某些Unix系统上不支持poll() 

4)select() 对于超时值提供了更好的精度:微秒,而poll是毫秒

与多进程/多线程服务器进行对比 它的缺点在于: 

1)每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大 ,循环次数有点多; 

2)同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大 。 

3)select支持的文件描述符数量太小了,默认是1024;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐