epoll学习ITeye快报 - 乐橙lc8

epoll学习ITeye快报

2019-02-06 07:56:20 | 作者: 云天 | 标签: 数据,事情,形式 | 浏览: 7261

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.fdlistenfd)
  {
  connfd = accept(listenfd,(sockaddr *) clientaddr, clilen);
  if(connfd 0){
  perror("connfd
  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第2次发送的数据)没有接纳完.

假如上面的试验中,对accept的socket都选用的是LT形式,那么只需还有数据留在buffer中,server就会持续得到告诉,读者能够自行改动代码进行试验.

根据这两个试验,能够得出这样的定论:ET形式仅当状况发作改变的时分才取得告诉,这儿所谓的状况的改变并不包含缓冲区中还有未处理的数据,也就是说,假如要选用ET形式,需求一向read/write直到犯错停止,很多人反映为什么选用ET形式只接纳了一部分数据就再也得不到告诉了,大多由于这样;而LT 形式是只需有数据没有处理就会一向告诉下去的.

别的,从这个比如中,也能够论述一些根本的网络编程概念.首要,衔接的两头中,一端发送成功并不代表着对方上层应用程序接纳成功, 就拿上面的client测验程序来说,10字节的数据现已发送成功,可是上层的server并没有调用read读取数据,因而发送成功只是阐明晰数据被对方的协议栈接纳存放在了相应的buffer中,而上层的应用程序是否接纳了这部分数据不得而知;相同的,读取数据时也只代表着本方协议栈的对应 buffer中有数据可读,而此刻时分在对端是否在发送数据也不得而知.

转自:https://www.chinaunix.net/jh/23/1089208.html
版权声明
本文来源于网络,版权归原作者所有,其内容与观点不代表乐橙lc8立场。转载文章仅为传播更有价值的信息,如采编人员采编有误或者版权原因,请与我们联系,我们核实后立即修改或删除。

猜您喜欢的文章

阅读排行

  • 1
  • 2

    常用工具alibaba

    日志,检查,监控
  • 3
  • 4
  • 5

    pthread快报

    线程,运转,调用
  • 6

    linux 解压rarmingxing

    原生,指令
  • 7

    Linux常用端口sohu

    协议,效劳,端口
  • 8

    apache 的modguojizaixian

    状况,指令,检查
  • 9

    epoll学习ITeye快报

    数据,事情,形式
  • 10

    linux sort uniqITeyealibaba

    排序,文件,字符