1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > epoll监听文件_epoll

epoll监听文件_epoll

时间:2020-09-01 02:15:55

相关推荐

epoll监听文件_epoll

poll和select相比最大优势在于可以管理更多的文件描述符。

epoll和poll相比最大的有时是速度更快,减少了大量文件描述符从内核到用户态的拷贝。

使用epoll的大体框架是

1 创建epoll instalce,得到epoll的文件描述符

调用epoll_create可以在内核创建一个epoll instance,返回这个instance的文件描述符。size参数已经被忽略了。

int epoll_create(int size);

int epoll_create1(int flags);

epoll_create1的参数flags值得一说。它可以取0或者FD_CLOEXEC。如果设置了FD_CLOEXEC,那么epoll_create1返回的文件描述符就会被字段设置FD_CLOEXEC标记。于是当进程以后调用fork的时候,epoll_create1所返回的文件描述符在子进程里是被关闭的,用不了。

例如

int epollfd = epoll_create1(0);

2 利用上面得到的epollfd,添加要监控的文件描述符

struct epoll_event ev;

ev.data.fd = fd;

ev.events = EPOLLIN|EPOLLET;

if(epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) < 0) goto err;

其中:

第一个参数是epoll_create返回的fd

第二个参数是要做的事情,包括:

EPOLL_CTL_ADD

EPOLL_CTL_MOD

EPOLL_CTL_DEL

第三个参数是要监控的fd

第四个参数是struct epoll_event用来描述要监控哪些时间,以及当时间发生时传回哪些数据。

EPOLLIN 表示要监控文件描述符的可读性

epoll_event.data是一个union,除了设置fd,也可以当作void*来使用,执行自定义的数据。当文件描述符准备好时,epoll会把这个指针再“吐出来”。

3 设置好要监控的fd和事件后,就可以等待事件发生了。

struct epoll_event *evs;

evs = calloc(64, sizeof(ev));

while(1)

{

int n = epoll_wait(epollfd, evs, 64, -1);

...

第一个参数是epoll_create返回的文件描述符

第二个参数是提供给epoll_wait的struct epoll_event数组,epoll会把准备好的文件描述符和时间,以及之前ADD时自定义的数放到这个数组里。

第三个参数是数组长度

第四个参数是超时时间

返回值n是此次检测到已就绪的文件描述符数量。之后可以依次取出evs[0]到evs[n-1]来处理。

4 处理得到的struct epoll_event数组

对于数组的每个成员,需要先检查文件描述符发生的什么事件,有没有错误产生。

if(e->events & EPOLLERR || e->events & EPOLLHUP )

....

epoll和poll都是检测文件描述符的缓冲区有没有数据,来判断文件描述符是否准备好了。

但是epoll的事件触发方式更灵活,有边缘触发(ET)和水平触发(LT)两种。

这两个概念和数字电路里的一样。

边缘触发: 当状态从0->1的瞬间触发一次。即缓冲区从空变成有数据的时候返回。

水平触发: 状态为1时始终触发。即只要缓冲区有数据,就会返回。

epoll的默认行为是水平触发,此时的行为和poll一直,就是只要缓冲区有数据没读完,调用epoll就会立刻返回。

边缘触发的性能更好,但是有个问题,就是只当缓冲期从空变成有数据的那一刻触发一次,之后再调用epoll,它就会阻塞,一直等待下一次缓冲区由空到非空的事件。

因此如果需要采用性能更好的边缘触发模式,需要做到以下两点:

1 只支持非阻塞文件描述符,也就是说必须设置NONBLOCK标记。

int fl = 0;

int r = 0;

if((fl = fcntl(fd, F_GETFL, 0)) < 0) goto err;

if((r = fcntl(fd, F_SETFL, fl|O_NONBLOCK)) < 0) goto err;

2 必须确保每一次触发后,都要处理完缓冲区的全部数据。

通常用一个while(1)来解决。

由于已经将文件描述符设置为NONBLOCK了,因此可以用死循环。

如果返回-1,需要需要判断如果errno是EAGAIN,则是正常的,说明缓冲区的数据处理完了。如果是其它的errno,就是真的出错了。

while(1)

{

if((cnt = read(evs[i].data.fd, buf, BUFSIZ)) < 0)

{

if(errno != EAGAIN) close(evs[i].data.fd);

break;

}

else if (*buf == EOF)

{

close(evs[i].data.fd);

break;

}

...

...

例子:

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

int check_epoll_err(struct epoll_event* ee)

{

if(ee == NULL) return -1;

if(

ee->events & EPOLLERR ||

ee->events & EPOLLHUP

)

{

return -1;

}

return 1;

}

int no_block(int fd, char* errbuf)

{

int fl = 0;

int r = 0;

if((fl = fcntl(fd, F_GETFL, 0)) < 0) goto err;

if((r = fcntl(fd, F_SETFL, fl|O_NONBLOCK)) < 0) goto err;

return 0;

err:

strcpy(errbuf, strerror(errno));

return errno * -1;

}

int do_accept(int sock, char* errbuf)

{

int ep = 0;

int r = 0;

struct epoll_event ev;

struct epoll_event *evs;

if(listen(sock, 1024) < 0) goto err;

if((ep = epoll_create1(0)) < 0) goto err;

ev.data.fd = sock;

ev.events = EPOLLIN|EPOLLET;

if(epoll_ctl(ep, EPOLL_CTL_ADD, sock, &ev) < 0) goto err;

evs = calloc(64, sizeof(ev));

int n, i;

struct sockaddr_in caddr;

socklen_t addrlen;

int cfd;

while(1)

{

n = epoll_wait(ep, evs, 64, -1);

for(i=0; i

{

if(check_epoll_err(&(evs[i])) < 0)

{

close(evs[i].data.fd);

continue;

}

else if(sock == evs[i].data.fd)

{

while(1) //loop try one non_block fd.

{

if((cfd = accept(sock, (struct sockaddr*)&caddr, &addrlen)) < 0)

{

if(errno == EAGAIN || errno == EWOULDBLOCK)

break; // all connection accepted.

else

goto err;

}

printf("accept client: %s:%d\n", inet_ntoa(caddr.sin_addr), caddr.sin_port);

// make client fd non_block.

if((no_block(cfd, errbuf)) < 0) goto err;

// add client into epoll.

ev.data.fd = cfd;

ev.events = EPOLLIN|EPOLLET;

if(epoll_ctl(ep, EPOLL_CTL_ADD, cfd, &ev) < 0) goto err;

continue;

}

puts("accept ok");

}

else

{

// client fd ready.

if(evs[i].events & EPOLLIN == EPOLLIN)

{

int cnt;

char buf[BUFSIZ];

int finish = 0;

char hello[10] = "hello: ";

while(1)

{

if((cnt = read(evs[i].data.fd, buf, BUFSIZ)) < 0)

{

if(errno != EAGAIN) close(evs[i].data.fd);

break;

}

else if (*buf == EOF)

{

close(evs[i].data.fd);

break;

}

printf("from client: %s", buf);

if(write(evs[i].data.fd, hello, strlen(hello)) < 0) goto err;

if(write(evs[i].data.fd, buf, cnt) < 0) goto err;

}

}

}

}

}

return 0;

err:

strcpy(errbuf, strerror(errno));

return errno * -1;

}

int do_listen(int port, char* errbuf)

{

int r = 0;

int sock = -1;

struct sockaddr_in servaddr;

servaddr.sin_family = AF_INET;

inet_pton(AF_INET, "0.0.0.0", &servaddr.sin_addr);

servaddr.sin_port = htons(port);

if((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) goto err;

else if((r = bind(sock, (struct sockaddr*)&servaddr, sizeof(servaddr))) != 0) goto err;

int opt = 1;

setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));

// make client fd non_block.

if((no_block(sock, errbuf)) < 0) goto err;

return sock;

err:

strcpy(errbuf, strerror(errno));

return errno * -1;

}

int main()

{

int port = 1111;

int r = 0;

char errbuf[BUFSIZ];

int sock = do_listen(port, errbuf);

if(sock < 0)

{

puts(errbuf);

exit(errno);

}

r = do_accept(sock, errbuf);

if(r < 0)

{

puts(errbuf);

exit(errno);

}

return 0;

}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。