0.阅读引用
epoll的et模式一次没读完,下次有新数据来了,还会触发么
1.代码与测试
1.1 本次使用的测试工具
nc -v 127.0.0.1 8000
1.2 测试epoll-et的源码
搜索关键字:EPOLLET,O_NONBLOCK,边沿触发,非阻塞
epoll边沿触发模式的编程关键点:
- 要将事件设置成EPOLLET;
- 将事件设置成EPOLLET之后一定要将对应的fd设置成非阻塞的;
- 要对一次触发得到的缓冲区中的内容进行循环读直到不能再读取出数据为止.
源码如下:
#include <sys/epoll.h>
#include <fcntl.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <unistd.h>
#include <string.h>
#define MAX_EVENT_NUM 1024
#define BUFFER_SIZE 10
#define true 1
#define false 0
void use_et(struct epoll_event *event, int num, int epollfd, int listenfd)
{
char buf[BUFFER_SIZE];
static int connNumBal = 0;
static int connNum = 0;
for(int i = 0; i < num; i++)
{
int sockfd = event[i].data.fd;
if(sockfd == listenfd)
{
struct sockaddr_in clientaddr;
int clilen = sizeof(clientaddr);
int connfd;
do{
if((connfd = accept(listenfd, (struct sockaddr *)&clientaddr, &clilen)) < 0 )
{
if(errno == EAGAIN)
{
printf("accept fail");
break;
}
}
int flag = fcntl(connfd,F_GETFL);
flag |= O_NONBLOCK;
fcntl(connfd,F_SETFL,flag);
struct epoll_event event;
event.data.fd = connfd;
event.events = EPOLLIN | EPOLLET;
epoll_ctl(epollfd, EPOLL_CTL_ADD,connfd, &event);
connNum ++;
connNumBal ++;
printf("来了第[%d]个连接,现有[%d]个连接,我对这个连接套结字描述符设置了非阻塞属性,事件的触发模式选择了ET,向epoll树中添加了节点之后,到达这里准备离开循环\n",connNum,connNumBal);
break;
}while(connfd>0);
//printf("来了第[%d]个链接,我设置了非阻塞属性,添加了ET标志,向epoll树中添加了节点之后,我成功离开循环\n",num);
}
else if(event[i].events & EPOLLIN){
printf("event trigger once\n");
char buf[10] ={0};
int ret ;
while((ret =recv(sockfd, buf, BUFFER_SIZE, 0) ) > 0)
{
// printf("从缓冲区读取了 %d bytes,内容是:%s===================\n", ret, buf);
write(STDOUT_FILENO,buf,ret);
}
if(ret < 0)
{
if(errno == EAGAIN)
{
printf("此次的内核缓冲区的内容已经读完了\n");
}else{
printf("recv error\n");
}
}
if(ret == 0){
connNumBal--;
printf("对端关闭了连接,我要把这个连接从epoll树上摘掉啦,现在的连接有[%d]\n",connNumBal);
epoll_ctl(epollfd, EPOLL_CTL_DEL, event[i].data.fd, NULL);
close(sockfd);
}
}
}
}
int start_server(char *ipaddr, char *port)
{
const char * s = NULL;
int ret = 0;
int sock = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in serveraddr;
bzero(&serveraddr, sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_port = htons(atoi(port));
inet_pton(AF_INET, ipaddr, &serveraddr.sin_addr);
//设置重用ip地址和端口号
int on = 1;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on));
setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, (char*)&on, sizeof(on));
ret = bind(sock, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
if(ret < 0)
{
perror(s);
exit(1);
}
ret = listen(sock, 128);
if(ret < 0)
{
perror(s);
exit(1);
}
return sock;
}
int main(int argc, char *argv[])
{
if (argc < 3)
{
printf("++++++++++++++++++++++++++++++++++++++++++++++++++++\n");
printf("+ ./server_et 127.0.0.1 8000 +\n");
printf("+ please use the suggested format. +\n");
printf("++++++++++++++++++++++++++++++++++++++++++++++++++++\n");
return 0;
}
int listenfd = start_server(argv[1], argv[2]);
//setnonblocking(listenfd);
struct epoll_event events[MAX_EVENT_NUM];
int epollfd = epoll_create(5);
if(epollfd < 0)
{
printf("epoll_create err\n");
exit(1);
}
struct epoll_event event;
event.data.fd = listenfd;
event.events = EPOLLIN | EPOLLOUT | EPOLLET ;
epoll_ctl(epollfd, EPOLL_CTL_ADD,listenfd, &event);
while(1)
{
int ret = epoll_wait(epollfd, events, MAX_EVENT_NUM, -1);
if(ret < 0)
{
printf("epoll failure\n");
break;
}
if(ret > 0 )
{
printf("\n产生了事件,现在触发的事件有[%d]个\n",ret);
use_et(events, ret, epollfd, listenfd);//et模式
}
}
close(listenfd);
return 0;
}
本文探讨了北京大学同学的性能测试中,如何在epoll的边沿触发(ET)模式下,正确设置socket为非阻塞并避免漏报消息。通过实例代码展示了EPOLLET和O_NONBLOCK的关键使用,并解释了ET模式下如何处理未读完的数据。
1104

被折叠的 条评论
为什么被折叠?



