代码

  1. #include <stdio.h>
  2. #include <sys/types.h>
  3. #include <sys/socket.h>
  4. #include <netdb.h>
  5. #include <stdlib.h>
  6. #include <unistd.h>
  7. #include <arpa/inet.h>
  8. #include <netinet/in.h>
  9. #include <errno.h>
  10. #include <string.h>
  11. #include <ctype.h>
  12. #include <sys/epoll.h>
  13. int main()
  14. {
  15. int lfd = socket(AF_INET, SOCK_STREAM, 0);
  16. if (lfd == -1)
  17. {
  18. perror("socket error");
  19. exit(1);
  20. }
  21. //设置端口复用
  22. int opt = 1;
  23. setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, (const void *)&opt, sizeof(opt));
  24. struct sockaddr_in serv_addr;
  25. memset(&serv_addr, 0, sizeof(serv_addr));
  26. serv_addr.sin_family = AF_INET;
  27. serv_addr.sin_port = htons(9317);
  28. serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
  29. int ret = bind(lfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
  30. if (ret == -1)
  31. {
  32. perror("bind error");
  33. exit(1);
  34. }
  35. ret = listen(lfd, 64);
  36. if (ret == -1)
  37. {
  38. perror("listen error");
  39. exit(1);
  40. }
  41. struct sockaddr_in client_addr;
  42. socklen_t cli_addr_len = sizeof(struct sockaddr_in);
  43. //
  44. struct epoll_event events[2000];
  45. // 创建epoll实例
  46. int epfd = epoll_create(2000);
  47. //把监听事件挂到epoll红黑树上
  48. struct epoll_event event_l;
  49. event_l.events = EPOLLIN;
  50. event_l.data.fd = lfd;
  51. if ((epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &event_l)) < 0)
  52. {
  53. fprintf(stderr, "epoll ctl error: %s\n", strerror(errno));
  54. exit(1);
  55. }
  56. while (1)
  57. {
  58. // 有事件发送的epoll_event结构体会复制到events上,ret为事件总数
  59. int ret = epoll_wait(epfd, events, sizeof(events) / sizeof(events[0]), -1);
  60. if (ret < 0)
  61. {
  62. fprintf(stderr, "epoll wait error: %s\n", strerror(errno));
  63. exit(1);
  64. }
  65. int k;
  66. for (k = 0; k < ret; k++)
  67. {
  68. // 读事件
  69. if (events[k].events & EPOLLIN)
  70. {
  71. // tfd: this fd
  72. int tfd = events[k].data.fd;
  73. if (tfd == lfd)
  74. {
  75. // 处理监听套接字的事件
  76. int cfd = accept(lfd, (struct sockaddr *)&client_addr, &cli_addr_len);
  77. if (cfd < 0)
  78. {
  79. fprintf(stderr, "accept error: %s\n", strerror(errno));
  80. }
  81. // 把客户端fd挂到epoll红黑树上
  82. struct epoll_event tmp;
  83. tmp.events = EPOLLIN;
  84. tmp.data.fd = cfd;
  85. if ((epoll_ctl(epfd, EPOLL_CTL_ADD, cfd, &tmp)) < 0)
  86. {
  87. fprintf(stderr, "epoll ctl error: %s\n", strerror(errno));
  88. exit(1);
  89. }
  90. }
  91. else
  92. {
  93. // 客户端fd的事件
  94. char buff[1024];
  95. int rec_len, i;
  96. if ((rec_len = recv(tfd, buff, sizeof(buff), 0)) < 0)
  97. {
  98. fprintf(stderr, "read error: %s\n", strerror(errno));
  99. exit(1);
  100. }
  101. else if (rec_len == 0)
  102. {
  103. // 客户端关闭连接
  104. if ((epoll_ctl(epfd, EPOLL_CTL_DEL, tfd, NULL)) < 0)
  105. {
  106. fprintf(stderr, "epoll ctl error: %s\n", strerror(errno));
  107. exit(1);
  108. }
  109. close(tfd);
  110. }
  111. else
  112. {
  113. printf("%s", buff);
  114. for (i = 0; i < rec_len; ++i)
  115. {
  116. buff[i] = toupper(buff[i]);
  117. }
  118. send(tfd, buff, rec_len, 0);
  119. }
  120. }
  121. }
  122. }
  123. }
  124. close(epfd);
  125. close(lfd);
  126. return 0;
  127. }

epoll的一点小问题

如果某个fd被关闭,并且这次关闭是真正地释放了资源,而不是减少引用技术,那么这个fd会自动从eopll中删除,这时再调用epoll_ctl(EPOLL_CTL_DEL)可能会报错。

  1. Q6 Will closing a file descriptor cause it to be removed from all<br /> epoll sets automatically?
   A6  Yes,  but  be aware of the following point.  A file descriptor is a<br />           reference to an open file description (see  [open(2)](http://man.he.net/man2/open)).   Whenever  a<br />           descriptor  is duplicated via [dup(2)](http://man.he.net/man2/dup), [dup2(2)](http://man.he.net/man2/dup2), [fcntl(2)](http://man.he.net/man2/fcntl) F_DUPFD, or<br />           [fork(2)](http://man.he.net/man2/fork), a new file descriptor referring  to  the  same  open  file<br />           description  is  created.   An  open  file description continues to<br />           exist until all file descriptors referring to it have been  closed.<br />           A  file  descriptor is removed from an epoll set only after all the<br />           file descriptors referring to the underlying open file  description<br />           have been closed (or before if the descriptor is explicitly removed<br />           using [epoll_ctl(2)](http://man.he.net/man2/epoll_ctl) EPOLL_CTL_DEL).  This means that  even  after  a<br />           file  descriptor  that  is  part  of  an epoll set has been closed,<br />           events may be reported for  that  file  descriptor  if  other  file<br />           descriptors  referring  to  the  same  underlying  file description<br />           remain open.