readn调用的阻塞,比如设定读500个字符,但是只读到498,完事儿阻塞了,等另剩下的2个字符,然而在server代码里,一旦read变为readn阻塞了,它就不会被唤醒了,因为epoll_wait因为readn的阻塞不会循环执行,读不到新数据。有点死锁的意思,差俩字符所以阻塞,因为阻塞,读不到新字符。
LT(level triggered):LT是缺省的工作方式,并且同时支持block和no-block socket。在这种做法中,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的fd进行IO操作。如果你不作任何操作,内核还是会继续通知你的,所以,这种模式编程出错误可能性要小一点。传统的select/poll都是这种模型的代表。
ET(edge-triggered):ET是高速工作方式,只支持no-block socket。在这种模式下,当描述符从未就绪变为就绪时,内核通过epoll告诉你。然后它会假设你知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的就绪通知。请注意,如果一直不对这个fd作IO操作(从而导致它再次变成未就绪),内核不会发送更多的通知(only once).
1.#include <stdio.h>
2.#include <string.h>
3.#include <netinet/in.h>
4.#include <arpa/inet.h>
5.#include <sys/wait.h>
6.#include <sys/types.h>
7.#include <sys/epoll.h>
8.#include <unistd.h>
9.#include <fcntl.h>
10.
11.#define MAXLINE 10
12.#define SERV_PORT 8000
13.
14.int main(void)
15.{
16. struct sockaddr_in servaddr, cliaddr;
17. socklen_t cliaddr_len;
18. int listenfd, connfd;
19. char buf[MAXLINE];
20. char str[INET_ADDRSTRLEN];
21. int efd, flag;
22.
23. listenfd = socket(AF_INET, SOCK_STREAM, 0);
24.
25. bzero(&servaddr, sizeof(servaddr));
26. servaddr.sin_family = AF_INET;
27. servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
28. servaddr.sin_port = htons(SERV_PORT);
29.
30. bind(listenfd, (struct sockaddr *)&servaddr, sizeof(servaddr));
31.
32. listen(listenfd, 20);
33.
34. ///
35. struct epoll_event event;
36. struct epoll_event res_event[10];
37. int res, len;
38.
39. efd = epoll_create(10);
40.
41. event.events = EPOLLIN | EPOLLET; /* ET 边沿触发,默认是水平触发 */
42.
43. //event.events = EPOLLIN;
44. printf("Accepting connections ...\n");
45. cliaddr_len = sizeof(cliaddr);
46. connfd = accept(listenfd, (struct sockaddr *)&cliaddr, &cliaddr_len);
47. printf("received from %s at PORT %d\n",
48. inet_ntop(AF_INET, &cliaddr.sin_addr, str, sizeof(str)),
49. ntohs(cliaddr.sin_port));
50.
51. flag = fcntl(connfd, F_GETFL); /* 修改connfd为非阻塞读 */
52. flag |= O_NONBLOCK;
53. fcntl(connfd, F_SETFL, flag);
54.
55. event.data.fd = connfd;
56. epoll_ctl(efd, EPOLL_CTL_ADD, connfd, &event); //将connfd加入监听红黑树
57. while (1) {
58. printf("epoll_wait begin\n");
59. res = epoll_wait(efd, res_event, 10, -1); //最多10个, 阻塞监听
60. printf("epoll_wait end res %d\n", res);
61.
62. if (res_event[0].data.fd == connfd) {
63. while ((len = read(connfd, buf, MAXLINE/2)) >0 ) //非阻塞读, 轮询
64. write(STDOUT_FILENO, buf, len);
65. }
66. }
67.
68. return 0;
69.}
多了这几行:
epoll 的 ET模式, 高效模式,但是只支持 非阻塞模式。 — 忙轮询。