IO多路转接技术(select)

linux相关的IO编程,进程,线程,网络编程等;方法及代码解析:https://pan.baidu.com/s/1IVn3xfCaRPnQklOrcvsQ_g 密码:vi76

IO多路转接技术之select

  • IO多路转接技术select/poll/epool:解决服务器一次只能处理一个客户端的问题,及并发处理client端的请求。IO多路则可以间接实现并行处理多个客户端的请求,及同时处理来自client的请求。
  1. 第一种:select/poll
    图画形象解释select/poll的功能l

2.第二种:epoll
图画形象解释epoll功能

  • IO多路转接技术的实现:
  1. 构建一张存储客户端套接字(文件)描述符的列表

  2. 调用具有阻塞功能的监听函数对文件描述符表进行监听,一旦有对应的描述符所代表的client有请求时,立即将该描述符返回。

  3. 返回时,会告诉进程有多少描述符要进行IO操作(即client请求处理)。

  4. IO多路转接-select函数原型;


    int select(int nfds,
    fd_set *readfds,
    fd_set *writefds,
    fd_set *exceptfds,
    struct timeval *timeout);
    参数分析:
    int nfds://第1024(最高位)个文件描述符,即第1023个
    fd_set *readfds://读取描述符流指针
    fd_set *writefds://写描述符流指针
    fd_set *exceptfds://异常描述符流指针
    struct timeval timeout://设值或NULL,NULL时为永久阻塞,检测到fd变化时返回
    结构体参数:
    struct timeval {
    long tv_sec; /
    seconds /
    long tv_usec; /
    microseconds */
    };
    文件描述符操作函数及类型;

select函数的优点:跨平台
缺点:

2.select 工作过程分析(文件描述符0,1,2分别默认为标准输入,标准输出,标准出错)
多个客户端:A B C D E ...连接到服务器,分别对应文件描述符3,4,100,101,102,103...

1)变量的定义:
fd_set reads,temp;//用户空间
FD_SET(3,&reads);//

2)初始化select(103+1,&reads,NULL,NULL,NULL);
103+1为内核拿到的初始化文件描述符表的大小,从3...103均制值为1
3)当内核遍历描述符表时,内核检测对应描述符的客户端有数据发来时,对应描述符的内容不变乃为1,否则置为0
.如下图示:fd_set表的变化,左边为初始化得到的表;右边为内核遍历检测完成后,修改之后的表,该表将会印射到用户空间的reads所指的表。


然后服务器就遍历新的reads表,对应的描述符为1则表明该描述符对应的客户端发来了数据,为0则没有发来数据。
3,select多路转接代码
1)伪码

int main()
{
    //socket
    int lfd =socket();
    //bind
    bind();
    //listen
    listen();
    
    //create socket list
    fd_set reads,temp;
    //init
    fd_zero(&reads);
    //listen lfd add to reads
    fd_set(lfd,&reads);
    int maxfd=lfd;
    
    while(1)
    {
        //select委托检测
        temp=reads;
    int ret=select(maxfd+1,&reads,NULL,NULL,NULL);
      //检测lfd是否在以temp为高位地址的监听表中
      if(fd_isset(lfd,&temp))
      {
        //在则,接受新连接
        int cfd =accept();
        //cfd加入读集合
        fd_set(cfd,&reads);
        //更新maxfd
        maxfd=(maxfd<cfd?cfd:maxfd);
      }
      //客户端发送数据
      for(int i=lfd+1;i<=maxfd;i++)
      {
        if(fd_isset(i,&temp)
        {
            int len =read();
            if(len==0)
            {
            
                //cfd从读集合中删除
                fd_clr(i,&reads);
            }
            write();
        }
     }
  }
}

2)实现多客户端连接的server代码:
建立select_server.c文件;内容如下:

#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<sys/un.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include <sys/select.h>
#include <sys/time.h>

#define MAX_LISTEN 100

int main()
{       
        int server_fd,client_fd;
        struct sockaddr_in server,client;
        int client_len=sizeof(client);
        //initial socket object
        server.sin_family = AF_INET;
        server.sin_addr.s_addr =htonl(INADDR_ANY);
        server.sin_port =htons(8888);
        
        //socket
        server_fd=socket(AF_INET,SOCK_STREAM,0);
        if(server_fd == -1)
        {
        printf("socket error\n");
        exit(1);
        }
        printf("server_fd=%d\n",server_fd);
        //bind
        if(bind(server_fd,(struct sockaddr *)&server,sizeof(server))==-1)
        {
        printf("bind error\n");
        close(server_fd);
        exit(1);
        }
        
        //listen
        if(listen(server_fd,MAX_LISTEN)==-1)
        {
        printf("listen error\n");
        close(server_fd);
        exit(1);
        }
        printf("listening...........\n");
        
        //max fd
        int maxfd=server_fd;
        //create socket list
        fd_set reads,temp;
        //init
        FD_ZERO(&reads);
        //listen server add to reads
        FD_SET(server_fd,&reads);

        while(1)
        {
        //select
        temp=reads;
        int ret=select(maxfd+1,&temp,NULL,NULL,NULL);
        if(ret ==-1)
        {
        printf("select error\n");
        close(server_fd);
        exit(1);
        }
        //verify server_fd whether listening
        if(FD_ISSET(server_fd,&temp))
        {
        //accept
        client_fd=accept(server_fd,(struct sockaddr *)&client,&client_len);
        if(client_fd==-1)
        {
        printf("accept error\n");
        close(server_fd);
        exit(1);
        }
        char ip[64];
        printf("new client IP:%s  Port: %d\n",inet_ntop(AF_INET,&client.sin_addr.s_addr,ip,sizeof(ip)),ntohs(client.sin_port));     
        //add client_fd into reads
        FD_SET(client_fd,&reads);
        //update maxfd
        maxfd = (maxfd <=client_fd ? client_fd : maxfd);
        }
        //operation send to client
        for(int i=server_fd+1;i<=maxfd;++i)
        {
        if(FD_ISSET(i,&temp))
        {
        char read_buf[1024] ={0};
        int len=recv(i,read_buf,sizeof(read_buf),0);
        if(len==-1)
        {
        printf("read error\n");
        exit(1);
        }
        else if(len==0)
        {
        printf("client exit\n");
        close(i);
        //delete client_fd from reads
        FD_CLR(i,&reads);
        }
        else
        {
        printf("client says :%s\n",read_buf);
        send(i,"hello client",12,0);
        printf("send to client\n");
        }
        }
        }
        }
        close(server_fd);
        return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值