I/O复用方法——select

I/O复用

I/O复方法:select poll epoll

作用:

  • 可以同时监听多个文件描述符,
    使服务器在不引入多进程的情况下同时处理多个文件描述符。

网络程序需要使用 I/O 复用技术:

◼ TCP 服务器同时要处理监听套接字和连接套接字。
◼ 服务器要同时处理 TCP 请求和 UDP 请求。
◼ 程序要同时处理多个套接字。
◼ 客户端程序要同时处理用户输入和网络连接。
◼ 服务器要同时监听多个端口。

I/O 复用虽然能同时监听多个文件描述符,但它本身是阻塞的。并且当
多个文件描述符同时就绪时,如果不采取额外的措施,程序就只能按顺序依处理其中的每一个文件描述符,这使得服务器看起来好像是串行工作的。如果要提高并发处理的能力,可以配合使用多线程或多进程等编程方法。

socket

socket 有读事件产生:

socket 通信的对方关闭连接。
监听socket 上有新的链接请求。
socket 上有未处理的错误。

select 系统调用
#include<sys/select.h>

select 返回值为就绪的文件描述符的数目。
如果在超时时间内没有任何文件描述符就绪,select 将返回 0。
select 失败是返回-1。
n>0,有n个文件描述符就绪。

处理三种事件:读事件,写事件,和异常事件。

集合 fd_set 将描述符添加到集合中,总大小默认为 1024 个位。

int FD_ISSET(int fd, fd_set *fdset); // 测试 fdset 的位 fd 是否被设置。

在这里插入图片描述
适用于描述符数目不多的场景。

  1 #include<stdio.h>
  2 #include<string.h>
  3 #include<unistd.h>
  4 #include<sys/select.h>
  5 #include<sys/time.h>
  6 
  7 int main()
  8 {
  9     int fd=0;// stdin 0;  stdout 1; stderr 2 将键盘描述符的值赋给fd
 10 
 11     fd_set fdset;//定义集合,用来收集描述符,被selet检测
 12 
 13     while(1)//循环检测
 14     {
 15         struct timeval tv={5,0};//设置超时时间 5秒
 16         FD_ZERO(&fdset); //清空整个集合中的每个位  为0
 17         FD_SET(fd,&fdset);//将描述符添加到集合           只有一个描述符  如果有多个描述符 循环
 18 
 19         int n=select(fd+1,&fdset,NULL,NULL,&tv);//fd+1 描述符最大值+1 只用检测有数据的地方
 20         if(n==-1)
 21         {
 22             printf("select err\n");
 23             break;
 24         }
 25         else if(n==0)
 26         {
 27             printf("time out\n");
 28         }
 29         else
 30         {
 31             if(FD_ISSET(fd,&fdset))//检测描述符上是否有数据
 32             {
 33                 char buff[128]={0};
 34                 read(fd,buff,127);
 35                 printf("read:%s\n",buff);
 36             }
 37         }
 38     }
 39 
 40 }

在这里插入图片描述

TCP

TCP服务器

1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<unistd.h>
  4 #include<string.h>
  5 #include<sys/socket.h>
  6 #include<netinet/in.h>
  7 #include<arpa/inet.h>
  8 #include<assert.h>
  9 #include<sys/select.h>
 10 #include<sys/time.h>
 11 
 12 #define MAX 10
 13 int socket_init();
 14 
 15 void fds_init(int fds[])//清空描述符  全置为-1
 16 {
 17     if(fds==NULL)
 18     {
 19         return;
 20     }
 21     for(int i=0;i<MAX;i++)
 22     {
 23         fds[i]=-1;
 24     }
 25 }
 26 void fds_add(int fd,int fds[])//向空闲的集合中添加描述符
 27 {
 28     if(fds==NULL)
 29     {
 30         return;
 31     }
 32     for(int i=0;i<MAX;i++)
 33     {
 34         if(fds[i]==-1)
 35         {
 36             fds[i]=fd;
 37             break;
 38         }
 39     }
 40 }
 41 void fds_del(int fd,int fds[])//将描述符从集合中移除
 42 {
 43     if(fds==NULL)
 44     {
 45         return;
 46     }
 47     for(int i=0;i<MAX;i++)
 48     {
 49         if(fds[i]==fd)
 50         {
 51             fds[i]=-1;
 52             break;
 53         }
 54     }
 55 }
 56 int main()
 57 {
 58     int sockfd=socket_init();
 59     assert(sockfd!=-1);
 60     int fds[MAX];//将描述符存放到数组中
 61 
 62     fds_init(fds);//初始化数组值都为-1,代表数组是空的
 63 
 64     fds_add(sockfd,fds);//将监听套介子添加到数组
 65 
 66     fd_set fdset;//集合 ->select
 67 
 68     while(1)
 69     {
 70         FD_ZERO(&fdset);//清空集合
 71         int maxfd=-1;//记录描述符最大值
 72 
 73         for(int i=0;i<MAX;i++)
 74         {
 75             if(fds[i]==-1)
 76             {
 77                 continue;
 78             }
 79             FD_SET(fds[i],&fdset);//把有效描述符添加到集合
 80             if(maxfd<fds[i])
 81             {
 82                 maxfd=fds[i];
 83             }
 84         }
 85         struct timeval tv={5,0};//超时时间 5秒
 86         int n=select(maxfd+1,&fdset,NULL,NULL,&tv);
 87         if(n==-1)
 88         {
 89             continue;
 90         }
 91         else if(n==0)
 92         {
 93             printf("time out\n");
 94             continue;
 95         }
 96         else
 97         {
 98             for(int i=0;i<MAX;i++)//遍历所有描述符找到就绪的
 99             {
100                 if(fds[i]==-1)
101                 {
102                     continue;
103                 }
104                 if(FD_ISSET(fds[i],&fdset))
105                 {
106                     if(fds[i]==sockfd)   //监听套接子  调用accept
107                     {
108                     struct sockaddr_in caddr;
109                     int len=sizeof(caddr);
110 
111                     int c=accept(sockfd,(struct 	sockaddr*)&caddr,&len);
112                     if(c<0)
113                     {
114                         continue;
115                     }
116                     printf("accept c=%d\n",c);
117                     fds_add(c,fds);
118                     }
119                     else           //连接套接子  调用recv
120                     {
121                         char buff[128]={0};
122                         int num=recv(fds[i],buff,127,0);
123                         if(num<=0)
124                         {
125                             close(fds[i]);
126                             fds_del(fds[i],fds);//从数组中删除不用的描述符
127                             printf("cilent close\n");
128                             continue;
129                         }
130                         printf("read:%s\n",buff);
131                         send(fds[i],"ok",2,0);
132                     }
133                 }
134             }
135         }
136     }
137 }
138 
139 int socket_init()
140 {
141     int sockfd=socket(AF_INET,SOCK_STREAM,0);
142     if(sockfd==-1)
143     {
144         return -1;
145     }
146 
147     struct sockaddr_in saddr;
148     memset(&saddr,0,sizeof(saddr));
149     saddr.sin_family=AF_INET;
150     saddr.sin_port=htons(6000);
151     saddr.sin_addr.s_addr=inet_addr("127.0.0.1");
152 
153     int res=bind(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
154     if(res==-1)
155     {
156         return -1;
157     }
158 
159     res=listen(sockfd,5);
160     if(res==-1)
161     {
162         return -1;
163     }
164     return sockfd;
165 }
                                                                                               
                                                                                                                                                                   

TCP客户端

1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<unistd.h>
  4 #include<string.h>
  5 #include<assert.h>
  6 #include<sys/socket.h>
  7 #include<netinet/in.h>
  8 #include<arpa/inet.h>
  9 
 10 int main()
 11 {
 12     int sockfd=socket(AF_INET,SOCK_STREAM,0);//创建套接字
 13     assert(sockfd!=-1);
 14 
 15     //bind()//可以绑定,但是一般不绑定
 16 
 17     struct sockaddr_in saddr;
 18     memset(&saddr,0,sizeof(saddr));
 19     saddr.sin_family=AF_INET;
 20     saddr.sin_port=htons(6000);
 21     saddr.sin_addr.s_addr=inet_addr("127.0.0.1");
 22 
 23     int res=connect(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));//三次握手
 24     assert(res!=-1);
 25     while(1)
 26     {
 27         char buff[128]={0};
 28         printf("input:\n");
 29         fgets(buff,128,stdin);
 30 
 31         if(strncmp(buff,"end",3)==0)
 32         {
 33             break;
 34         }
 35         send(sockfd,buff,strlen(buff),0);
 36 
 37         memset(buff,0,128);
 38         recv(sockfd,buff,127,0);
 39         printf("buff=%s\n",buff);
 40     }
 41     close(sockfd);
 42 }
~                                  

在这里插入图片描述

  1. 对方关闭描述符
  2. 对方发数据
  3. 对方链接

select 都会返回。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值