0. 前言

  这篇文章主要记录在使用epoll实现NIO接入时所遇到的问题。

1. epoll简介

  epoll是Linux下提供的NIO,其主要有两种模式,ET(Edge trige)和LT(Level trige)。在linux下使用man epoll手册即可知道这两种模式主要的区别:

  ET:边缘触发,故名思议,所添加的描述符,只在当其改变状态的时候才会触发一次,就如同数电里面电平的边缘触发。

  在man里面列举了一个例子,当一个fd添加到epoll中时,当有2KB数据到达时,epoll_wait会返回事件个数以及其fd,此时,当程序读取了1KB数据后继续调用epoll_wait,1)对于ET来说会继续等待,2)而LT则是继续触发返回。

2. epoll错误程序分析

  下述为错误的示例:

 #include <sys/epoll.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <string.h> #define MAX_BACKLOG 256
#define MAX_EVENTS 1024 static int SetNonBlock(int nFd)
{
int nOldOpt = fcntl(nFd, F_GETFD, );
int nNewOpt = nOldOpt | O_NONBLOCK; return fcntl(nFd, F_SETFD, nNewOpt);
} static void AddEvent(int nEpfd, int nFd)
{
struct epoll_event event;
event.data.fd = nFd;
event.events = EPOLLIN | EPOLLOUT | EPOLLRDHUP | EPOLLET; return epoll_ctl(nEpfd, EPOLL_CTL_ADD, &event);
} int main(int argc, char const *argv[])
{
if (argc != )
{
printf("usage: CMD Port\n");
exit(-);
} int nPort = atoi(argv[]);
if (nPort < )
{
printf("Port Invalid\n");
exit(-);
} int nSvrFd = socket(AF_INET, SOCK_STREAM, );
//设置非阻塞
SetNonBlock(nSvrFd); //绑定地址
struct sockaddr_in addr;
bzero(&addr, sizeof(addr)); addr.sin_addr = ;
addr.sin_port = htons(argv[]);
addr.sin_family = htos(AF_INET); if ( != bind(nSvrFd, (struct sockaddr*)&addr, sizeof(addr)))
{
perror("Bind Failure:");
exit(-);
} //监听端口
if ( != listen(nSvrFd, MAX_BACKLOG))
{
perror("Listen Failure:");
exit(-);
} int nEpfd = epoll_create();
struct epoll_event events[MAX_EVENTS]; AddEvent(nEpfd, nSvrFd); while ()
{
//等待事件到来
int nReadyNums = epoll_wait(nEpfd, events, MAX_EVENTS, -); for (int i = ; i < nReadyNums; ++i)
{
if (events[i].data.fd == nSvrFd)
{
//这里对于ET模式来说是有问题的
int nClientFd = accept(nSvrFd, NULL, NULL);
if (- != nClientFd)
{
//设置为非阻塞
SetNonBlock(nClientFd);
//添加事件监听
AddEvent(nEpfd, nClientFd);
} } else
{
//处理FD事件
}
}
} return ;
}

  分析:这里的程序使用ET + 非阻塞,对于accept没有使用循环接收,则会导致当两个连接同时接入的时候,只触发一次,则accept一次,另外一个则停留在SYN队列中。当终端超时后发送FIN状态,这边只是将该连接标识为只读状态。并连接处于CLOSE_WAIT状态。当下一次接入的时候,触发epoll,这次accept的却是上一次的连接,这次的连接依然停留在SYN队列中。如果后续都是单次触发的话,则会导致后续交易都失败。

  这里对KEEPALIVE选项做个补充,KEEPALIVE是用于检测到连接断开后有操作系统自动释放资源,但是并不会释放SYN队列里面的连接,也就是说,CLOSE_WAIT状态会被清除,但是问题还是存在,会把现象遮蔽了。

  正确应该是在accept加上一个循环:

while ((nClientFd = accept(nSvrFd, NULL, NULL)) > )
{
//设置为非阻塞
SetNonBlock(nClientFd);
//添加事件监听
AddEvent(nEpfd, nClientFd);
}

3. 总结

  对于ET模式+非阻塞,无论是recv还是accept,都需要加上循环处理

epoll ET模式陷阱分析的更多相关文章

  1. (转)彻底学会使用epoll(一)——ET模式实现分析

    注:之前写过两篇关于epoll实现的文章,但是感觉懂得了实现原理并不一定会使用,所以又决定写这一系列文章,希望能够对epoll有比较清楚的认识.是请大家转载务必注明出处,算是对我劳动成果的一点点尊重吧 ...

  2. C/C++ 知识点---sizeof使用规则及陷阱分析(网摘)

    C/C++ 知识点---sizeof使用规则及陷阱分析 原文出处:[胖奇的专栏] 1.什么是sizeof 首先看一下sizeof在msdn上的定义:     The sizeof keyword gi ...

  3. epoll(2) 源码分析

    epoll(2) 源码分析 文本内核代码取自 5.0.18 版本,和上一篇文章中的版本不同是因为另一个电脑出了问题,但是总体差异不大. 引子留下的问题 关键数据结构 提供的系统调用 就绪事件相关逻辑 ...

  4. 三种工厂模式的分析以及C++实现

    三种工厂模式的分析以及C++实现 以下是我自己学习设计模式的思考总结. 简单工厂模式 简单工厂模式是工厂模式中最简单的一种,他可以用比较简单的方式隐藏创建对象的细节,一般只需要告诉工厂类所需要的类型, ...

  5. 基于Java 生产者消费者模式(详细分析)

    Java 生产者消费者模式详细分析 本文目录:1.等待.唤醒机制的原理2.Lock和Condition3.单生产者单消费者模式4.使用Lock和Condition实现单生产单消费模式5.多生产多消费模 ...

  6. 微软和Google的盈利模式对比分析

    一: 微软和Google是世界上最成功科技巨头之一,但他们之间却有着不同的产品和业务,二者的盈利方式也各有不同,本文将分析和探讨的二者盈利模式的异同. 微软的盈利模式 在1975年由大学肄业的Bill ...

  7. Libreswan软件的密钥协商协议IKEv1主模式实现分析

    Libreswan软件的密钥协商协议IKEv1主模式实现分析 1 协商过程 IKEv1(互联网密钥交换协议第一版)是IPsec VPN隧道协商使用的标准密钥协商协议,其协商过程如下图所示. 总共来回交 ...

  8. C2B电商三种主要模式的分析_数据分析师

    C2B电商三种主要模式的分析_数据分析师 在过去的一年中电商领域血雨腥风,尤其是天猫.京东.苏宁.当当.易讯等B2C电商打得不亦乐乎.而随着B2C领域竞争进入白热化阶段,C2B模式也在天猫" ...

  9. epoll惊群原因分析

    考虑如下情况(实际一般不会做,这里只是举个例子): 在主线程中创建一个socket.绑定到本地端口并监听 在主线程中创建一个epoll实例(epoll_create(2)) 将监听socket添加到e ...

随机推荐

  1. 问题解决——MFC Ribbon 响应函数 错乱 执行其他函数

    ==================================声明================================== 本文原创,转载在正文中显要的注明作者和出处,并保证文章的完 ...

  2. [WPF系列]-Prism+EF

      源码:Prism5_Tutorial   参考文档 Data Validation in WPF √ WPF 4.5 – ASYNCHRONOUS VALIDATION Reusable asyn ...

  3. JAVA类的静态加载和动态加载以及NoClassDefFoundError和ClassNotFoundException

    我们都知道Java初始化一个类的时候可以用new 操作符来初始化, 也可通过Class.forName()的方式来得到一个Class类型的实例,然后通过这个Class类型的实例的newInstance ...

  4. Shell基础学习小结

    0 shell基础概念 Shell是解释性语言,使用脚本编程语言的好处是,它们多半运行在比编译型语言还高的层级,能够轻易处理文件与目录之类的对象:缺点是它们的效率通常不如编译型语言.Shell命令有本 ...

  5. postman使用之四:切换环境和设置读取变量

    postman提供了environment管理功能,想要在多个环境中测试,比如在测试环境.灰度环境.生产环境等,只需要用同样的接口,切换下环境即可,非常方便.具体步骤: 切换环境 1.点击界面右上角的 ...

  6. c++中static关键字的用法总结

    类中的静态成员真是个让人爱恨交加的特性.我决定好好总结一下静态类成员的知识点,以便自己在以后面试中,在此类问题上不在被动. 静态类成员包括静态数据成员和静态函数成员两部分. 一 静态数据成员: 类体中 ...

  7. bean 接收的参数为Class类型

    这两个是等价的

  8. C#.NET 大型通用信息化系统集成快速开发平台 4.1 版本 - 密码强化、网络安全强化

    公司的系统庞大后,用户数量也庞大,容易引起各种网络安全相关问题,需要把每个用户的网络安全意识都提高,由于历史原因有些用户的密码过于简单,很容易被猜到,引起很多没必要的麻烦. 系统登录成功后,检查密码复 ...

  9. echarts在.Net中使用实例(一) 简单的Demo

    前言 这个必须要有前言,即便很短,对于有强迫症的人来说不容易啊.言归正传,之前做图一直使用rdlc自带的格式,虽然任务完成,但是一直觉得不太美观, 空余时间开始找其他的插件,终于找到了Highchar ...

  10. 【转】Chrome 控制台不完全指南

    原文出处: 刘哇勇的博客(@刘哇勇)   欢迎分享原创到伯乐头条 Chrome的开发者工具已经强大到没朋友的地步了,特别是其功能丰富界面友好的console,使用得当可以有如下功效: 更高「逼格」更快 ...