linux网络编程 多路IO转接服务器 select

多路IO转接服务器:不由应用程序自己监听客户端连接,取而代之由内核替应用程序监听文件

  • select能监听的文件描述符个数受限于FD_SETSIZE,一般为1024
  • 超过限制值后,select采用轮询模型,会降低服务器响应效率
   1 /*
  2 头文件:#include <sys/select.h>
  3 原型:
  4 int select(int nfds, fd_set *restrict readfds,fd_set *restrict writefds, fd_set *restrict errorfds,struct timeval *restrict timeout);
  5 nfds:          最大描述符+1
  6 readfds:        可读事件
  7 writefds:       可写事件
  8 errorfds:       异常事件
  9 timeout:        设置监听的时间  
 10 
 11 返回值:成功返回满足条件的总数;失败返回-1,设置errno
 12 作用:判断集合中是否有对应的文件描述符发送数据
 13 
 14 
 15 fd_set set;     设置文件描述符集
 16 void FD_CLR(int fd,fd_set *set);        将fd从set中清除
 17 void FD_ZERO(fd_set *set);              将set清空
 18 void FD_SET(int fd,fd_set *set);        将将fd设置到set集合中去
 19 
 20 int  FD_ISSET(int fd,fd_set *set);      判断fd是否在集合中
 21 返回值:成功返回0;失败返回-1,设置errno
 22 作用:设置文件描述符集合内的成员
 23 
 24 struct timespec {
 25     time_t      tv_sec;          seconds 
 26     long        tv_nsec;         nanoseconds 
 27 };
 28 struct timeval {
 29     time_t      tv_sec;          seconds 
 30     suseconds_t tv_usec;         microseconds 
 31 };
 32 */

服务器监听、对可读事件的操作:


 34 #include<stdio.h>
 35 #include<sys/socket.h>
 36 #include<arpa/inet.h>
 37 #include<strings.h>
 38 #include<unistd.h>
 39 #include<ctype.h>
 40 #include"fork.h"
 41 #define SERV_PORT 8888
 42 #define SERV_ADDR "127.0.0.1"
 43 void main(void)
 44 {
 45         //定义listennfd套接字
 46         int nfds,listenfd,connfd,sockfd;
 47         listenfd=socket(AF_INET,SOCK_STREAM,0);
 48         char buf[BUFSIZ];
 49 
 50         struct sockaddr_in serv_sockaddr,clie_sockaddr;
 51         socklen_t clie_sockaddr_len=sizeof(clie_sockaddr);
 52         bzero(&clie_sockaddr,sizeof(clie_sockaddr));
 53         bzero(&serv_sockaddr,sizeof(serv_sockaddr));
 54         serv_sockaddr.sin_family=AF_INET;
 55         serv_sockaddr.sin_port=htons(SERV_PORT);
 56         serv_sockaddr.sin_addr.s_addr=htonl(INADDR_ANY);
 57         //绑定listenfd套接字
 58         bind(listenfd,(struct sockaddr *)&serv_sockaddr,sizeof(serv_sockaddr));
 59         //进行监听
 60         listen(listenfd,128);
 61         //select转接内核负责监听,并返回请求连接的信息
 62         //client[]将所添加的监听文件描述符放入,以便搜索可连接的sock文件描述符
 63         int client[FD_SETSIZE];
 64         //readset用于添加读事件文件描述符oldset用于保存readset的设置
 65         //(调用select会将readset不满足的描述符删除)
 66         fd_set readset,oldset;
 67         //起初listenfd即为最大文件描述符
 68         nfds=listenfd;
 69         //maxno作为client[]的下标
 70         int maxno=-1;
 71         //用-1初始化client[]
 72         int i;
 73         for(i=0;i<FD_SETSIZE;i++){
 74                 client[i]=-1;
 75         }
 76         //初始化oldset,并添加listenfd文件描述符到oldset保存
 77         FD_ZERO(&oldset);
 78         FD_SET(listenfd,oldset);
 79         //每次监听都重新设置readset内的所需监听的文件描述符
 80         //request=select();查看readset中是否有我监听的那个客户端请求连接
 81         int request;
 82         while(1){
 83                 readset=oldset;
 84                 request=select(nfds+1,&readset,NULL,NULL,NULL);
 85                 if(request<0){//出错打印,退出
 86                         perror("select error\n");
 87                         exit(1);
 88                 }
 89         //判断是否有客户端发送请求,有就把客户端的文件描述符放到oldset中
 90                 if(FD_ISSET(listenfd,&readset)){
 91         //当listenfd在readset集合中则表示有对应的客户端请求连接,存在才接收,所以accpet不阻塞
 92                         connfd=accept(listenfd,(struct sockaddr *)&clie_sockaddr,&clie_sockaddr_len);
 93                         printf("connect sucessful\n");
 94                         for(i=0;i<FD_SETSIZE;i++){
 95         //将接收到对应的客户端的文件描述符放到自己定义的数组中,方便找到对应的文件描述符
 96                                 if(client[i]<0){
 97                                 client[i]=connfd;
 98                                 break;
 99                                 }
100                         }
101                         FD_SET(connfd,&oldset);//将connfd放到oldset保存
102                         if(connfd>nfds){        //因为nfds只存最大的文件描述符号
103                                 nfds=connfd;
104                         }
105                         if(i>maxno){//保证maxno是clinet[]最后一个元素的下标
106                                 maxno=i;
107                         }
108                         if(--requset==0){
109                                 continue;
110                         }
111                 }
112                 for(i=0;i<=maxno;i++){//检查client中文件描述符是否有发送数据
113                         if((sockfd=client[i])<0){
114                                 continue;
115                         }
116                         if(FD_ISSET(sockfd,&readset)){
117                                 if((n=read(sockfd,buf,sizeof(buf)))==0){
118                                         close(sockfd);
119                                         FD_CLR(sockfd,&oldset);//清除oldset中的connfd文件描述符
120                                 }else if(n>0){
121                                         printf("%s",buf);
122                                 }
123                 //如果后续没有需要执行操作的文件描述符,着退回select去,继续监听        
124                                 if(--request==0){
125                                         break;
126                                 }
127                         }
128                 }
129         }
130         close(listenfd);
131         return 0;
132 }

select特点:

  • 定义字符集,并设置监听的sock文件描述符,传入select()
  • 如果自定义的字符集没有接收到客户端的请求,则相应的fd位翻转为0
  • 因此用户需要在传入时,保存原先设置的文件描述集
  • 得到传出的集合,需要从文件描述集中自己判断,哪个满足条件
  • 因此用户需要再自定义对应的数组,自己进行判断

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值