Netty学习之理解epoll

目录

epoll原理

epoll_create

该函数生成一个epoll专用的文件描述符。

int epoll_create(int size);

  • size:epoll上能关注的最大文件描述符数

epoll_ctl

用于控制某个epoll文件描述符事件,可以注册、修改、删除

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

  • epfd:epoll_create生成的epoll专用的文件描述符

  • op:

    • EPOLL_CTL_ADD 注册

    • EPOLL_CTL_MOD 修改

    • EPOLL_CTL_DEL 删除

  • fd:关联的文件描述符

  • event:告诉内核要监听什么事件

代码块
typedef union epoll_data {
   void *ptr;
   int fd;
   uint32_t  u32;
   uint64_t  u64;
} epoll_data_t;
​
struct epoll_event {
   uint32_t event;
   epoll_data data;
}
​
events:
EPOLLIN 读
EPOLLOUT 写
EPOLLERR 异常
 

epoll_wait

等待IO事件发生

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

  • epfd:epoll_create生成的epoll专用的文件描述符

  • events:用于回传待处理事件的数组(传出参数)

  • maxevents:告诉内核这个events的大小(events数组的大小肯定是有上限的)

  • timeout:超时时间

    • -1:永久阻塞

    • 0:立即返回

    • >0:超时时间

代码示例

#include <ctype.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <sys/epoll.h>int main(int argc, const char* argv[]) 
{
  if (argc < 2) 
  {
    printf("eg: ./a.out port\n");
    exit(1);
  }
  struct sockaddr_in serv_addr;
  socklen_t serv_len = sizeof(serv_addr);
  int port = atoi(argv[1]);
​
  int lfd = socket(AF_INET, SOCK_STREAM, 0);
  memset(&serv_addr, 0, serv_len);
  serv_addr.sin_family = AF_INET;
  serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
  serv_addr.sin_port = htons(port);
​
  bind(lfd, (struct sockaddr*)&serv_addr, serv_len);
  listen(lfd, 36);
  printf("start accept...........\n");
​
  struct sockaddr_in client_addr;
  socklen_t cli_len = sizeof(client_addr);
​
    //创建epoll树根节点
  int epfd = epoll_create(2000);
  //初始化epoll树
  struct epoll_evnet ev;
  ev.events = EPOLLIN;
  ev.data.fd = lfd;
  epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &ev);
​
  struct epoll_event all[2000];
  while (1)
  {
    int ret = epoll_wait(epfd, all, sizeof(all)/sizeof(all[0]), -1);
    for (int i =0; i < ret; i++)
    {
      int fd = all[i].data.fd;
      //判断是否有新连接
      if (fd == lfd) {
        //接收连接请求
        int cfd = accept(lfd, (struct sockaddr*)&client_addr, &cli_len);
        if (cfd == -1) 
        {
          perror("accept error");
          exit(1);
        }
        struct epoll_event temp;
        temp.events = EPOLLIN;
        temp.data.fd = cfd;
        epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &temp);
        //打印客户端信息
        char ip[64] = {0};
        printf("new client ip: %s, port: %d\n", inet_ntop(AF_INET, &client_addr.sin_addr.s_addr, ip, sizeof(ip)), ntohs(client_addr.sin_port));
      } 
      else 
      {
               //处理已连接的客户端发送过来的数据
        if (!all[i].events & EPOLLIN) 
        {
          continue;
        }
        //读数据
        char buf[1024] = {0};
        int len = recv(fd, buf, sizeof(buf), 0);
        if (len == -1) 
        {
          perror("recv error");
          exit(1);
        }
        else if (len == 0)
        {
          printf("client disconnected\n");
          //将fd从树上删除
          ret = epoll_ctl(epfd, EPOLL_CTL_DEL, fd, NULL);
          if (ret == -1)
          {
            perror("epoll-ctl del error");
            exit(1);
          }
          close(fd);
        }
        else 
        {
          printf("recv buf:%s\n", buf);
          write(fd, buf, len);
        }
      }
​
    }
​
  };
  close(lfd);
  return 0;
}
 

参考资料

http://www.man7.org/linux/man-pages/man7/epoll.7.html

https://www.bilibili.com/video/av44660437/?p=8

https://www.jianshu.com/p/31cdfd6f5a48

猜你喜欢

转载自www.cnblogs.com/yeyang/p/12580820.html