I/O复用之 EPOLLONESHOT 事件

EPOLLONESHOT 事件


  • EPOLLONESHOT 事件
      • 1. 使用EPOLLONESHOT的原因及优点
      • 2. recv返回值 及 与errno的配合使用
      • 3. 示例程序


1. 使用EPOLLONESHOT的原因及优点

即使使用ET模式,一个socket上的某个事件还是可能被触发多次。比如:一个线程在读取完某个socket上的数据后开始处理这些数据,而在数据的处理过程中该socket上又有新数据可读(EPOLLIN再次被触发),此时另外一个线程被唤醒用来读取这些新的数据。于是就出现了两个线程同时操作一个socket的局面。而我们希望一个socket连接在任一时刻都只能被一个线程处理,这就可以通过EPOLLONESHOT事件实现。
对于注册了EPOLLONESHOT事件的文件描述符,操作系统最多触发其上注册的一个事件,且只触发一次,除非我们使用epoll_ctl函数重置该文件描述符上注册的EPOLLONESHOT事件。这样,在一个线程使用socket时,其他线程无法操作socket。同样,只有在该socket被处理完后,须立即重置该socket的EPOLLONESHOT事件,以确保这个socket在下次可读时,其EPOLLIN事件能够被触发,进而让其他线程有机会操作这个socket。


2. recv返回值 及 与errno的配合使用

在编写程序时,发现对recv函数返回值意义仍然不清,导致部分代码含义没看懂,在此特别记录一下,以下用ret表示recv函数的返回值。

(1) ret == 0

表示对方断开连接。当客户端不与服务端交互数据好长时间之后,服务端程序会自动断开连接(当客户端recv时),同时客户端的连接状态变成了 CLOSE_WAIT,如果客户端再向服务端发送数据,然后recv服务端的反馈时,就会造成recv返回0。

(2) ret > 0
表示接收到数据。ret值为接收数据的字节数。

(3) ret < 0
表示recv函数出错。此时分为多种情况,仅介绍以下情况:

  • errno == EAGAIN 表示数据未读完。数据量太大,一次发送导致缓冲区满,需要再次检查是否还有未读取数据。

在此引用其他大佬的思考:
当对侧没有send,即本侧的套接字s的接收缓冲区无数据,返回值是?(EAGAIN,原因为超时,待测)


3. 示例程序

/* EPOLLONESHOT 事件的使用
** 运行命令: g++ filename.cpp -lpthread; ./a.out 127.0.0.1 6666
** 可以使用telnet连接该服务器(telnet 127.0.0.1 6666)
*/#include <iostream>
#include <cstdio>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/sendfile.h>
#include <unistd.h>
#include <signal.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstdlib>
#include <cstring>
#include <fcntl.h>
#include <sys/epoll.h>
#include <pthread.h>
using namespace std;const int MAX_EVENT_NUMBER = 1024;
const int BUF_SIZE = 1024;struct fds{int epoll_fd;int sock_fd;
};void err( int line ) {cout << "error_line: " << line << endl;
}int setnonblocking( int fd ) {int old_option = fcntl( fd, F_GETFL );int new_option = old_option | O_NONBLOCK;fcntl( fd, F_SETFL, new_option );return old_option;
}void addfd( int epoll_fd, int fd, bool oneshot ) {epoll_event event;event.data.fd = fd;event.events = EPOLLIN | EPOLLET;if ( oneshot ) {event.events |= EPOLLONESHOT;}epoll_ctl( epoll_fd, EPOLL_CTL_ADD, fd, &event );setnonblocking( fd );
}void reset_oneshot( int epoll_fd, int fd ) {epoll_event event;event.data.fd = fd;event.events = EPOLLIN | EPOLLET | EPOLLONESHOT;epoll_ctl( epoll_fd, EPOLL_CTL_MOD, fd, &event );
}void * worker( void * arg ) {int sock_fd = ( (fds *)arg )->sock_fd;int epoll_fd = ( (fds *)arg )->epoll_fd;printf("start new thread to receive data on fd: %d\n", sock_fd);char buf[BUF_SIZE];memset( buf, 0, sizeof( buf ) );while( true ) {int ret = recv( sock_fd, buf, BUF_SIZE - 1, 0 );if ( !ret ) {close( sock_fd );cout << "foreiner closed the connection\n";break;} else if ( ret < 0 ) {if ( errno == EAGAIN ) {  //数据未读完,需要再次读取reset_oneshot( epoll_fd, sock_fd );cout << "read later\n";break;}} else {printf( "got %d bytes of data: %s\n", ret, buf );sleep(5);}}printf("end thread receiving data on fd: %d\n", sock_fd);
}int main( int argc, char * argv[] ) {if ( argc < 3 ) {printf( "usage: ./file ip_number port_number\n" );return 1;}const char * ip = argv[1];const int port = atoi( argv[2] );struct sockaddr_in address;memset( &address, 0, sizeof( address ) );address.sin_family = AF_INET;address.sin_port = htons( port );inet_pton( AF_INET, ip, &address.sin_addr );int sock_fd = socket( AF_INET, SOCK_STREAM, 0 );if ( sock_fd < 0 ) {err( __LINE__ );}int ret = bind( sock_fd, ( struct sockaddr * )&address, sizeof( address ) );if ( sock_fd < 0 ) {err( __LINE__ );}ret = listen( sock_fd, 5 );if ( sock_fd < 0 ) {err( __LINE__ );}epoll_event events[MAX_EVENT_NUMBER];int epoll_fd = epoll_create( 5 );if ( epoll_fd < 0 ) {err( __LINE__ );}/* 监听socket的 sock_fd 不能注册 EPOLLONESHOT 事件,否则程序只能处理一个客户连接** 因为后续的客户连接请求将不再触发sock_fd的 EPOLLIN 事件*/addfd( epoll_fd, sock_fd, false );while( true ) {ret = epoll_wait( epoll_fd, events, MAX_EVENT_NUMBER, -1 );  //等待事件发生if ( ret < 0 ) {printf( "epoll failure\n" );break;}for ( int i = 0; i < ret; i++ ) {int fd = events[i].data.fd;if ( fd == sock_fd ) {  //有新的连接请求struct sockaddr_in client;socklen_t client_length = sizeof( client );int conn_fd = accept( sock_fd, ( struct sockaddr * )&client,&client_length );//对每个非监听文件描述符都注册 EPOLLONESHOT 事件//添加的是刚accept的fdaddfd( epoll_fd, conn_fd, true );} else if ( events[i].events & EPOLLIN ) {  //有可读取数据pthread_t thread;fds fds_for_new_worker;fds_for_new_worker.epoll_fd = epoll_fd;fds_for_new_worker.sock_fd = fd;  //内核事件表中的fd,不要搞混//新启动一个线程为sock_fd服务pthread_create( &thread, NULL, worker, (void *)&fds_for_new_worker );} else {printf( "something else happened\n" );}}}close(sock_fd);return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/381951.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

uva 1612——Guess

题意&#xff1a;有n个选手参加比赛&#xff0c;比赛有3个题目&#xff0c;每个选手每个题目都有一个评测之前的于得分&#xff0c;当通过题目时才可以得到相应分数&#xff0c;否则为0&#xff0c;然后按照得分排名&#xff0c;id小的排在前面&#xff0c;现在给定选手的于得分…

uva 1611——Crane

题意&#xff1a;给定一个1-n的排序&#xff0c;每次可以选定一个偶数长的序列&#xff0c;然后交换前一部分和后一部分&#xff0c;使得最后的成为1-n的序列。求最后的次数和每次的策略。 思路&#xff1a;贪心。每次贪心的策略都是把i放到第i位置上&#xff0c;交换的时候找到…

【世界上最美丽的7张太阳照片】

【世界上最美丽的&#xff17;张太阳照片】 文章来源:http://ms.blogger.cn/peon/posts/1634.aspx转载于:https://www.cnblogs.com/paulzhu/archive/2004/02/24/1572.html

uva 11925——Generating Permutations

题意&#xff1a;给定一个1-n的排列&#xff0c;用不超过2*n2的操作把他变成升序。每次操作只有两种&#xff0c;一种是交换前两个元素&#xff0c;另外一种是把最后一个元素放到最后一位。 思路&#xff1a;贪心。用双端队列来保存数据&#xff0c;每次当v[0]>v[1]&&am…

二维map —— HDU1263

水果 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 8752 Accepted Submission(s): 3482Problem Description 夏天来了~~好开心啊,呵呵,好多好多水果~~ Joe经营着一个不大的水果店.他认为生存之道就是经营最…

uva 11491——Erasing and Winning

题意&#xff1a;给定一个n位的整数&#xff0c;要求从中去掉k位&#xff0c;使得剩下的数字最大。 思路&#xff1a;单调队列。在满足删除的数等于k 的前提下求一个不敌减的序列。 code&#xff1a; #include <iostream> #include <cstdio> #include <cmath>…

工作组和域的区别

局域网上的资源需要管理&#xff0c;“域”和“工作组”就是两种不同的网络资源管理模式。那么二者有何区别呢&#xff1f;看了这篇文章&#xff0c;您就会明白了。工作组 Work Group在一个网络内&#xff0c;可能有成百上千台电脑&#xff0c;如果这些电脑不进行分组&#xff…

uva 12545——Bits Equalizer

题意&#xff1a;给定两个等长的串s和t&#xff0c;其中s串包括0&#xff0c;1&#xff0c;&#xff1f;&#xff0c;T包含0&#xff0c;1&#xff0c;每次可以把s中的0变成1或者交换任意两个数或者&#xff1f;变成任意01.求由s到t的最小步数。 思路&#xff1a;杂。所有带&am…

Ananagrams Uva 156

Ananagrams (map的使用) Most crossword puzzle fans are used to anagrams–groups of words with the same letters in different orders–for example OPTS, SPOT, STOP, POTS and POST. Some words however do not have this attribute, no matter how you rearrange their…

流星

我的心情像下雨故乱飞溅 静落一地因为没有你 我的心情像流星划破天际 惨淡的光阴因为没有你 我的心情像风铃没有主见 无法成形因为没有你 我的心情像问号钩子心碎 一点茫然因为没有你 我的心情像冰块没有温度 化了一地因为没有你 我好想你我好想你我好想你 http://photo.…

uva 1623——Enter The Dragon

题意&#xff1a;有n个装满水的湖&#xff0c;可以预知将来m天下雨情况&#xff0c;每次下满一个湖&#xff0c;或者不下&#xff0c;不下雨的时候可以让某个湖变干&#xff0c;问是否存在一种方案使得每次下雨之前湖总是干的。 思路&#xff1a;贪心。什么时候下雨&#xff0c…

CodeForce 2A —— Winner

A. Winner The winner of the card game popular in Berland “Berlogging” is determined according to the following rules. If at the end of the game there is only one player with the maximum number of points, he is the winner. The situation becomes more diffi…

GARFIELD@12-30-2004

tele-murderer 转载于:https://www.cnblogs.com/rexhost/archive/2004/12/30/84230.html

[codevs] 1098 均分纸牌

均分纸牌 题目描述 Description 有 N 堆纸牌&#xff0c;编号分别为 1&#xff0c;2&#xff0c;…, N。每堆上有若干张&#xff0c;但纸牌总数必为 N 的倍数。可以在任一堆上取若于张纸牌&#xff0c;然后移动。   移牌规则为&#xff1a;在编号为 1 堆上取的纸牌&#xf…

uva 11536——Smallest Sub-Array

题意&#xff1a;给定n个数&#xff0c;这n个数在m的范围之内。v[i](v[i-1]v[i-2]v[i-3])%m1; 然后求一个最短连续子序列使得序列内包括1-k个数。 思路&#xff1a;枚举。枚举以v[i]结尾的最短序列。用ct维护已经找到的k个数中的个数&#xff0c;queue跳转区间。 code&#x…

好消息,关于2005的default provider

好消息&#xff0c;关于2005的default provider 看到一则消息&#xff0c;说在vs.net 2005 bETA 2以后&#xff0c;关于Membership, Roles, Profile and Personalization 的会默认使用 SQL SERVER 2005的provider(现在BETA 1使用的是ACCESS&#xff0c;所以在做MEMBERSHIP&…

[Qt] 解决toggled无法触发setVisible

解决toggled无法触发setVisible 解决方法&#xff1a; 在QT Designer中&#xff0c;创建QPushButton时需要将按钮修改为checkable。在默认情况下&#xff0c;checkable是不选中的&#xff0c;默认为触发按钮&#xff08;trigger button&#xff09;&#xff0c;也就是按下之后…

周华健,歌声伴我成长(四)

1997年&#xff0c;经典的朋友《朋友》 朋友 这些年一个人 风也过雨也走 有过泪有过错 还记得坚持甚麽 真爱过才会懂 会寂寞会回首 终有梦终有你在心中 朋友一生一起走 那些日子不再有 一句话一辈子 一生情一杯酒 朋友不曾孤单过 一声朋友你会懂 还有伤还有痛 还要走还有我 199…

hihoCoder挑战赛16 A—— 王胖浩与三角形

思路&#xff1a;开始没有思路&#xff0c;想到了用三边乱搞&#xff08;每条边按照比例增加&#xff09;然而样例都无法通过。后来想到了海伦公式sqrt((abc)(ab-c)(bc-a)(ac-b))/4&#xff0c;那么这样以来就是让这个三角形趋于正三角形了&#xff0c;即三边的方差最小&#x…

虚析构函数

需要虚析构函数的原因&#xff1a; 首先看一下这段代码&#xff1a; #include <iostream> using namespace std;class A { private: int *a; public: A() { a new int; cout << "A::A() is called.\n"; }~A() { delete a; cout << "A::~A(…