Linux关于IO复用(select使用)

I/O复用网络应用场合

当客户处理多个描述字 一个客户同时处理多个套接口 如果一个tcp服务器既要处理监听套接口,又要处理连接套接口 如果一个服务器既要处理TCP,又要处理UDP

select函数作用

这个函数允许进程指示内核等待多个事件中的任一个发生,并仅在一个或多个事件发生或经过某指定的时间后才唤醒进程

select函数什么情况下返回

作为一个例子,我们可以调用函数select并通知内核仅在下列情况发生时才返回: 集合{1,4,5}中的任何描述子准备好读 或 集合{2,7}中的任何描述字准备好写或 集合{1,4}中的任何描述字有异常条件待处理或 已经过了10.2秒 也就是说,通知内核我们对哪些描述字感兴趣 (读、写或异常条件)以及等待多长时间。

select函数

包含头文件<sys/select.h><sys/socket.h> 功能:提供了即时响应多个套接的读写事件 原型:     int select(int maxfdp1,fd_set *readset,fd_set *writeset,fd_set *except,const struct timeval *timeout); 参数 maxfdp1:等待最大套接字值加1,(等待套接字的数量) readset:要检查读事件的容器 writeset:要检查写事件的容器 timeout:超时时间 返回值:返回触发套件接字的个数

select函数使用

我们还是模拟CS架构,来使用select

下面是服务器代码:

#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <sys/types.h>  
#include <sys/wait.h>
#include <sys/socket.h>  
#include <netinet/in.h> /* for struct sockaddr_in*/  
#include <sys/errno.h>  
#include <signal.h> 
#include <sys/select.h>


//关于IO复用服务器的socket
void error_exit(char *name)
{
   perror(name);
   exit(-1);
}

int main(int argc,char *argv[])
{
   int sockfd=socket(AF_INET,SOCK_STREAM,0);
   if(sockfd<0)
   {
        error_exit("create error");
   }
   //绑定地址(ip和端口号)
   struct sockaddr_in svraddr;
   memset(&svraddr,0,sizeof(svraddr));
   svraddr.sin_family=AF_INET;
   svraddr.sin_addr.s_addr=INADDR_ANY;
   //svraddr.sin_addr.s_addr=inet_addr("127.0.0.1");第二种写法
   svraddr.sin_port=htons(5555);
   int ret=bind(sockfd,(struct sockaddr*)&svraddr,sizeof(svraddr));
   if(ret<0)
   {
      error_exit("bind error");
   }
   //设置监听参数back login 半连接数最大
   ret=listen(sockfd,1024);
   if(ret<0)
   {
      error_exit("listen error");
   }
   //添加service到fd
   fd_set listen_set,temp_set;
   FD_ZERO(&temp_set);
   FD_ZERO(&listen_set);
   FD_SET(sockfd,&listen_set);

   int maxfd=sockfd; 
   struct sockaddr removeaddr;
   int addr_len = sizeof(removeaddr);
   //定义一个属于去保存accept fd,FD_SETSIZE函数能够监听文件描述符最大值
   int fds[FD_SETSIZE]={0};
   int i=0;
   for(;i<FD_SETSIZE;i++)
   {
       fds[i]=-1;
   }
   char buf[1024]={0};
   while(1)
   {
        temp_set=listen_set;
        int nevent=select(maxfd+1,&temp_set,NULL,NULL,NULL);
        if(nevent==0)
        {
             printf("timeout\n");
             continue;
        }
        else if(nevent<0)
        {
            error_exit("select error");
        }
        if(FD_ISSET(sockfd,&temp_set))   
        {
             int fd=accept(sockfd,(struct sockaddr *)&removeaddr,&addr_len);
              if(fd<0)
              {
                 error_exit("accept error");
              }
              printf("new connection  %d…\n",fd);
              //把accept添加到监听集
              FD_SET(fd,&listen_set);
              //数量改变
              maxfd=maxfd>fd?maxfd:fd;

              for(i=0;i<FD_SETSIZE;i++)
              {
                   if(fds[i]==-1)
                   {
                        fds[i]=fd;
                        break;
                   }
              }
        }
        for(i=0;i<FD_SETSIZE;i++)
        {
               if(fds[i]==-1)
               { 
                   continue;
               }
               if(FD_ISSET(fds[i],&temp_set))
               {
                  memset(buf,0,1024);
                  int rdsize=read(fds[i],buf,1024);
                  if(rdsize<=0)
                  {
                       printf("close %d\n",fds[i]);
                       //对方socket关闭,或者出错,把accept,fd移出监听集合
                       close(fds[i]);
                       FD_CLR(fds[i],&listen_set); 
                       fds[i]=-1;
                  }
                  else
                  {
                      printf("read buf =%s,%d\n",buf,fds[i]);
                  }
               }
        }
   }
   return 0;
}

下面是客户端代码,写入数据hello

#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <sys/types.h>  
#include <sys/wait.h>
#include <sys/socket.h>  
#include <netinet/in.h> /* for struct sockaddr_in*/  
#include <sys/errno.h>  
#include <signal.h> 

//关于客户端的socket
void error_exit(char *name)
{
   perror(name);
   exit(-1);
}
int main(int argc,char *argv[])
{
   if(argc<3)
   {
        printf("run program+ip+port\n");
        return-1;
   }
   int sockfd=socket(AF_INET,SOCK_STREAM,0);
   if(sockfd<0)
   {
        error_exit("create error");
   }
   //连接服务器,设置服务器的地址(ip和端口)
   struct sockaddr_in svraddr;
   memset(&svraddr,0,sizeof(svraddr));
   svraddr.sin_family=AF_INET;
   svraddr.sin_addr.s_addr= inet_addr(argv[1]);
   svraddr.sin_port=htons(atoi(argv[2]));
   int ret =connect(sockfd,(struct sockaddr *)&svraddr,sizeof(svraddr));
   if(ret<0)
   {
      error_exit("connect error");
   }

   write(sockfd,"hello",strlen("hello"));
   sleep(5); 
   close(sockfd);
   return 0;
}

先执行服务器代码:

等待中......

执行客户端代码:

 

查看服务端结果如下:

总结:成功读出来数据,证明我们输入成功,并且把fd读出来,解释一下为何从fd为何从4开始,我们知道系统中有0,1,2分别代表标准输入、标准输出和标准错误信息输出,总共3个,所以我们创建的fd从4开始计算

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值