Linux网络编程-12、epoll的两种事件模型1、LT(默认水平触发)(监听的fd有数据就触发epoll_wait)2、ET(边沿触发)(监听的fd数据到达的那一刻才会触发epoll_wait)

1、LT(默认水平触发)(监听的fd有数据就触发epoll_wait)

#include "wrap.h"
#include <sys/epoll.h>
#include <arpa/inet.h>
#include <ctype.h>
#define PORT 9527
#define LISTENMAX 128
#define TESTBUF 5

int main(void)
{
    // 1、socket()创建监听套接字
    int lfd, cfd;
    int ret;
    int n; //读到的字节数
    char buf[TESTBUF]; // 用来接收客户端发来的数据
    lfd = Socket(AF_INET, SOCK_STREAM, 0);
    // 2、setsockopt设置端口复用
    int opt = 1;
    ret = setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, (void *)&opt, sizeof(opt));
    if (ret == -1)
        sys_error("setsockopt error");
    // 3、bind()绑定地址结构ip+端口号
    struct sockaddr_in saddr;
    socklen_t saddrlen = sizeof(saddr);
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(PORT); // 网络字节序下的端口号
    saddr.sin_addr.s_addr = htonl(INADDR_ANY); // 网络字节序下的ip地址
    Bind(lfd, (struct sockaddr *)&saddr, saddrlen);
    struct sockaddr_in caddr; // 用来接收建立连接的客户端地址结构
    socklen_t caddrlen = sizeof(caddr);  
    // 4、listen设置监听上限
    Listen(lfd, LISTENMAX);
    // 5、epoll小秘书只负责监听(1次调用, 只监听1次) 
    // 6、epoll_create创建1棵监听树
    int epfd; // 监听树的根结点
    epfd = epoll_create(128); // 仅供参考, 监听树的结点数128 内核会扩容的
    if (epfd == -1)
        sys_error("epoll_create error");
    // 7、epoll_ctl往监听树上添加结点(想要监听的客户端=文件描述符)
    // 初始: 只监听lfd的读事件 = 把lfd加入监听树
    struct epoll_event event;
    event.events = EPOLLIN;
    event.data.fd = lfd;
    ret = epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &event);
    if (ret == -1)
        sys_error("epoll_ctl error");
    int epnum; // 接收监听树中所有的事件数
    struct epoll_event wevent[BUFSIZ]; // (传出参数)接收监听树中实际发生读事件的结点
    while (1)
    {
        // 5、epoll_wait小秘书只负责监听
        epnum = epoll_wait(epfd, wevent, BUFSIZ, 0); // 采用非阻塞轮询
        // 8、分析一次监听中产生的读事件
        if (epnum == -1)
            sys_error("epoll_wait error");
        else if (epnum > 0) // 有读事件产生
        {
            // 传出的数组-挨个看看是谁产生的读事件
            for (int i = 0; i < epnum; ++i)
            {
                if (wevent[i].data.fd == lfd)
                {
                    // 9、lfd有读事件-新客户端建立连接
                    cfd = Accept(lfd, (struct sockaddr *)&caddr, &caddrlen);
                    // 打印客户端信息
                    char cip[256];
                    inet_ntop(AF_INET, &caddr.sin_addr.s_addr, cip, sizeof(cip));
                    printf("新连接的客户端: ip %s port %d\n", cip, ntohs(caddr.sin_port));
                    // 10、新cfd加入监听树(监听读事件)
                    event.data.fd = cfd;
                    event.events = EPOLLIN; // 默认水平触发LT 监听的fd中有数据就会触发epoll_wait
                    // event.events = EPOLLIN | EPOLLET; // 改为边沿触发ET 
                    ret = epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &event);
                    if (ret == -1)
                        sys_error("while epoll_ctl error");
                }
                else
                {
                    // 9、其他fd有读事件-旧客户端想发数据给我
                    if ((n = read(wevent[i].data.fd, buf, TESTBUF)) > 0)
                    {
                        // 10、模拟数据处理
                        for (int j = 0; j < n; ++j)
                            buf[j] = toupper(buf[j]);  
                        // 11、发给客户端
                        write(wevent[i].data.fd, buf, n);              
                    }
                    else if (n == 0) // 客户端想断开连接
                    {
                        // 该结点从监听树中移除
                        ret = epoll_ctl(epfd, EPOLL_CTL_DEL, wevent[i].data.fd, NULL);
                        if (ret == -1)
                            sys_error("while epoll_ctl error");
                        Close(wevent[i].data.fd);
                    }
                }
            }
        }   
    } 
    Close(lfd);

    return 0;
}

2、nc 127.0.0.1 9527 测试(注意read只读5个字节)

 

2、ET(边沿触发)(监听的fd数据到达的那一刻才会触发epoll_wait)

(监听的fd的事件 | EPOLLET)即可设置ET(边沿触发)

struct epoll_event tmp;
tmp.data.fd = cfd;
tmp.events = EPOLLIN | EPOLLET; // 改为边沿触发ET(监听的fd中数据到达的一刻才会触发epoll_wait) 
#include "wrap.h"
#include <sys/epoll.h>
#include <arpa/inet.h>
#include <ctype.h>
#define PORT 9527
#define LISTENMAX 128
#define TESTBUF 5

int main(void)
{
    // 1、socket()创建监听套接字
    int lfd, cfd;
    int ret;
    int n; //读到的字节数
    char buf[TESTBUF]; // 用来接收客户端发来的数据
    lfd = Socket(AF_INET, SOCK_STREAM, 0);
    // 2、setsockopt设置端口复用
    int opt = 1;
    ret = setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, (void *)&opt, sizeof(opt));
    if (ret == -1)
        sys_error("setsockopt error");
    // 3、bind()绑定地址结构ip+端口号
    struct sockaddr_in saddr;
    socklen_t saddrlen = sizeof(saddr);
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(PORT); // 网络字节序下的端口号
    saddr.sin_addr.s_addr = htonl(INADDR_ANY); // 网络字节序下的ip地址
    Bind(lfd, (struct sockaddr *)&saddr, saddrlen);
    struct sockaddr_in caddr; // 用来接收建立连接的客户端地址结构
    socklen_t caddrlen = sizeof(caddr);  
    // 4、listen设置监听上限
    Listen(lfd, LISTENMAX);
    // 5、epoll小秘书只负责监听(1次调用, 只监听1次) 
    // 6、epoll_create创建1棵监听树
    int epfd; // 监听树的根结点
    epfd = epoll_create(128); // 仅供参考, 监听树的结点数128 内核会扩容的
    if (epfd == -1)
        sys_error("epoll_create error");
    // 7、epoll_ctl往监听树上添加结点(想要监听的客户端=文件描述符)
    // 初始: 只监听lfd的读事件 = 把lfd加入监听树
    struct epoll_event event;
    event.events = EPOLLIN;
    event.data.fd = lfd;
    ret = epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &event);
    if (ret == -1)
        sys_error("epoll_ctl error");
    int epnum; // 接收监听树中所有的事件数
    struct epoll_event wevent[BUFSIZ]; // (传出参数)接收监听树中实际发生读事件的结点
    while (1)
    {
        // 5、epoll_wait小秘书只负责监听
        epnum = epoll_wait(epfd, wevent, BUFSIZ, 0); // 采用非阻塞轮询
        // 8、分析一次监听中产生的读事件
        if (epnum == -1)
            sys_error("epoll_wait error");
        else if (epnum > 0) // 有读事件产生
        {
            // 传出的数组-挨个看看是谁产生的读事件
            for (int i = 0; i < epnum; ++i)
            {
                if (wevent[i].data.fd == lfd)
                {
                    // 9、lfd有读事件-新客户端建立连接
                    cfd = Accept(lfd, (struct sockaddr *)&caddr, &caddrlen);
                    // 打印客户端信息
                    char cip[256];
                    inet_ntop(AF_INET, &caddr.sin_addr.s_addr, cip, sizeof(cip));
                    printf("新连接的客户端: ip %s port %d\n", cip, ntohs(caddr.sin_port));
                    // 10、新cfd加入监听树(监听读事件)
                    event.data.fd = cfd;
                    // event.events = EPOLLIN; // 默认水平触发LT 
                    event.events = EPOLLIN | EPOLLET; // 改为边沿触发ET(监听的fd中数据到达的一刻才会触发epoll_wait) 
                    ret = epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &event);
                    if (ret == -1)
                        sys_error("while epoll_ctl error");
                }
                else
                {
                    // 9、其他fd有读事件-旧客户端想发数据给我
                    if ((n = read(wevent[i].data.fd, buf, TESTBUF)) > 0)
                    {
                        // 10、模拟数据处理
                        for (int j = 0; j < n; ++j)
                            buf[j] = toupper(buf[j]);  
                        // 11、发给客户端
                        write(wevent[i].data.fd, buf, n);              
                    }
                    else if (n == 0) // 客户端想断开连接
                    {
                        // 该结点从监听树中移除
                        ret = epoll_ctl(epfd, EPOLL_CTL_DEL, wevent[i].data.fd, NULL);
                        if (ret == -1)
                            sys_error("while epoll_ctl error");
                        Close(wevent[i].data.fd);
                    }
                }
            }
        }   
    } 
    Close(lfd);

    return 0;
}

2、nc 127.0.0.1 9527 测试(注意read只读5个字节)  

 3、敲下回车, 有数据到达, 这一刻重新触发epoll_wait再read读5个字节

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

汪呈祥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值