博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
关于epoll的示例
阅读量:5312 次
发布时间:2019-06-14

本文共 6748 字,大约阅读时间需要 22 分钟。

下午研究了一下epoll,参考了以下的博客综合写了一个例子。

这篇文章中有一些和我从man上面查到的不相符合的地方,特此指出。

1)关于epoll_create

这个函数的size参数已经器用。更推荐使用的是epoll_create1(0)来代替普通的用法。另外epoll_create1(EPOLLCLOEXEC)表示生成的epoll fd具有“执行后关闭”特性。

2) epoll_ctl

这个函数在指定EPOLL_CTL_DEL时,为了与linux内核2.6.9之前相兼容,还是要让最后的参数指向一个非null变量。

另外,events.EPOLLONESHOT确实表示只监听一次事件,但是当我们监听完这次事件之后,如果还需要继续监听这个fd的话,只需要使用EPOLL_CTL_MOD修改event。

 

3) 关于实例代码

实例代码我运行了一下,感觉有点问题。后来参考了这篇文章()的说法,发现修改之后就可以实行了。关键点有这么几点,

1. EPOLLET其实比EPOLLLT高级,所以优先用。

2. 用EPOLLET的时候,按照man的讲法,是必须要使用非阻塞fd,另外,必须要考虑EAGAIN。

 

先上服务器代码

1 #include 
2 #include
3 #include
4 #include
5 #include
6 #include
7 #include
8 #include
9 #include
10 #include
11 12 using namespace std; 13 14 #define MAXLINE 5 15 #define OPEN_MAX 100 16 #define LISTENQ 20 17 #define SERV_PORT 5000 18 #define INFTIM 1000 19 20 void setnonblocking(int sock) 21 { 22 int opts; 23 opts=fcntl(sock,F_GETFL); 24 if(opts<0) 25 { 26 perror("fcntl(sock,GETFL)"); 27 return; 28 } 29 opts = opts|O_NONBLOCK; 30 if(fcntl(sock,F_SETFL,opts)<0) 31 { 32 perror("fcntl(sock,SETFL,opts)"); 33 return; 34 } 35 } 36 37 void CloseAndDisable(int sockid, epoll_event ee) 38 { 39 close(sockid); 40 ee.data.fd = -1; 41 } 42 43 int main() 44 { 45 int i, maxi, listenfd, connfd, sockfd,epfd,nfds, portnumber; 46 char line[MAXLINE]; 47 socklen_t clilen; 48 49 portnumber = 5000; 50 51 //声明epoll_event结构体的变量,ev用于注册事件,数组用于回传要处理的事件 52 53 struct epoll_event ev,events[20]; 54 //生成用于处理accept的epoll专用的文件描述符 55 56 epfd=epoll_create(256); 57 struct sockaddr_in clientaddr; 58 struct sockaddr_in serveraddr; 59 listenfd = socket(AF_INET, SOCK_STREAM, 0); 63 64 memset(&serveraddr, 0, sizeof(serveraddr)); 65 serveraddr.sin_family = AF_INET; 66 serveraddr.sin_addr.s_addr = htonl(INADDR_ANY); 67 serveraddr.sin_port=htons(portnumber); 68 69 // bind and listen 70 bind(listenfd,(sockaddr *)&serveraddr, sizeof(serveraddr)); 71 listen(listenfd, LISTENQ); 72 73 //设置与要处理的事件相关的文件描述符 74 ev.data.fd=listenfd; 75 //设置要处理的事件类型 76 ev.events=EPOLLIN|EPOLLET; 77 //ev.events=EPOLLIN; 78 79 //注册epoll事件 80 epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev); 81 82 maxi = 0; 83 84 int bOut = 0; 85 for ( ; ; ) 86 { 87 if (bOut == 1) 88 break; 89 //等待epoll事件的发生 90 91 nfds=epoll_wait(epfd,events,20,-1); 92 //处理所发生的所有事件 93 cout << "\nepoll_wait returns\n"; 94 95 for(i=0;i
< 0)124 continue;125 126 char * head = line;127 int recvNum = 0;128 int count = 0;129 bool bReadOk = false;130 while(1)131 {132 // 确保sockfd是nonblocking的133 recvNum = recv(sockfd, head + count, MAXLINE, 0);134 if(recvNum < 0)135 {136 if(errno == EAGAIN)137 {138 // 由于是非阻塞的模式,所以当errno为EAGAIN时,表示当前缓冲区已无数据可读139 // 在这里就当作是该次事件已处理处.140 bReadOk = true;141 break;142 }143 else if (errno == ECONNRESET)144 {145 // 对方发送了RST146 CloseAndDisable(sockfd, events[i]);147 cout << "counterpart send out RST\n";148 break;149 }150 else if (errno == EINTR)151 {152 // 被信号中断153 continue;154 }155 else156 {157 //其他不可弥补的错误158 CloseAndDisable(sockfd, events[i]);159 cout << "unrecovable error\n";160 break;161 }162 }163 else if( recvNum == 0)164 {165 // 这里表示对端的socket已正常关闭.发送过FIN了。166 CloseAndDisable(sockfd, events[i]);167 cout << "counterpart has shut off\n";168 break;169 }170 171 // recvNum > 0172 count += recvNum;173 if ( recvNum == MAXLINE)174 {175 continue; // 需要再次读取176 }177 else // 0 < recvNum < MAXLINE178 {179 // 安全读完180 bReadOk = true;181 break; // 退出while(1),表示已经全部读完数据182 }183 }184 185 if (bReadOk == true)186 {187 // 安全读完了数据188 line[count] = '\0';189 190 cout << "we have read from the client : " << line;191 //设置用于写操作的文件描述符192 193 ev.data.fd=sockfd;194 //设置用于注测的写操作事件195 196 ev.events = EPOLLOUT | EPOLLET;197 //修改sockfd上要处理的事件为EPOLLOUT198 199 epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);200 }201 }202 else if(events[i].events & EPOLLOUT) // 如果有数据发送203 {204 const char str[] = "hello from epoll : this is a long string which may be cut by the net\n";205 memcpy(line, str, sizeof(str));206 cout << "Write " << line << endl;207 sockfd = events[i].data.fd;208 209 bool bWritten = false;210 int writenLen = 0;211 int count = 0;212 char * head = line;213 while(1)214 {215 // 确保sockfd是非阻塞的216 writenLen = send(sockfd, head + count, MAXLINE, 0);217 if (writenLen == -1)218 {219 if (errno == EAGAIN)220 {221 // 对于nonblocking 的socket而言,这里说明了已经全部发送成功了222 bWritten = true;223 break;224 }225 else if(errno == ECONNRESET)226 {227 // 对端重置,对方发送了RST228 CloseAndDisable(sockfd, events[i]);229 cout << "counterpart send out RST\n";230 break;231 }232 else if (errno == EINTR)233 {234 // 被信号中断235 continue;236 }237 else238 {239 // 其他错误240 }241 }242 243 if (writenLen == 0)244 {245 // 这里表示对端的socket已正常关闭.246 CloseAndDisable(sockfd, events[i]);247 cout << "counterpart has shut off\n";248 break;249 }250 251 // 以下的情况是writenLen > 0252 count += writenLen;253 if (writenLen == MAXLINE)254 {255 // 可能还没有写完256 continue;257 }258 else // 0 < writenLen < MAXLINE259 {260 // 已经写完了261 bWritten = true;262 break; // 退出while(1)263 }264 }265 266 if (bWritten == true)267 {268 //设置用于读操作的文件描述符269 ev.data.fd=sockfd;270 271 //设置用于注测的读操作事件272 ev.events=EPOLLIN | EPOLLET;273 274 epoll_ctl(epfd,EPOLL_CTL_MOD,sockfd,&ev);275 }276 }277 }278 }279 return 0;280 }

注意以下几点:

1. #14设定为5是故意的,为了测试后续的输入和输出

2. 整个服务器的功能是先读取字符串,然后向对方写内容。

3. #110处设置通信socket为非阻塞。

4. 注意#130~#183的读干净缓冲区的read。

5. 注意#213~#264的完全写完所需要传送内容的write。

6. 关于EPOLLET,epoll_wait只有在socket状态发生变化的时候才会返回。所以要对fd进行循环accept,read, write;知直到socket的缓冲区空(read, accept)或者填满(write)为止。

 7. 下面是客户端实验代码

1 int 2 main(int argc, char **argv) 3 { 4     int                    sockfd; 5     char                recvline[MAXLINE + 1]; 6     struct sockaddr_in    servaddr; 7  8     if (argc != 2) 9         err_quit("usage: a.out 
");10 11 if ( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)12 err_sys("socket error");13 14 bzero(&servaddr, sizeof(servaddr));15 servaddr.sin_family = AF_INET;16 servaddr.sin_port = htons(5000); 17 if (inet_pton(AF_INET, argv[1], &servaddr.sin_addr) <= 0)18 err_quit("inet_pton error for %s", argv[1]);19 20 if (connect(sockfd, (SA *) &servaddr, sizeof(servaddr)) < 0)21 err_sys("connect error");22 23 char input[100];24 while (fgets(input, 100, stdin) != EOF)25 {26 write(sockfd, input, strlen(input));27 28 int n = 0;29 int count = 0;30 while (1)31 {32 n = read(sockfd, recvline + count, MAXLINE);33 if (n == MAXLINE)34 {35 count += n;36 continue;37 }38 else 39 break;40 }41 printf("%s\n", recvline);42 }43 exit(0);44 }

 

 

 

转载于:https://www.cnblogs.com/aicro/archive/2012/12/27/2836170.html

你可能感兴趣的文章
简单工厂模式
查看>>
SET ANSI_NULLS ON SET QUOTED_IDENTIFIER ON 什么意思 sql server 2005 2008
查看>>
数据结构--单链表
查看>>
JNDI的学习与使用
查看>>
Delphi7编译的程序自动中Win32.Induc.a病毒的解决办法
查看>>
STM32下载失败,st-link v2 在线下载sw模式检测不到
查看>>
Objective-C 【关于导入类(@class 和 #import的区别)】
查看>>
倍福TwinCAT(贝福Beckhoff)常见问题(FAQ)-点击运行按钮进入到运行状态报错Error starting TwinCAT System怎么办 AdsWarning1823怎么办...
查看>>
iTextSharp 使用详解(转)
查看>>
【转】javascript 中的很多有用的东西
查看>>
Python中替换元素
查看>>
关于双核心:也许你不知道的五件事
查看>>
Trace 2018徐州icpc网络赛 (二分)(树状数组)
查看>>
让你的 Python 代码优雅又地道
查看>>
Centos7.2正常启动关闭CDH5.16.1
查看>>
Android 监听返回键、HOME键
查看>>
Android ContentProvider的实现
查看>>
jmeter里面Dug Sampler 和json提取器的用法
查看>>
sqlserver 各种判断是否存在(表名、函数、存储过程等)
查看>>
公司居然使用监听设备,大家来讨论下IT公司应该怎样管理
查看>>