2021-09-15

一、epoll 相关函数
记不住函数的声明的时候可以在使用man手册查看, man 2 epoll_+函数名字

epoll_create函数声明:
int epoll_create(int size);

功能:在内核中创建epoll事件表,创建一个实例
size:在内核中创建的epoll事件表的大小
return:返回epoll事件表的文件描述符

epoll_ctl函数声明:
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

功能:用于控制监听到的某个文件描述符上的事件,可以注册、修改、删除事件
epfd: 由epoll_create生成的epoll操作的文件描述符
op: 对fd文件描述符要进行的操作
EPOLL_CTL_ADD //注册
EPOLL_CTL_MOD //修改
EPOLL_CTL_DEL //删除
fd: 要进行的关联到epoll事件表的文件描述符
return : 成功返回 0,失败返回-1
struct epoll_event *event: event 是指向epoll_even结构体的一个指针,对结构体成员进行操作
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 */
       };

epoll_wait函数声明
int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout);

功能:轮询IO事件的发生,等待epoll事件从epoll实例中发生, 并返回事件以及对应文件描述符。
epfd:由epoll_create生成的epoll专用的文件描述符
struct epoll_event *events:用于回传代处理事件的数组

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 */
};
maxevents:每次能处理的最大事件数
timeout:等待IO事件发生的超时值
return:成功:返回发生的事件数;失败:-1

二、边沿触发和水平触发
epoll事件有两种模型,边沿触发:edge-triggered (ET), 水平触发:level-triggered (LT)
边沿触发仅触发一次,水平触发会一直触发。
水平触发(level-triggered):
socket接收缓冲区不为空 有数据可读 读事件一直触发
socket发送缓冲区不满 可以继续写入数据 写事件一直触发
边沿触发(edge-triggered):
socket的接收缓冲区状态变化时触发读事件,即空的接收缓冲区刚接收到数据时触发读事件
socket的发送缓冲区状态变化时触发写事件,即满的缓冲区刚空出空间时触发读事件
事件宏:
EPOLLIN : 表示对应的文件描述符可以读(包括对端SOCKET正常关闭);
EPOLLOUT: 表示对应的文件描述符可以写;
EPOLLPRI: 表示对应的文件描述符有紧急的数据可读(这里应该表示有带外数据到来);
EPOLLERR: 表示对应的文件描述符发生错误;
EPOLLHUP: 表示对应的文件描述符被挂断;
EPOLLET: 将 EPOLL设为边缘触发(Edge Triggered)模式(默认为水平触发),这是相对于水平触发(Level Triggered)来说的。
#define MAX_EVENTS 10 struct epoll_event ev, events[MAX_EVENTS];
int listen_sock, conn_sock, nfds, epollfd;

       /* Code to set up listening socket, 'listen_sock',              (socket(), bind(), listen()) omitted */

       // 创建epoll实例           epollfd = epoll_create1(0);

       if (epollfd == -1) {
           perror("epoll_create1");
           exit(EXIT_FAILURE);
       }

       // 将监听的端口的socket对应的文件描述符添加到epoll事件列表中           ev.events = EPOLLIN;
       ev.data.fd = listen_sock;
       if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listen_sock, &ev) == -1) {
           perror("epoll_ctl: listen_sock");
           exit(EXIT_FAILURE);
       }

       for (;;) {
           // epoll_wait 阻塞线程,等待事件发生               nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
           if (nfds == -1) {
               perror("epoll_wait");
               exit(EXIT_FAILURE);
           }

           for (n = 0; n < nfds; ++n) {
               if (events[n].data.fd == listen_sock) {
                   // 新建的连接                       conn_sock = accept(listen_sock,
                                      (struct sockaddr *) &addr, &addrlen);
                   // accept 返回新建连接的文件描述符                       if (conn_sock == -1) {
                       perror("accept");
                       exit(EXIT_FAILURE);
                   }
                   setnonblocking(conn_sock);
                   // setnotblocking 将该文件描述符置为非阻塞状态
                   ev.events = EPOLLIN | EPOLLET;
                   ev.data.fd = conn_sock;
                   // 将该文件描述符添加到epoll事件监听的列表中,使用ET模式                       if (epoll_ctl(epollfd, EPOLL_CTL_ADD, conn_sock,
                               &ev) == -1)
                       perror("epoll_ctl: conn_sock");
                       exit(EXIT_FAILURE);
                   }
               } else {
                   // 使用已监听的文件描述符中的数据                       do_use_fd(events[n].data.fd);
               }
           }
       }

示例代码:
//epoll 事件队列
static struct epoll_event events[EPOLL_SIZE];
init();
while (1)
{
//epoll_events_count表示就绪事件的数目
int epoll_events_count = epoll_wait(ep_fd, events, sizeof(events), -1);
if (epoll_events_count < 0)
{
perror(“epoll fail”);
break;
}
cout << "epoll_events_count = " << epoll_events_count << endl;

//处理这些就绪事件
for (int i = 0; i < epoll_events_count; i++)
{
  int sock_fd = events[i].data.fd;
  if (sock_fd == listen_fd)
  {
    struct sockaddr_in client_addr;
    // (struct sockaddr *)&client_addr, (socklen_t *)sizeof(client_addr)
    int client_fd = accept(sock_fd, NULL, NULL);
    if (client_fd < 0)
    {
      perror("accept fail");
      exit(-1);
    }
    cout << "client connection from: "
         << inet_ntoa(client_addr.sin_addr) << ":"
         << ntohs(client_addr.sin_port) << ", client_fd = "
         << client_fd << endl;
    addfd(ep_fd, client_fd, true);
    //用标准库模板list储存连接的fd
    client_list.push_back(client_fd);
    cout << "聊天室里新增用户" << client_fd << endl;
    cout << "现在总共有: " << client_list.size() << "位用户在聊天室" << endl;

    char buf[MAX_SIZE];
    memset(buf, 0, sizeof(buf));
    sprintf(buf, "欢迎来到在线聊天室! 你的ID是: <Client %d>", client_fd);
    int ret = send(client_fd, buf, sizeof(buf), 0);
    if (ret < 0)
    {
      perror("send error");
      Close();
      exit(-1);
    }
  }
  else
  {
    int ret = sendMessage(sock_fd);
    if (ret < 0)
    {
      perror("error");
      Close();
      exit(-1);
    }
  }
}

}
Close();

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值