IO复用,select,

一,IO复用的方法

IO复用的方法:select poll epoll

IO复用的功能

这里的描述符就是套接字

在这里插入图片描述
以前TCP服务器端和客户端
一个服务器端要连接10个客户端就需要创建10个线程
学校发书,所有人在图书馆排队领书没到你你就在等相当于阻塞,另一种到你了我给你打电话你再来领就不会出现阻塞(例子)
在程序中来讲就是我们将所有要关注的描述符全部注册到select上,select帮我们检测哪个描述符上有数据产生(有可能是一个或2,3个上同时有数据)

举个例子来使用select

检测键盘(键盘也是描述符),如果5秒内键盘上有数据的输入,我们就把数据读一下并打印出来,如果5秒内没有数据我们就打印个time out
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

select在Linux上和Windows都有
在这里插入图片描述
在这里插入图片描述

二,一个描述符的select代码

在这里插入图片描述

  1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<unistd.h>
  4 #include<string.h>
  5 #include<sys/select.h>
  6 
  7 #define STDIN 0//0就是从键盘输入
  8 int main()
  9 {
 10  int fd=STDIN;
 11  fd_set fdset;
 12  while(1)
 13  {
 14   FD_ZERO(&fdset);
 15   FD_SET(fd,&fdset);
 16   struct timeval tv={5,0};如果没有输入数据或者还没有到5秒就会在这里阻塞住
 17   int n=select(fd+1,&fdset,NULL,NULL,&tv);// 因为描述符是从0开始的,但fd_    set所置1的的标号是从1开始的(fd+1描述符的最大值+1这里只有一个)
 18   if(n<0)
 19   {
 20    printf("select err\n");
 21   }
 22   else if(n==0)
 23   {
 24    printf("time out\n");
 25   }
 26   else
 27   {
 28    if(FD_ISSET(fd,&fdset))
 29    {
 30     char buff[128]={0};
 31     read(fd,buff,128);
 32     printf("read:%s\n",buff);
 33    }
 34   }
 35  }
 36 }

三,多描述符的代码

将select用到tcp的服务器端
fd是文件描述符
阻塞
如果描述符是sockfd则调用accept()接受连接
在这里插入图片描述
memset(&saddr,0,sizeof(saddr));

封装的fds_init(); fds_add(); fds_del();都是用来对数组进行操作的

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

客户端是TCP有循环代码

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
多路 I/O 复用是一种在系统编程中常用的技术,它允许一个进程同时监视多个 I/O 事件,以提高程序的效率和响应能力。在 C 语言中,一个常用的多路 I/O 复用函数是 `select`。 `select` 函数的原型如下: ```c #include <sys/select.h> int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); ``` 参数说明: - `nfds`:监视的文件描述符集合中最大的文件描述符值加1。 - `readfds`:读事件的文件描述符集合。 - `writefds`:写事件的文件描述符集合。 - `exceptfds`:异常事件的文件描述符集合。 - `timeout`:超时时间,如果设置为 NULL,则阻塞直到有事件发生;如果设置为零,立即返回;如果设置为一个指定时间,超过该时间还没有事件发生,则返回。 `select` 函数的工作原理是将进程阻塞,直到监视的文件描述符集合中的任意一个文件描述符就绪(可读、可写或出现异常),或者超过指定的超时时间。 使用 `select` 函数进行多路 I/O 复用的一般步骤如下: 1. 创建并初始化文件描述符集合。 2. 将需要监视的文件描述符添加到相应的集合中。 3. 调用 `select` 函数进行阻塞等待。 4. 检查哪些文件描述符已经就绪。 5. 处理就绪的文件描述符。 需要注意的是,`select` 函数在每次调用时都会修改传入的文件描述符集合,因此在每次调用前需要重新初始化。 除了 `select`,还有其他的多路 I/O 复用函数,如 `poll` 和 `epoll`,它们在不同的操作系统中有不同的实现方式和特性,可以根据具体需求选择合适的函数。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值