IO多路转接之epoll

为处理大量句柄而作改进的poll

创建epoll模型

int epoll_create(int size);

在内核里创建epoll模型:

  1. 创建红黑树
  2. 创建队列
  3. 建立底层回调机制

注册事件

int epoll_ctl(int epfd,int op,int fd,struct epoll_event *event);

epfd:epoll_create的返回值。

op:表示处理动作。

  1. EPOLL_CTL_ADD:注册新的fd到epfd中。
  2. EPOLL_CTL_MOD:修改已经注册的fd的监听事件。
  3. EPOLL_CTL_DEL:从epfd中删除一个fd。
epoll_event结构体:
typedef union epoll_data {
               void        *ptr;
               int          fd;
               uint32_t     u32;
               uint64_t     u64;
           } epoll_data_t;

           struct epoll_event {
               uint32_t     events;      /* Epoll events */
               epoll_data_t data;        /* User data variable */
           };

events常用宏:EPOLLIN(可读)和EPOLLOUT(可写);


#include <sys/epoll.h>

int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout);

编写epoll服务器

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<sys/epoll.h>


#define MAX 128

int startup(int port){
  int sock=socket(AF_INET,SOCK_STREAM,0);
  if(sock<0){
    perror("socket\n");
    exit(2);
  }

  int opt=1;
  setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
  struct sockaddr_in local;
  local.sin_family=AF_INET;
  local.sin_port=htons(port);
  local.sin_addr.s_addr=htonl(INADDR_ANY);
  if(bind(sock,(struct sockaddr*)&local,sizeof(local)<0)){
    perror("bind\n");
    exit(4);
  }
  if(listen(sock,5)<0){
    perror("listen\n");
    exit(2);
  }
  return sock;
}

void  serviceIO(int epfd,struct epoll_event* revs,int num,int listen_sock){
  int i=0;
  struct epoll_event ev;
  for(;i<num;i++){
    int fd=revs[i].data.fd;
    if(revs[i].events&EPOLLIN){//如果是可读事件
      //read;
      if(fd==listen_sock){
        struct sockaddr_in client;
        socklen_t len=sizeof(client);
        int new_fd=accept(fd,(struct sockaddr*)&client,&len);//建立连接
        if(new_fd<0){
          perror("accept");
          continue;
        }
        printf("get a new client[%s:%d]\n",inet_ntoa(client.sin_addr),ntohs(client.sin_port));
        ev.events=EPOLLIN;
        ev.data.fd=new_fd;
        epoll_ctl(epfd,EPOLL_CTL_ADD,new_fd,&ev);
      }else {
        char buf[1024];
        ssize_t s=read(fd,buf,sizeof(buf));
        if(s>0){
          buf[s]=0;
          printf("%s\n",buf);
          ev.events=EPOLLOUT;
          ev.data.fd=fd;
          epoll_ctl(epfd,EPOLL_CTL_MOD,fd,&ev);
        }else if(s==0){
          printf("client quit\n");
          close(fd);
          epoll_ctl(epfd,EPOLL_CTL_DEL,fd,NULL);
        }else{
          perror("read");
          close(fd);
          epoll_ctl(epfd,EPOLL_CTL_DEL,fd,NULL);
        }
      }
    }
    if (revs[i].events&EPOLLOUT){
      //write
      const char* msg="HTTP/1.0 200 ok\r\n\r\n<html><h1>EPOLL SUCESS</h1><html>\r\n";;
      write(fd,msg,strlen(msg));
      epoll_ctl(fd,EPOLL_CTL_DEL,fd,NULL);
    }
    close(epfd);
  }
}
int main(int argc,char* argv[]){
  if(argc!=2){
    printf("usage:%s [port]\n",argv[0]);
    return 1;
  }
  //创建监听套接字
  int listen_sock=startup(atoi(argv[1]));
  //创建epoll
  int epfd=epoll_create(256);
  if(epfd<0){
    perror("epoll_create");
    return 5;
  }
  //添加监听到的文件描述符以及处理动作
  struct epoll_event ev;
  ev.events=EPOLLIN;
  ev.data.fd=listen_sock;
  epoll_ctl(epfd,EPOLL_CTL_ADD,listen_sock,&ev);
  struct epoll_event revs[MAX];
  int size=0;

  for(;;){
    switch(size=epoll_wait(epfd,revs,MAX,-1)){//等待文件描述符对应的事件发生
      case -1:
        perror("epoll_wait");
        break;
      case 0:
        printf("timeout\n");
        break;
      default:
        serviceIO(epfd,revs,size,listen_sock);
        break;
    }
  }
}

epoll优点:

文件描述符数目无上限。

一旦某个文件描述符就绪,内核会迅速采用回调机制迅速激活这个文件描述符。

当文件描述符就绪,会被放入就绪队列中。事件复杂度为O(1)

内存映射机制:内核直接将就绪队列通过mmap方式映射到用户态。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值