IO复用-代替多线程-select,poll,epoll

select

//select(maxfd,rset,wset,eset,timeout);  r读,w写,e错误,timeout多长时间轮询一次
//有事件就返回
//rset-->  uL fds_bites[_FD_SIZE/(8*sizeof(long))]
//#define _FD_SIZE 1024 默认值1024,内核定义
//数组总大小_FD_SIZE/(8*sizeof(long)),每一位可以存(8*sizeof(long))个有效数据位,所以总共可以有_FD_SIZE个文件操作符
fd_set rfds , rset;

FD_ZERO(&rfds);

//fd[fd/32]=1<<(fd&31);
//fd&31 与fd%32的结果是一致的,目的是把数组响应位数的二进制第(fd)位置一
FD_SET(sockfd,&rfds);

int maxfd=sockfd;

while(1){
    rset=rfds;
    int nready=select(maxfd+1,&rset,NULL,NULL,NULL);//事件个数

    if(FD_ISSET(sockfd,&rset)){
        //定义clientaddr
        int clientfd=accept(sockfd,(struct sockaddr*)&clientaddr,&len);

        FD_SET(clientfd,&rfds);
        maxfd=clientfd;
          
    }
    int i=0;
    for(i=sockfd+1;i<=maxfd;i++){
        //(原本我的理解是此时rfds=rset的)这里为什么是rset而不是不可以是rfds
        //理解错误的地方,只有读事件即有客户端加入的时候,rfds=rset
        // select阻塞在写事件的时候,没有执行FD_SET,此时的rfds=rset不一致
        if(FD_ISSET(i,&rset)){
            // 读事件,发送
            //FD_CLR(i,&rfds);close(i);
            //第一层bit_set。第二层系统io
        }
    }
}

缺点:
内核每一次都需要拷贝rset,拷贝整个,有很多不需要;IO数量有限制;参数较多,许都需要单独维护

//poll代替select

struct pollfd fds[1024]={0};
fds[sockfd].fd=socket;
fds[sockfd].event=POLLIN;

int maxfd=sockfd;

while(1){
    int nready=poll(fds,maxfd+1,-1);

    if(fds[sockfd].revents & POLLIN){
        //定义clientaddr
        int clientfd=accept(sockfd,(struct sockaddr*)&clientaddr,&len);
        
        fds[clientfd].fd=clientfd;
        fds[clientfd].events=POLLIN;
        
        maxfd=clientfd;
    }
    int i=0;
    for(i=sockfd+1;i<=maxfd;i++){

        if(fds[i].revents & POLLIN)     //清空判断123

        fds[i].fd=-1;
        fds[i].events=0;
        close(i);
  
}

//epoll
//基于IO的划分 listenfd/clientfd 面向IO,IO越来越多,每个IO都要处理不同事件判断---符合正常思考
//基于事件划分(reactor,底层也是epoll) EPOLLIN/EPOLLOUT 面向事件,划分好事件,在事件里面处理不同的IO---代码不用很长

int epfd=epoll_create(1);//链表,1size没有意义,可以是任何数
struct epoll_event ev;
ev.data.fd=sockfd;
ev.events=EPOLLIN;
epoll_ctl(epfd,EPOLL_CTL_ADD,sockfd,&ev);

struct epoll_event events[1024];

while(1){
      int nready=epoll_wait(epfd,events,1024,-1);

      for(int i=0;i<nready;i++){
          int connfd=events[i].data.fd;
          //基于IO状态做判断 (if---accept else----recv+sendif(sockfd==connfd){
              xxx;
              ev.data.fd=clientfd;
              //epoll的水平触发(FT)和边沿触发(ET),默认是水平触发,边沿触发(EPOLLIN|EPOLLET)
              //水平触发的用处:先读TCP头部Length,在循环Length次接收数据
              ev.events=EPOLLIN;
              epoll_ctl(epfd,EPOLL_CTL_ADD,clientfd,&ev);
          }
          else if(events[i].events & EPOLLIN){
                  xxx;
                  if(count==0){
                      //注意:recv,send, epoll_ctl里面对应的都是connfd,而不是i
                      epoll_ctl(epfd,EPOLL_CTL_DEL,connfd,NULL);
                      close(connfd);
                  }
          }
      }
}

//连接池原理

#define BUFFER_LENGTH 128
struct conn_item{
    int fd;//对应的clientfd
    char buffer[];
    int idx;//每次拼接从哪里开始
};
struct conn_item connlist[1024]={0};

//第一步
connlist[clientfd].fd=clientfd;
memset(connlist[clientfd].buffer,0,BUFFER_LENGTH);
connlist[clientfd].idx=0;

//第二步
char* buffer=connlist[connfd].buffer;
int idx=connlist[connfd].idx;
int count=recv(connfd,buffer+idx,BUFFER_LENGTH-idx,0);

connlist[connfd].idx+=count;
send(connfd,buffer+idx,count,0);
posted @   流光最璀璨i  阅读(9)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
点击右上角即可分享
微信分享提示