epoll的操作就这么简单,总共不过4个API:epoll_create, epoll_ctl, epoll_wait和close.但凡作过比较深入的网络编程的人,都会知道,在win平台下,高效的IO模型是IOCP,而在linux底下则是 epoll。那么,epoll与iocp之间到底有哪些异同之处呢?
目前国内的网游研发,在服务器使用的开发平台方面,win和 linux的比例各占多少,我一时半会也没有准确数据,但从我了解的这么多公司情况来看,用win系统的还是比较多一点,这些企业一般都是比较单纯的网游 公司,而用linux的则多数是一些传统的互联网公司,比如网易和腾讯。
网游服务器用win还是linux,向来都是大家关注的话题。我想,原因可能很多,但此处不想过多论述这个问题,为避免多费口舌,我还是明确表明一下自己的观点:我是推荐用linux作开发的,虽然我也是刚转来作linux平台下的开发。
那么,说具体一点。但凡作过比较深入的网络编程的人,都会知道,在win平台下,高效的IO模型是IOCP,而在linux底下则是epoll。那么,epoll与iocp之间到底有哪些异同之处呢?
首先,我们看一下它们相同的地方。
两者都是处理异步IO的高效模型,这种高效,除了“异步处理”这个共同的特征之外,二者都可以通过指针携带应用层数据:在IOCP里,应用层数据可以通过 单句柄数据和单IO数据来与IOCP底层通信;而在epoll里,可以通过epoll_data里的"void *ptr"来传递。这是一种很重要的思想,也是它们高效的原因所在:当事件的通知到来时,它不仅告诉你发生了什么样的事件,还同时告诉这次事件所操作的数 据是哪些。
那么,epoll和iocp到底又有什么不同呢?
以我目前粗浅的使用经验来看,至少可以得到以下结论:
1.iocp是在IO操作完成之后,才通过get函数返回这个完成通知的;而epoll则不是在IO操作完成之后才通知你,它的工作原理是,你如果想进行 IO操作时,先向epoll查询是否可读或可写,如果处于可读或可写状态后,epoll会通过epoll_wait函数通知你,此时你再进行进一步的 recv或send操作。
2.在1的基础上,我们其实可以看到,epoll仅仅是一个异步事件的通知机制,其本身并不作任何的IO读写操作,它只负责告诉你是不是可以读或可以写 了,而具体的读写操作,还要应用层自己来作;但iocp的封装就要多一些,它不仅会有完成之后的事件通知,更重要的是,它同时封装了一部分的IO控制逻 辑。从这一点上来看,iocp的封装似乎更全面一点,但是,换个角度看,epoll仅提供这种机制也是非常好的,它保持了事件通知与IO操作之间彼此的独 立性,使得epoll的使用更加灵活。
这只是我初步使用epoll开发过程中的体会,以后有更深的体会时还会发上来跟大家分享。
#include <iostream>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#define MAXLINE 10
#define OPEN_MAX 100
#define LISTENQ 20
#define SERV_PORT 5555
#define INFTIM 1000
void setnonblocking(int sock)
{
int opts;
opts=fcntl(sock,F_GETFL);
if(opts<0)
{
perror("fcntl(sock,GETFL)");
exit(1);
}
opts = opts|O_NONBLOCK;
if(fcntl(sock,F_SETFL,opts)<0)
{
perror("fcntl(sock,SETFL,opts)");
exit(1);
}
}
int main()
{
int i, maxi, listenfd, connfd, sockfd,epfd,nfds;
ssize_t n;
char line[MAXLINE];
socklen_t clilen;
//声明epoll_event结构体的变量,ev用于注册事件,数组用于回传要处理的事件
struct epoll_event ev,events[20];
//生成用于处理accept的epoll专用的文件描述符
epfd=epoll_create(256);
struct sockaddr_in clientaddr;
struct sockaddr_in serveraddr;
listenfd = socket(AF_INET, SOCK_STREAM, 0);
//把socket设置为非阻塞方式
setnonblocking(listenfd);
//设置与要处理的事件相关的文件描述符
ev.data.fd=listenfd;
//设置要处理的事件类型
ev.events=EPOLLIN|EPOLLET;
//注册epoll事件
epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev);
bzero(&serveraddr, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
char *local_addr="200.200.200.204";
inet_aton(local_addr,&(serveraddr.sin_addr));//htons(SERV_PORT);
serveraddr.sin_port=htons(SERV_PORT);
bind(listenfd,(sockaddr *)&serveraddr, sizeof(serveraddr));
listen(listenfd, LISTENQ);
maxi = 0;
for ( ; ; ) {
//等待epoll事件的发生
nfds=epoll_wait(epfd,events,20,500);
//处理所发生的所有事件
for(i=0;i<nfds;++i)
{
if(events[i].data.fd==listenfd)
{
connfd = accept(listenfd,(sockaddr *)&clientaddr, &clilen);
if(connfd<0){
perror("connfd<0");
exit(1);
}
setnonblocking(connfd);
char *str = inet_ntoa(clientaddr.sin_addr);
std::cout<<"connect from "<_u115 tr<<std::endl;
//设置用于读操作的文件描述符
ev.data.fd=connfd;
//设置用于注测的读操作事件
ev.events=EPOLLIN|EPOLLET;
//注册ev
epoll_ctl(epfd,EPOLL_CTL_ADD,connfd,&ev);
}
else if(events[i].events&EPOLLIN)
{
if ( (sockfd = events[i].data.fd) < 0) continue;
if ( (n = read(sockfd, line, MAXLINE)) < 0) {
if (errno == ECONNRESET) {
close(sockfd);
events[i].data.fd = -1;
} else
std::cout<<"readline error"<<std::endl;
} else if (n == 0) {
close(sockfd);
events[i].data.fd = -1;
}
//设置用于写操作的文件描述符
ev.data.fd=sockfd;
//设置用于注测的写操作事件
ev.events=EPOLLOUT|EPOLLET;
//修改sockfd上要处理的事件为EPOLLOUT
epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);
}
else if(events[i].events&EPOLLOUT)
{
sockfd = events[i].data.fd;
write(sockfd, line, n);
//设置用于读操作的文件描述符
ev.data.fd=sockfd;
//设置用于注测的读操作事件
ev.events=EPOLLIN|EPOLLET;
//修改sockfd上要处理的事件为EPOLIN
epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);
}
}
}
}
相关推荐
赠送jar包:netty-transport-classes-epoll-4.1.74.Final.jar; 赠送原API文档:netty-transport-classes-epoll-4.1.74.Final-javadoc.jar; 赠送源代码:netty-transport-classes-epoll-4.1.74.Final-sources.jar;...
赠送jar包:netty-transport-classes-epoll-4.1.73.Final.jar; 赠送原API文档:netty-transport-classes-epoll-4.1.73.Final-javadoc.jar; 赠送源代码:netty-transport-classes-epoll-4.1.73.Final-sources.jar;...
epoll机制epoll_create、epoll_ctl、epoll_wait、close(在epoll的ET模式下,read和write或send和recv当返回值0且errno=EAGAIN - linking530的专栏 - CSDN博客.mht
linux socket tcp大并发 epoll使用教程 有关epoll的一切
在本部分我们实现一个较为好用实用的Epoll并发类, 由于实现代码与使用方式较简单, 因此就不在此赘述了, 下面我还使用了该类实现了一个基于Epoll的echo-server, 以演示该类的用法; 由于此处仅为Epoll类库的第一个...
Lua的epoll模块 更多细节,请查看sample.lua API: ok,err=epoll.setnonblocking(fd) 设置一个文件描述符非阻塞。 epfd,err=epoll.create() 创建一个 epoll 文件描述符。ok,err=epoll.register(epfd,fd,event...
linux epoll多线程编程 例子
下面文章在这段时间内研究 select/poll/epoll的内核实现的一点心得体会: select,poll,epoll都是多路复用IO的函数,简单说就是在一个线程里,可以同时处理多个文件描述符的读写。 select/poll的实现很类似,epoll...
3)用到了epoll_event结构体中的 epoll_data.ptr 指针:把客户端的信息保存在 epoll_data.ptr 指针当中,当出现epoll事件的时候从该指针提取客户端信息,然后进行操作。 完整的c代码,可以直接使用gcc编译,编译...
服务器与客户端建立连接需要使用到一些接口,包括但不限于...epollepoll底层通过红黑树来描述,并维护一个ready list,将事件表中已经就绪的事件添加到这里,在使用epoll_wait调用时,仅观察这个list中有没有数据即可。
把epoll接收数据封装为接口形式供线程调用
linux系统下,利用epoll接收串口助手发来的数据并打印。
linux下完整的epoll多线程高并发服务器代码
Linux下通过epoll机制进行串口监听,当收到数据时,通过tcp进行数据转发给服务器
epoll反应堆模型代码,相对于网上普通的epoll模型增加了send recv 操作,并有详细的注释epoll反应堆
赠送jar包:netty-transport-classes-epoll-4.1.73.Final.jar; 赠送原API文档:netty-transport-classes-epoll-4.1.73.Final-javadoc.jar; 赠送源代码:netty-transport-classes-epoll-4.1.73.Final-sources.jar;...
它主要涉及到TCP/UDP协议以及select/poll/epoll等多路复用技术。 TCP/UDP协议是网络通信的基础,其中TCP协议提供面向连接的可靠数据传输,而UDP协议则提供无连接的不可靠数据传输。在Linux网络编程中,开发者需要...
epoll多路IO转接机制:常用STL(vector, map)、文件读写(fstream)、jsoncpp解析json数据、MySQL基本操作; 实现的功能:注册、单点登录、登出、群聊(支持文本和图片的传送)、上线下线公告、在线用户记录;
FIFO配合管道PIPE,实现FIFO传递数据,PIPE触发epoll,异步IO效果
不管epoll管理的连接有多少,epoll_wait都会在常数时间内返回。而使用LT模式时,epoll_wait会去遍历所有连接的状态,只要某个连接的接收缓冲区中还有数据,epoll_wait就会报告。可以看出在LT模式下,epoll管理的连接...