[Linux网络编程]07-epoll函数原理解析和使用,ET模式和LT模式,epoll反应堆模型了解

一.epoll函数解析

其本质是红黑树和链表

  1. int epoll_create(int size);
    作用:创建一个epoll红黑树
    size:创建的红黑树的监听节点数量(仅供内核参考)
    返回值:
    成功–返回新创建的红黑树的根节点的fd;失败 -1 error

  2. int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
    作用:对epoll进行操作,ADD,MOD,DEL等

epfd:epoll_create函数的返回值 epfd
fd:待监听的fd
op:对该监听红黑树所要做的操作

EPOLL_CTL_ADD 添加fd到监听红黑树
EPOLL_CTL_MOD 修改fd在监听红黑树上的监听事件
EPOLL_CTL_DEL 将一个fd从监听红黑树上摘下(取消监听)

event:本质是 struct epoll_event 结构体地址
event结构体成员:
(1)events:EPOLLIN、EPOLLOUT、EPOLLERR
(2)data:联合体(内部: int fd:对应监听事件的fd ,void *ptr:泛型指针 )

返回值
成功0,失败-1 error

3. int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
作用:监听红黑树上节点的对应事件,以events作为传出参数,返回所有被监听到的文件描述符数组。
epfd:epoll_create函数的返回值 epfd
events:传出参数,一个数组[],用于存放满足监听条件的那些fd结构体
maxevents:数组元素的总个数 struct epoll_event events[1024]
timeout:
-1:阻塞
0:不阻塞
>0:超时时间(毫秒)

返回值:
>0:满足监听的总个数,可以用作循环上限
0:没有fd满足监听的事件
-1:失败,error

在这里插入图片描述

二.epoll实现多路IO转接

核心思路

  1. socket()、bind()、listen() 初始化socket,返回lfd(监听所用socket)
  2. epoll_create创建红黑树,它的返回值就是树的根节点
  3. epoll_ctl将listenfd添加到树上
  4. 循环epoll_wait进行监听,它的返回值是满足监听的总个数,所以以它的返回值为遍历上限去判断事件
  5. 如果它返回的数组中data.fd等于lfd,那么就accept去连接客户端 并将新的cfd加入树中
  6. 如果不是lfd,就说明有读事件发生,就去判断读到的返回值,<0是出错 ==0是客户端关闭(这两个都要去将该cfd从树中移除 并close),>0就处理数据然后写回
#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/epoll.h>
#include <fcntl.h>

#define OPEN_MAX 5000
#define SERVE_PORT 9527

int main()
{
   
   
    // 所需要的变量
    int lfd, cfd, efd, ret, wait_ret, i, sockfd, len;
    char buf[1024];
    // 地址结构体
    struct sockaddr_in serve_addr, client_addr;
    socklen_t client_addr_len;
    serve_addr.sin_family = AF_INET;                // IPV4
    serve_addr.sin_port = htons(SERVE_PORT);        // 绑定端口
    serve_addr.sin_addr.s_addr = htonl(INADDR_ANY); // 绑定ip(ANY系统自动分配)

    // 创建socket
    lfd = socket(AF_INET, SOCK_STREAM, 0);
    if (lfd < 0)
    {
   
   
        perror("socket error");
        exit(1);
    }
    // bind绑定
    bind(lfd, (struct sockaddr *)&serve_addr, sizeof(serve_addr));
    // 设置上限
    listen(lfd, 128);

    // 创建红黑树
    efd = epoll_create(1); // efd就是树的根节点

    // 将lfd挂在树上
    // epoll结构体                       ep是epoll_wait所需的数组(存放满足事件的fd)
    struct epoll_event tep, ep[128]; //  tep是epoll_ctl的参数(传监听的事件)
    tep.events = EPOLLIN;
    tep.data.fd = lfd;
	
    ret = epoll_ctl(efd, EPOLL_CTL_ADD, lfd, &tep);
    if (ret < 0)
    {
   
   
        perror("epoll_ctl error");
        exit(1);
    }

    // 循环去epoll_wait进行监听
    while (1)
    {
   
   
        wait_ret = epoll_wait(efd, ep, 128, -1); // wait_ret就是实际满足事件的总个数

        // 以wait_ret为上限去遍历事件
        for (i = 0; i < wait_ret; i++)	
        {
   
   
            // sockfd用于接收满足事件的fd
            sockfd = ep[i].data.fd;

            // 如果等于lfd,那就说明有客户端要来连接,就去accept
            if (sockfd == lfd)	
            {
   
   
                client_addr_len = sizeof(client_addr);
                cfd = accept(lfd, (struct sockaddr *)&client_addr, &client_addr_len);

				// 将cfd设置为非阻塞
                int flag = fcntl(cfd, F_GETFL);
                flag |= O_NONBLOCK;
                fcntl(cfd, F_SETFL, flag);
	
                // 把新的cfd加入树中
                tep.events = EPOLLIN | EPOLLET;
                tep.data.fd = cfd;
                ret = epoll_ctl(efd, EPOLL_CTL_ADD, cfd, &tep);
                if (ret < 0)
                {
   
   
                    perror("epoll_ctl cfd error");
                    exit(1);
                }
            }
            // 如果不是lfd,那就说明有读事件发生(读数据)
            else
            {
   
   
                len = read(sockfd, buf, sizeof(buf));
                if (len == 0) // 说明对方关闭连接(从树上摘下 & close)
                {
   
   
                    epoll_ctl
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值