epoll有两种模式,Edge Triggered(简称ET) 和 Level Triggered.在采用这两种模式时要注意的是,如果采用ET模式,那么仅当状态发生变化时才会通知,而采用LT模式类似于原来的 select/poll操作,只要还有没有处理的事件就会一直通知.
以代码来说明问题:
首先给出server的代码,需要说明的是每次accept的连接,加入可读集的时候采用的都是ET模式,而且接收缓冲区是5字节的,也就是每次只接收5字节的数据:
#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>
#include <errno.h>
using namespace std;
#define MAXLINE 5
#define OPEN_MAX 100
#define LISTENQ 20
#define SERV_PORT 5000
#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;
//ev.events=EPOLLIN;
//注册epoll事件
epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev);
bzero(&serveraddr, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
char *local_addr="127.0.0.1";
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.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);
cout << "accapt a connection from " << str << endl;
//设置用于读操作的文件描述符
ev.data.fd=connfd;
//设置用于注测的读操作事件
ev.events=EPOLLIN|EPOLLET;
//ev.events=EPOLLIN;
//注册ev
epoll_ctl(epfd,EPOLL_CTL_ADD,connfd,&ev);
}
else if(events.events&EPOLLIN)
{
cout << "EPOLLIN" << endl;
if ( (sockfd = events.data.fd) < 0)
continue;
if ( (n = read(sockfd, line, MAXLINE)) < 0) {
if (errno == ECONNRESET) {
close(sockfd);
events.data.fd = -1;
} else
std::cout<<"readline error"<<std::endl;
} else if (n == 0) {
close(sockfd);
events.data.fd = -1;
}
line[n] = '\0';
cout << "read " << line << endl;
//设置用于写操作的文件描述符
ev.data.fd=sockfd;
//设置用于注测的写操作事件
ev.events=EPOLLOUT|EPOLLET;
//修改sockfd上要处理的事件为EPOLLOUT
//epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);
}
else if(events.events&EPOLLOUT)
{
sockfd = events.data.fd;
write(sockfd, line, n);
//设置用于读操作的文件描述符
ev.data.fd=sockfd;
//设置用于注测的读操作事件
ev.events=EPOLLIN|EPOLLET;
//修改sockfd上要处理的事件为EPOLIN
epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);
}
}
}
return 0;
}
下面给出测试所用的Perl写的client端,在client中发送10字节的数据,同时让client在发送完数据之后进入死循环, 也就是在发送完之后连接的状态不发生改变--既不再发送数据, 也不关闭连接,这样才能观察出server的状态:
#!/usr/bin/perl
use IO::Socket;
my $host = "127.0.0.1";
my $port = 5000;
my $socket = IO::Socket::INET->new("$host:$port") or die "create socket error $@";
my $msg_out = "1234567890";
print $socket $msg_out;
print "now send over, go to sleep\n";
while (1)
{
sleep(1);
}
运行server和client发现,server仅仅读取了5字节的数据,而client其实发送了10字节的数据,也就是说,server仅当第一次监听到了EPOLLIN事件,由于没有读取完数据,而且采用的是ET模式,状态在此之后不发生变化,因此server再也接收不到EPOLLIN事件了.
如果我们把client改为这样:
#!/usr/bin/perl
use IO::Socket;
my $host = "127.0.0.1";
my $port = 5000;
my $socket = IO::Socket::INET->new("$host:$port") or die "create socket error $@";
my $msg_out = "1234567890";
print $socket $msg_out;
print "now send over, go to sleep\n";
sleep(5);
print "5 second gonesend another line\n";
print $socket $msg_out;
while (1)
{
sleep(1);
}
可以发现,在server接收完5字节的数据之后一直监听不到client的事件,而当client休眠5秒之后重新发送数据,server再次监听到了变化,只不过因为只是读取了5个字节,仍然有10个字节的数据(client第二次发送的数据)没有接收完.
如果上面的实验中,对accept的socket都采用的是LT模式,那么只要还有数据留在buffer中,server就会继续得到通知,读者可以自行改动代码进行实验.
基于这两个实验,可以得出这样的结论:ET模式仅当状态发生变化的时候才获得通知,这里所谓的状态的变化并不包括缓冲区中还有未处理的数据,也就是说,如果要采用ET模式,需要一直read/write直到出错为止,很多人反映为什么采用ET模式只接收了一部分数据就再也得不到通知了,大多因为这样;而LT 模式是只要有数据没有处理就会一直通知下去的.
另外,从这个例子中,也可以阐述一些基本的网络编程概念.首先,连接的两端中,一端发送成功并不代表着对方上层应用程序接收成功, 就拿上面的client测试程序来说,10字节的数据已经发送成功,但是上层的server并没有调用read读取数据,因此发送成功仅仅说明了数据被对方的协议栈接收存放在了相应的buffer中,而上层的应用程序是否接收了这部分数据不得而知;同样的,读取数据时也只代表着本方协议栈的对应 buffer中有数据可读,而此时时候在对端是否在发送数据也不得而知.
转自:http://www.chinaunix.net/jh/23/1089208.html
分享到:
相关推荐
标题与描述中的“unbound + libevent + epoll学习”指向了深入探讨三个关键概念:Unbound(一个DNS解析器),Libevent(一个事件驱动库),以及epoll(一种高效的I/O多路复用机制)。这三者在现代网络编程、尤其是高...
epoll学习实例,epoll是Linux内核为处理大批量文件描述符而作了改进的poll,是Linux下多路复用IO接口select/poll的增强版本,它能显著提高程序在大量并发连接中只有少量活跃的情况下的系统CPU利用率。另一点原因就是...
在Linux系统中,`epoll`是用于高效网络编程的I/O多路复用机制,尤其适用于构建高性能的网络服务器。本笔记将深入探讨`epoll`的工作原理、两种主要模式——Edge Triggered (ET) 和 Level Triggered (LT),以及如何在...
最后,`分别使用epoll、libevent实现的端口映射程序,学习epoll,libevent.zip`包含了使用Epoll和libevent实现的端口映射程序,有助于比较两种I/O多路复用技术的差异。 综上所述,Epoll是Linux环境下实现高性能网络...
它主要涉及到TCP/UDP协议以及select/poll/epoll等多路复用技术。 TCP/UDP协议是网络通信的基础,其中TCP协议提供面向连接的可靠数据传输,而UDP协议则提供无连接的不可靠数据传输。在Linux网络编程中,开发者需要...
附件为本人整理的libevent的入门学习资料,时候零基础学习。
**Epoll:Linux异步I/O的高效实现** 在Linux操作系统中,Epoll是一种用于处理大量并发I/O操作的高效机制。它属于I/O多路复用技术的一种,旨在优化传统的select和poll方法,尤其适合高并发服务器场景。Epoll在C++...
【epoll内核代码学习】 epoll是一种高效、可扩展的I/O事件通知机制,用于Linux内核中,尤其适用于高并发的网络编程。本文将深入解析epoll的内核实现,探讨其核心数据结构、关键操作以及与select的对比。 ### 第一...
在Linux系统中,`epoll`是用于I/O...综上所述,"Linux C++ epoll使用范例"中的代码可以作为学习和实践`epoll`的参考,通过客户端、服务端及测试程序,读者可以深入理解`epoll`的工作机制,并能实际操作中提升系统性能。
这个Demo对于学习如何在Linux环境下使用QT结合Epoll开发高并发服务器具有很高的参考价值。通过理解Epoll的工作原理和QT的网络编程模型,开发者可以构建出性能优异、能够应对大量并发连接的服务器应用。
**epoll**是Linux内核提供的一种高效I/O事件通知机制,它替代了传统的`poll`和`select`,在高并发I/O处理场景...学习和掌握`epoll`,尤其是其两种工作模式,对于提升服务器性能和处理高并发I/O场景有着至关重要的作用。
Linux的epoll是一种I/O多路...总之,`linux epoll 例子程序`提供了学习和理解epoll机制的实践平台,通过对这些代码的学习,我们可以深入理解epoll如何提升服务器处理并发能力,并掌握在实际项目中如何运用这一技术。
通过阅读和学习这个代码,初学者不仅能了解epoll的基本用法,还能深入理解如何在实际项目中优雅地封装和使用系统调用,提升系统编程能力。同时,对于开发者来说,这样的封装也便于代码的维护和复用。
总之,"python-epoll-examples"这个压缩包是学习Python在高并发场景下使用epoll的宝贵资源,通过实践这些示例,你将能够熟练掌握epoll的使用,提高你的网络编程能力。在实际项目中,结合epoll的高效特性,可以构建出...
**epoll_serverANDclient**项目提供了...通过这个项目,我们可以学习如何在C++中使用`epoll`实现高性能的客户端和服务器,理解其在并发I/O处理中的优势。在实际开发中,这种技术常用于构建高可用、高性能的网络服务。
Linux下的epoll是一种高效、可扩展的I/O多路复用技术,主要用于处理大量并发连接。它是基于事件驱动的I/O模型,适用于高并发服务器,如Web服务器、...在`epoll.cpp`代码中,我们可以深入学习如何在实践中应用这些概念。
总之,epoll回射服务器是一个实用的学习项目,它涵盖了网络编程的核心概念,如套接字、I/O多路复用以及C++的内存管理和异常处理。初学者可以通过这个项目深入理解epoll的工作原理,并为更复杂的网络应用打下基础。
学习epoll、多线程和线程池的知识,首先需要理解它们的基本原理,然后掌握如何在实际项目中应用。对于epoll,需要了解如何创建和管理epoll实例,以及如何正确设置和处理I/O事件。对于多线程,要学习线程的创建、同步...
总的来说,这个压缩包提供了学习和理解epoll如何在实际项目中应用于客户端-服务器通信的一个实例。通过阅读和分析源代码,开发者可以深入理解epoll的用法及其在高性能网络编程中的优势。同时,这也为实现其他基于...
在IT行业中,网络编程是构建高性能服务的基础,而epoll是Linux系统中用于高并发I/O操作的关键技术。...在csmodel_v5.2这个版本中,可能包含了实现这一模型的具体代码和配置文件,供开发者学习和参考。