EPOLL使用注意

EPOLL使用注意

  (2007-05-25 09:52:25)
标签: 

epoll

 

et

 

epoll_ctl

 

epoll_ctl_add

 

epoll_ctl_mod

 

网络通讯

 

tcp

分类: 开发经验

接上篇文章,这段时间一直对使用EPOLL关联socket的网络通讯进行测试,功能测试就不用说了,网络上有很多实现例子,都可以参考;我主要集中在压力测试和稳定情况的分析,现有了自己的一点体会,特贴出来供大家批驳。
首先描述一下测试环境:Linux服务器,内核版本:2.6.9,g++版本3.4.6,EPOLL都使用ET模式。
第一个体会是关于监听套接字m_hListenSocket和epoll关联。一般的例子都是创建监听套接字和EPOLL描述苻后使用如下代码建立关联:
       struct epoll_event struEvent;
       struEvent.events = EPOLLIN | EPOLLET;
       struEvent.data.fd = m_hListenSocket;
       epoll_ctl(m_hEpoll, EPOLL_CTL_ADD, m_hListenSocket, &struEvent);
建立连接后,任何客户端和服务器监听端口建立连接的功能测试都是正常的,可以建立连接、收发数据等。但开启压力测试后发现了新问题,150个连接没间隔1秒和服务器建立连接,发现EPOLL只返回给我130个请求,并建立了130个连接;反复测试了多次,都是如此,那剩余的客户端请求那里去了?接着测试了300个连接,发现EPOLL只返回给我280个请求,也吃掉了20个请求;如果先建立150个连接,再一个一个的请求会发生什么呢?测试的情况是第131个请求不是151个请求,是被EPOLL吃掉的请求出来了;也就是说新请求可以把EPOLL未投递给我的消息顶出来,EPOLL一直保持着20个请求不返回,为什么会有这种现象呢?解决办法是什么呢?
带着这些疑问,反复进行了测试,发现使用EPOLL提供的LT模式,没有这种问题,但CPU消耗实在是太高了,不能接受。
测试了接收和发送数据了TCP套接字,由于每次发送数据后都使用如下代码,和epoll重新建立了关联,所以没有发生过EPOLL吃掉消息的问题。注意如不使用代码再次建立关联,则不能使用户socket持续发送数据。
       struct epoll_event struEvent;
       struEvent.events = EPOLLIN | EPOLLOUT | EPOLLET;
       struEvent.data.fd = hSocket;
       epoll_ctl(m_hEpoll, EPOLL_CTL_MOD, hSocket, &struEvent);
同时获得这个启发,是不是ET模式下,接受客户端请求建立连接时,EPOLL也要和监听套接字m_hListenSocket重新建立关联呢 ,随即增加了如下代码进行了测试:
     epoll_ctl(m_hEpoll, EPOLL_CTL_MOD, m_hListenSocket, &struEvent);
这下好了,无论是150个连接还是300个,EPOLL都可以完整的反馈给我了。看来这一步是必须的,但为什么EPOLL一定要吃掉20个请求呢,这点是我最不能理解呢?如果没有关联,那第2个请求或第3个就应当不返回了,真诚希望能有高手给予一个明确的答复。
### 使用Epoll进行事件处理 #### 创建Epoll实例 为了启动基于`epoll`的事件处理流程,首先需要创建一个`epoll`实例。这可以通过调用`epoll_create1(0)`来实现,该函数返回一个新的`epoll`文件描述符用于后续的操作[^1]。 ```c int epfd = epoll_create1(0); if (epfd == -1) { perror("Failed to create epoll instance"); } ``` #### 控制操作——注册感兴趣的事件 一旦有了`epoll`实例,就可以利用`epoll_ctl()`向此实例中添加、修改或移除要监控的文件描述符及其对应的事件类型。比如想要监听某个套接字上的可读写状态变化,则可以这样做: ```c struct epoll_event event; event.events = EPOLLIN | EPOLLET; // 设置为边缘触发模式并关注输入就绪事件 event.data.fd = sockfd; // 将sockfd加入到epoll监视列表里 if (epoll_ctl(epfd, EPOLL_CTL_ADD, sockfd, &event) == -1) { perror("Error adding socket to epoll set"); } ``` 这里选择了边沿触发(`EPOLLET`)的方式,意味着只有当首次检测到条件满足时才会收到通知;而水平触发则会在每次符合条件的时候都发出信号直到条件消失为止。选择哪种取决于具体的应用场景需求[^4]。 #### 等待并响应事件 最后一步是等待实际发生的事件,并作出相应的反应。通过循环调用`epoll_wait()`可以从之前设置好的`epoll`集合里面获取已经准备好了的数据流(即发生了指定类型的活动)。下面是一个简单的例子展示如何做这件事: ```c #define MAX_EVENTS 10 struct epoll_event events[MAX_EVENTS]; while(true){ int nfds = epoll_wait(epfd, events, MAX_EVENTS, -1); //-1表示无限期阻塞直至有事件发生 if(nfds == -1){ perror("epoll wait error"); break; } for(int i=0;i<nfds;++i){ if(events[i].events&EPOLLIN){ // 如果是读取准备好... handle_read(events[i].data.fd); } else if(events[i].events&EPOLLOUT){// 或者写出准备好... handle_write(events[i].data.fd); } } } void close_epoll(){ close(epfd); // 记得关闭epoll句柄以释放资源 } ``` 这段代码展示了基本框架:在一个无限循环内部不断查询是否有新的IO事件到来,并针对不同的情况执行特定的任务逻辑。需要注意的是,在程序结束前应当记得关闭由`epoll_create1()`所获得的那个特殊的文件描述符,以免造成系统资源泄露[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值