epoll IO多路复用技术
- 格式:docx
- 大小:98.02 KB
- 文档页数:30
epoll 用法
epoll是一种事件通知机制,用于在 Linux 上实现高效的 I/O 多路复用。
它可以同时监视多个文件描述符,当其中任意一个描述符就绪时,就会通知应用程序进行相应的处理。
epoll 的使用方法主要分为以下几步:
1. 创建 epoll 实例:调用 epoll_create 函数创建一个 epoll 实例,它返回一个文件描述符,用于后续的操作。
2. 添加事件监听:调用 epoll_ctl 函数将需要监听的文件描述符添加到 epoll 实例中,同时指定需要监听的事件类型,如可读、可写等。
3. 等待事件发生:调用 epoll_wait 函数等待事件的发生。
它会阻塞应用程序,直到有文件描述符就绪或者超时。
4. 处理事件:当 epoll_wait 返回时,应用程序需要根据返回的就绪事件列表进行相应的处理,如读取数据、写数据等。
需要注意的是,epoll 使用时需要结合非阻塞 I/O 来使用,以充分发挥其高效的特性。
epoll 的使用相对于传统的 select 和 poll 等方式更加高效,可以大大提高应用程序的性能和并发能力。
因此,在设计高并发网络应用程序时,epoll 是一个非常重要的工具。
- 1 -。
epoll 原理
epoll是Linux系统提供的一种高效I/O多路复用方式。
它采用事件驱动的方式实现I/O多路复用,可以同时监控多个文件描述符的状态,并在文件描述符就绪时通知应用程序进行读写操作。
与传统的select 和 poll 系统调用相比,epoll 具有更高的性能和更好的可扩展性。
epoll 基于内核中的事件驱动机制,通过注册回调函数实现对事件的监听和处理。
应用程序可以将一个或多个文件描述符注册到epoll 对象中,当所关注的文件描述符就绪时,内核会通知 epoll 对象,epoll 对象再调用应用程序注册的回调函数进行处理。
epoll 主要包含三个系统调用:epoll_create、epoll_ctl 和epoll_wait。
其中,epoll_create 用于创建 epoll 对象,epoll_ctl 用于向 epoll 对象中添加、修改或删除文件描述符,epoll_wait 则是阻塞等待 epoll 对象中的文件描述符就绪。
与 select 和 poll 不同的是,epoll 不需要在每次调用
epoll_wait 时重新向内核传递文件描述符集合,而是在注册文件描述符时将其添加到内核中的事件表中,这样每次调用 epoll_wait 时只需要从事件表中取出就绪的文件描述符即可,大大减少了内核与用户空间的数据交换次数,提高了系统的效率。
总之,epoll 是 Linux 平台上一种高效的 I/O 多路复用机制,采用事件驱动的方式实现对文件描述符的监控和处理。
它相对于传统的 select 和 poll 有更好的性能和可扩展性,是实现高并发网络编
程的重要工具之一。
非阻塞io多路复用机制非阻塞IO多路复用机制一、介绍在计算机网络中,I/O即输入输出操作,是计算机与外部设备进行交互的方式之一。
传统的I/O操作常常是阻塞式的,即当一个I/O操作进行时,CPU不能同时执行其它操作,需等待I/O操作完成后才能继续执行。
这种阻塞式的I/O操作,对于服务器等高性能应用是十分低效和浪费资源的。
为了提高I/O操作的效率,非阻塞式的I/O操作越来越被广泛应用于计算机网络。
非阻塞式的I/O操作,可以在读取或写入操作时立即返回,避免资源浪费。
二、多路复用机制多路复用是一种I/O操作的机制,可以同时监测多个I/O操作的状态,从而提高I/O操作的效率。
多路复用分为两种方式:select和epoll。
1. select多路复用select是一种多路复用机制,主要实现步骤是:将要监听的一组I/O操作,通过select函数注册在一个fd_set变量集合中,然后调用select函数等待I/O操作发生,当I/O操作发生时,再通过遍历fd_set集合,判断哪些I/O操作发生了变化,从而进行相应的操作。
select的缺点是效率低下和可扩展性差,因为每次调用select函数,都要遍历I/O操作的集合,当I/O操作很大时,效率会很低,甚至会出现错误。
2. epoll多路复用epoll是linux内核中提供的多路复用机制,实现方式与select相似,但是其效率更高,可扩展性更好。
epoll的实现原理是:将要监听的I/O 事件添加到内核中的事件表中,然后通过epoll_wait函数等待I/O事件发生。
当I/O事件发生后,内核会在自己的事件表中查找相应的I/O事件,从而可以快速地找到I/O事件的注册信息。
三、非阻塞IO与多路复用机制的结合非阻塞IO和多路复用机制的结合,可以实现高效的I/O操作。
首先将要操作的I/O设置为非阻塞式的,然后再通过多路复用机制对多个I/O 事件进行监听,当I/O事件发生时,操作系统会负责将I/O操作的结果返还给应用程序。
select poll epoll原理一、概述select、poll、epoll 都是常见的 I/O 多路复用机制,实现高效的 I/O 操作,提高程序的性能。
这三种机制都是通过在一个线程中处理多个文件描述符的 I/O 事件实现高并发。
本文将介绍 select、poll、epoll 的原理。
二、selectselect 是最早的 I/O 多路复用机制之一,提出于1983 年。
select 的基本原理是将一些文件描述符添加到一个 fd_set 集合中,通过 select 函数等待这些 fd_set 集合中的文件描述符中有一个或多个就绪,然后在这些文件描述符上执行 I/O 操作。
select 函数可以设置一个超时时间,当超时时间到达后若 fd_set 集合中没有就绪的文件描述符,则返回 0。
select 的缺点是效率低下。
在处理大量文件描述符时会采用轮询的方式,每次遍历所有文件描述符查看是否有就绪的 I/O 事件,需要大量的 CPU 资源,并且 select 的 fd_set 集合最大长度受到系统宏 FD_SETSIZE 的限制,通常不超过 1024。
三、pollpoll 函数是对 select 函数的一次改进,也是 I/O 多路复用机制中比较常见的一种。
poll 的基本原理是将一些文件描述符添加到一个 pollfd 数组中,通过 poll 函数等待这些数组中的文件描述符中有一个或多个就绪,然后在这些文件描述符上执行 I/O 操作。
与 select 不同的是,poll 数组不受数量限制,我们可以根据需要动态调整数组长度。
poll 函数也可以设置超时时间,当超时时间到达后若 pollfd 数组中没有就绪的文件描述符,则返回0。
poll 的缺点是仍然存在效率问题。
poll 在处理大量文件描述符时,每次都需要遍历整个 pollfd 数组,查看是否有就绪的 I/O 事件。
四、epollepoll 是 Linux 下的一种 I/O 多路复用机制,也是效率最高的一种。
io的多路复用机制IO的多路复用机制是一种高效的网络编程技术,它能够在单线程下同时处理多个IO任务,提高系统的并发性能。
下面将详细介绍IO 的多路复用机制及其应用。
一、什么是IO的多路复用机制IO的多路复用机制是指通过一个线程来监听多个IO事件,并且在有IO事件发生时进行响应的机制。
它的主要原理是利用操作系统提供的系统调用,如select、poll、epoll等,来监听多个IO事件的状态变化。
当有IO事件就绪时,通过系统调用返回的事件集合来确定是哪些IO事件就绪,并进行相应的处理。
二、为什么需要IO的多路复用机制在传统的IO模型中,每个IO任务通常需要一个线程来处理,当IO 任务数量较大时,会导致线程数量激增,造成系统资源的浪费和线程调度的开销。
而采用IO的多路复用机制后,只需要一个线程来监听多个IO事件,大大减少了线程的数量和系统开销,提高了系统的并发性能。
三、IO的多路复用机制的应用1. 服务器编程:在服务器编程中,经常需要处理多个客户端的连接请求。
使用IO的多路复用机制可以通过一个线程监听多个客户端的连接请求,并在有连接请求到来时进行处理。
2. 并发网络编程:在并发网络编程中,通常需要同时处理多个网络请求。
使用IO的多路复用机制可以在单线程下同时处理多个网络请求,提高系统的并发性能。
3. 文件传输:在文件传输中,通常需要同时读写多个文件。
使用IO 的多路复用机制可以在单线程下同时处理多个文件的读写操作,提高文件传输的效率。
四、IO的多路复用机制的优点1. 提高系统的并发性能:通过一个线程同时监听多个IO事件,减少了线程的数量和系统开销,提高了系统的并发性能。
2. 减少了线程切换的开销:由于采用了单线程的方式来处理多个IO 事件,减少了线程的切换开销,提高了系统的响应速度。
3. 简化了程序的设计:采用IO的多路复用机制可以将多个IO任务放在一个线程中处理,简化了程序的设计和维护。
五、IO的多路复用机制的局限性1. 对于大量的IO任务,单个线程可能无法处理,需要采用多线程或线程池来进行处理。
linux中select、poll、epoll原理select、poll和epoll是Linux下常用的I/O多路复用技术,都用于实现高效的事件驱动型的网络编程。
1. select(选择)select是最古老的I/O多路复用机制,它通过在套接字上设置阻塞(阻塞方式)进行等待,一旦有文件描述符准备就绪(可读、可写等),则返回。
select使用fd_set集合来保存要监听的文件描述符,因此其监听的文件描述符数量受到系统给定的FD_SETSIZE限制。
select的实现原理是:在内核中创建一个称为“等待队列”的数据结构(fd_set),该队列保存了需要等待的文件描述符,当某个文件描述符就绪时,会通过和用户进程的映射表通知用户进程。
select通过轮询所有注册的文件描述符,检查哪些文件描述符已经准备好,并将准备好的文件描述符从用户态拷贝到内核态。
select的缺点是每次调用都需要轮询全部的注册文件描述符,效率较低。
2. poll(轮询)poll是在select的基础上进行改进的多路复用技术。
poll与select的最大区别在于,它没有限制文件描述符的数量,并且使用了一个pollfd结构体数组来保存每个文件描述符及其关注的事件。
poll在内核中创建一个称为“等待队列”的数据结构,该队列保存了需要等待的文件描述符,当某个文件描述符就绪时,会通过和用户进程的映射表通知用户进程。
poll的实现原理是:将用户进程注册要监听的文件描述符及其关注的事件存储在内核中的一个事件表中,当发生事件时,内核会将该事件存储在内核态的事件表中,并通知用户进程。
与select不同的是,poll只需在事件发生时拷贝某些信息到内核态,而不需要拷贝全部的文件描述符。
poll的缺点是,当注册的文件描述符数量较大时,每次调用poll都需要遍历整个事件表,效率较低。
3. epoll(事件通知)epoll是Linux特有的一种I/O多路复用机制,通过内核与用户空间的共享内存来实现高效的事件通知。
多路io复用的原理
多路IO复用是一种提高IO效率的技术。
它通过在一个线程
内监听多个IO事件,使得程序能够有效地处理多个读写操作。
多路IO复用的原理主要分为两个方面:
1. 就绪通知机制
多路复用技术利用操作系统的就绪通知机制来监听IO事件。
它将多个IO事件注册到同一个select或epoll的文件描述符上,并通过内核提供的函数将文件描述符传递给操作系统,让操作系统来进行监听。
当有事件发生时,操作系统会通知进程,进程就可以进行相应的处理。
2. 非阻塞IO机制
多路复用技术还需要采用非阻塞IO机制。
当程序阻塞在某个
IO操作时,如果事件没有发生,程序就无法继续执行,这会
影响整个应用程序的运行。
非阻塞IO机制可以避免这种情况
的发生,它会立即返回一个错误码而不是让程序一直等待。
这样程序就可以进行其他任务或者重新发起IO操作,等待IO
事件发生,从而提高程序的效率。
综上所述,多路IO复用的原理就是利用操作系统的就绪通知
机制和非阻塞IO机制,来提高程序的IO效率,使得程序能
够同时处理多个IO事件。
linux中select、poll、epoll原理详解目录1. 引言1.1 背景和意义1.2 结构概述1.3 目的2. select原理详解2.1 基本概念2.2 使用方法2.3 应用场景3. poll原理详解3.1 基本概念3.2 使用方法3.3 应用场景4. epoll原理详解4.1 基本概念4.2 使用方法4.3 应用场景5. 结论5.1 对比分析选择合适的IO多路复用器5.2 总结与展望引言1.1 背景和意义在计算机网络编程中,同时监听多个文件描述符的可读、可写和异常事件是一项基本任务。
为了高效地处理这些事件,Linux提供了三种IO多路复用器:select、poll和epoll。
它们允许程序通过一次系统调用就能同时监听多个文件描述符,并在有可读、可写或异常事件发生时进行相应的处理。
使用IO多路复用器可以避免使用阻塞式IO或者轮询方式造成的性能损失,提高了程序的效率和响应速度。
尤其对于具有大量并发连接的服务器程序来说,选择合适的IO多路复用器可以极大地提升系统性能。
1.2 结构概述本文将详细解析Linux中三种IO多路复用器的原理和使用方法,包括select、poll和epoll。
对于每种IO多路复用器,我们将介绍其基本概念、使用方法以及适用场景。
通过深入理解这些IO多路复用器的工作原理,我们可以更好地掌握它们的特点及优缺点,并根据实际需求选择合适的方式来进行网络编程。
1.3 目的本文旨在帮助读者全面了解Linux中select、poll和epoll的原理和使用方法,以及它们在网络编程中的应用场景。
在深入理解这些IO多路复用器的基础上,读者可以根据实际需求灵活选择合适的IO多路复用器,提升程序的性能和可扩展性。
在接下来的文章中,我们将逐一介绍select、poll和epoll的原理详解、使用方法和应用场景,并进行对比分析,最后总结归纳各种IO多路复用器的特点及适用情况。
2. select原理详解2.1 基本概念在Linux系统中,select是一种常用的I/O多路复用机制,它可以监视多个文件描述符的状态是否满足某种条件,在有一或多个文件描述符就绪时通知进程进行相应的 I/O操作。
IO多路复用select,poll epoll以及区别要弄清问题先要知道问题的出现原因由于进程的执行过程是线性的(也就是顺序执行),当我们调用低速系统I-O(read,write,accept等等),进程可能阻塞,此时进程就阻塞在这个调用上,不能执行其他操作.阻塞很正常. 接下来考虑这么一个问题:一个服务器进程和一个客户端进程通信,服务器端read(sockfd1,bud,bufsize),此时客户端进程没有发送数据,那么read(阻塞调用)将阻塞直到客户端调用write(sockfd,but,size) 发来数据. 在一个客户和服务器通信时这没什么问题,当多个客户与服务器通信时,若服务器阻塞于其中一个客户sockfd1,当另一个客户的数据到达套接字sockfd2时,服务器不能处理,仍然阻塞在read(sockfd1.)上;此时问题就出现了,不能及时处理另一个客户的服务,咋么办?I-O多路复用来解决!I-O多路复用:继续上面的问题,有多个客户连接,sockfd1,sockfd2,sockfd3.sockfdn同时监听这n个客户,当其中有一个发来消息时就从select的阻塞中返回,然后就调用read读取收到消息的sockfd,然后又循环回select这样就不会因为阻塞在其中一个上而不能处理另一个客户的消息那这样子,在读取socket1的数据时,如果其它socket有数据来,那么也要等到socket1读取完了才能继续读取其它socket的数据吧。
那不是也阻塞住了吗?而且读取到的数据也要开启线程处理吧,那这和多线程IO有什么区别呢?3.跟多线程相比较,线程切换需要切换到内核进行线程切换,需要消耗时间和资-源. 而I-O多路复用不需要切换线-进程,效率相对较高,特别是对高并发的应用nginx就是用I-O多路复用,故而性能极佳.但多线程编程逻辑和处理上比I-O多路复用简单.而I-O多路复用处理起来较为复杂.这些名词比较绕口,理解涵义就好。
一、编目11. Epoll 是何方神圣?Epoll 可是当前在Linux 下开发大规模并发网络程序的热门人选,Epoll 在Linux2.6 内核中正式引入,和select 相似,其实都I/O 多路复用技术而已,并没有什么神秘的。
其实在Linux 下设计并发网络程序,向来不缺少方法,比如典型的Apache 模型(Process Per Connection ,简称PPC ),TPC (Thread Per Connection )模型,以及select 模型和poll 模型,那为何还要再引入Epoll 这个东东呢?那还是有得说说的…2. 常用模型的缺点如果不摆出来其他模型的缺点,怎么能对比出Epoll 的优点呢。
2.1 PPC/TPC 模型这两种模型思想类似,就是让每一个到来的连接一边自己做事去,别再来烦我。
只是PPC 是为它开了一个进程,而TPC 开了一个线程。
可是别烦我是有代价的,它要时间和空间啊,连接多了之后,那么多的进程/ 线程切换,这开销就上来了;因此这类模型能接受的最大连接数都不会高,一般在几百个左右。
2.2 select 模型1. 最大并发数限制,因为一个进程所打开的FD (文件描述符)是有限制的,由FD_SETSIZE 设置,默认值是1024/2048 ,因此Select 模型的最大并发数就被相应限制了。
自己改改这个FD_SETSIZE ?想法虽好,可是先看看下面吧…2. 效率问题,select 每次调用都会线性扫描全部的FD 集合,这样效率就会呈现线性下降,把FD_SETSIZE 改大的后果就是,大家都慢慢来,什么?都超时了??!!3. 内核/ 用户空间内存拷贝问题,如何让内核把FD 消息通知给用户空间呢?在这个问题上select 采取了内存拷贝方法。
2.3 poll 模型基本上效率和select 是相同的,select 缺点的 2 和 3 它都没有改掉。
3. Epoll 的提升把其他模型逐个批判了一下,再来看看Epoll 的改进之处吧,其实把select 的缺点反过来那就是Epoll 的优点了。
3.1. Epoll 没有最大并发连接的限制,上限是最大可以打开文件的数目,这个数字一般远大于2048, 一般来说这个数目和系统内存关系很大,具体数目可以cat /proc/sys/fs/file-max 察看。
3.2. 效率提升,Epoll 最大的优点就在于它只管你“活跃”的连接,而跟连接总数无关,因此在实际的网络环境中,Epoll 的效率就会远远高于select 和poll 。
3.3. 内存拷贝,Epoll 在这点上使用了“共享内存”,这个内存拷贝也省略了。
4. Epoll 为什么高效Epoll 的高效和其数据结构的设计是密不可分的,这个下面就会提到。
首先回忆一下select 模型,当有I/O 事件到来时,select 通知应用程序有事件到了快去处理,而应用程序必须轮询所有的FD 集合,测试每个FD 是否有事件发生,并处理事件;代码像下面这样:int res = select(maxfd+1, &readfds, NULL, NULL, 120);if (res > 0){for (int i = 0; i < MAX_CONNECTION; i++){if (FD_ISSET(allConnection[i], &readfds)){handleEvent(allConnection[i]);}}}// if(res == 0) handle timeout, res < 0 handle errorEpoll 不仅会告诉应用程序有I/0 事件到来,还会告诉应用程序相关的信息,这些信息是应用程序填充的,因此根据这些信息应用程序就能直接定位到事件,而不必遍历整个FD 集合。
int res = epoll_wait(epfd, events, 20, 120);for (int i = 0; i < res;i++){handleEvent(events[n]);}5. Epoll 关键数据结构前面提到Epoll 速度快和其数据结构密不可分,其关键数据结构就是:struct epoll_event {__uint32_t events; // Epoll eventsepoll_data_t data; // User data variable};typedef union epoll_data {void *ptr;int fd;__uint32_t u32;__uint64_t u64;} epoll_data_t;可见epoll_data 是一个union 结构体, 借助于它应用程序可以保存很多类型的信息:fd 、指针等等。
有了它,应用程序就可以直接定位目标了。
6. 使用Epoll既然Epoll 相比select 这么好,那么用起来如何呢?会不会很繁琐啊… 先看看下面的三个函数吧,就知道Epoll 的易用了。
int epoll_create(int size);生成一个Epoll 专用的文件描述符,其实是申请一个内核空间,用来存放你想关注的socket fd 上是否发生以及发生了什么事件。
size 就是你在这个Epoll fd 上能关注的最大socket fd 数,大小自定,只要内存足够。
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event );控制某个Epoll 文件描述符上的事件:注册、修改、删除。
其中参数epfd 是epoll_create() 创建Epoll 专用的文件描述符。
相对于select 模型中的FD_SET 和FD_CLR 宏。
int epoll_wait(int epfd,struct epoll_event * events,int maxevents,int timeout);等待I/O 事件的发生;参数说明:epfd: 由epoll_create() 生成的Epoll 专用的文件描述符;epoll_event: 用于回传代处理事件的数组;maxevents: 每次能处理的事件数;timeout: 等待I/O 事件发生的超时值;返回发生事件数。
相对于select 模型中的select 函数。
7. 例子程序下面是一个简单Echo Server 的例子程序,麻雀虽小,五脏俱全,还包含了一个简单的超时检查机制,简洁起见没有做错误处理。
view plain1.//2.// a simple echo server using epoll in linux3.//4.// 2009-11-055.// by sparkling6.//7.#include <sys/socket.h>8.#include <sys/epoll.h>9.#include <netinet/in.h>10.#include <arpa/inet.h>11.#include <fcntl.h>12.#include <unistd.h>13.#include <stdio.h>14.#include <errno.h>15.#include <iostream>ing namespace std;17.#define MAX_EVENTS 50018.struct myevent_s19.{20.int fd;21.void (*call_back)(int fd, int events, void *arg);22.int events;23.void *arg;24.int status; // 1: in epoll wait list, 0 not in25.char buff[128]; // recv data buffer26.int len;27.long last_active; // last active time28.};29.// set event30.void EventSet(myevent_s *ev, int fd, void (*call_back)(int, int, void*), void *arg)31.{32. ev->fd = fd;33. ev->call_back = call_back;34. ev->events = 0;35. ev->arg = arg;36. ev->status = 0;37. ev->last_active = time(NULL);38.}39.// add/mod an event to epoll40.void EventAdd(int epollFd, int events, myevent_s *ev)41.{42.struct epoll_event epv = {0, {0}};43.int op;44. epv.data.ptr = ev;45. epv.events = ev->events = events;46.if(ev->status == 1){47. op = EPOLL_CTL_MOD;48. }49.else{50. op = EPOLL_CTL_ADD;51. ev->status = 1;52. }53.if(epoll_ctl(epollFd, op, ev->fd, &epv) < 0)54. printf("Event Add failed[fd=%d]/n", ev->fd);55.else56. printf("Event Add OK[fd=%d]/n", ev->fd);57.}58.// delete an event from epoll59.void EventDel(int epollFd, myevent_s *ev)60.{61.struct epoll_event epv = {0, {0}};62.if(ev->status != 1) return;63. epv.data.ptr = ev;64. ev->status = 0;65. epoll_ctl(epollFd, EPOLL_CTL_DEL, ev->fd, &epv);66.}67.int g_epollFd;68.myevent_s g_Events[MAX_EVENTS+1]; // g_Events[MAX_EVENTS] is used by listenfd69.void RecvData(int fd, int events, void *arg);70.void SendData(int fd, int events, void *arg);71.// accept new connections from clients72.void AcceptConn(int fd, int events, void *arg)73.{74.struct sockaddr_in sin;75. socklen_t len = sizeof(struct sockaddr_in);76.int nfd, i;77.// accept78.if((nfd = accept(fd, (struct sockaddr*)&sin, &len)) == -1)79. {80.if(errno != EAGAIN && errno != EINTR)81. {82. printf("%s: bad accept", __func__);83. }84.return;85. }86.do87. {88.for(i = 0; i < MAX_EVENTS; i++)89. {90.if(g_Events[i].status == 0)91. {92.break;93. }94. }95.if(i == MAX_EVENTS)96. {97. printf("%s:max connection limit[%d].", __func__, MAX_EVENTS);98.break;99. }100.// set nonblocking101.if(fcntl(nfd, F_SETFL, O_NONBLOCK) < 0) break;102.// add a read event for receive data103. EventSet(&g_Events[i], nfd, RecvData, &g_Events[i]);104. EventAdd(g_epollFd, EPOLLIN|EPOLLET, &g_Events[i]);105. printf("new conn[%s:%d][time:%d]/n", inet_ntoa(sin.sin_addr), ntohs (sin.sin_port), g_Events[i].last_active);106. }while(0);107.}108.// receive data109.void RecvData(int fd, int events, void *arg)110.{111.struct myevent_s *ev = (struct myevent_s*)arg;112.int len;113.// receive data114. len = recv(fd, ev->buff, sizeof(ev->buff)-1, 0);115. EventDel(g_epollFd, ev);116.if(len > 0)117. {118. ev->len = len;119. ev->buff[len] = '/0';120. printf("C[%d]:%s/n", fd, ev->buff);121.// change to send event122. EventSet(ev, fd, SendData, ev);123. EventAdd(g_epollFd, EPOLLOUT|EPOLLET, ev);124. }125.else if(len == 0)126. {127. close(ev->fd);128. printf("[fd=%d] closed gracefully./n", fd);129. }130.else131. {132. close(ev->fd);133. printf("recv[fd=%d] error[%d]:%s/n", fd, errno, strerror(errno));134. }135.}136.// send data137.void SendData(int fd, int events, void *arg)138.{139.struct myevent_s *ev = (struct myevent_s*)arg;140.int len;141.// send data142. len = send(fd, ev->buff, ev->len, 0);143. ev->len = 0;144. EventDel(g_epollFd, ev);145.if(len > 0)146. {147.// change to receive event148. EventSet(ev, fd, RecvData, ev);149. EventAdd(g_epollFd, EPOLLIN|EPOLLET, ev);150. }151.else152. {153. close(ev->fd);154. printf("recv[fd=%d] error[%d]/n", fd, errno);155. }156.}157.void InitListenSocket(int epollFd, short port)158.{159.int listenFd = socket(AF_INET, SOCK_STREAM, 0);160. fcntl(listenFd, F_SETFL, O_NONBLOCK); // set non-blocking161. printf("server listen fd=%d/n", listenFd);162. EventSet(&g_Events[MAX_EVENTS], listenFd, AcceptConn, &g_Events[MAX_EVE NTS]);163.// add listen socket164. EventAdd(epollFd, EPOLLIN|EPOLLET, &g_Events[MAX_EVENTS]);165.// bind & listen166. sockaddr_in sin;167. bzero(&sin, sizeof(sin));168. sin.sin_family = AF_INET;169. sin.sin_addr.s_addr = INADDR_ANY;170. sin.sin_port = htons(port);171. bind(listenFd, (const sockaddr*)&sin, sizeof(sin));172. listen(listenFd, 5);173.}174.int main(int argc, char **argv)175.{176.short port = 12345; // default port177.if(argc == 2){178. port = atoi(argv[1]);179. }180.// create epoll181. g_epollFd = epoll_create(MAX_EVENTS);182.if(g_epollFd <= 0) printf("create epoll failed.%d/n", g_epollFd); 183.// create & bind listen socket, and add to epoll, set non-blocking 184. InitListenSocket(g_epollFd, port);185.// event loop186.struct epoll_event events[MAX_EVENTS];187. printf("server running:port[%d]/n", port);188.int checkPos = 0;189.while(1){190.// a simple timeout check here, every time 100, better to use a min i-heap, and add timer event191.long now = time(NULL);192.for(int i = 0; i < 100; i++, checkPos++) // doesn't check listen fd193. {194.if(checkPos == MAX_EVENTS) checkPos = 0; // recycle195.if(g_Events[checkPos].status != 1) continue;196.long duration = now - g_Events[checkPos].last_active;197.if(duration >= 60) // 60s timeout198. {199. close(g_Events[checkPos].fd);200. printf("[fd=%d] timeout[%d--%d]./n", g_Events[checkPos].fd, g_Events[checkPos].last_active, now);201. EventDel(g_epollFd, &g_Events[checkPos]);202. }203. }204.// wait for events to happen205.int fds = epoll_wait(g_epollFd, events, MAX_EVENTS, 1000); 206.if(fds < 0){207. printf("epoll_wait error, exit/n");208.break;209. }210.for(int i = 0; i < fds; i++){211. myevent_s *ev = (struct myevent_s*)events[i].data.ptr; 212.if((events[i].events&EPOLLIN)&&(ev->events&EPOLLIN)) // read ev ent213. {214. ev->call_back(ev->fd, events[i].events, ev->arg);215. }216.if((events[i].events&EPOLLOUT)&&(ev->events&EPOLLOUT)) // write event217. {218. ev->call_back(ev->fd, events[i].events, ev->arg);219. }220. }221. }222.// free resource223.return 0;224.}二、编目2Linux I/O多路复用技术在比较多的TCP网络服务器中有使用,即比较多的用到select 函数。