IO多路复用之epoll(二)

前一篇介绍了epoll的LT模式LT模式注意epollout事件在数据全部写成功后需要取消关注,或者更改为EPOLLIN。

而这次epoll的ET模式,要注意的是在读和写的过程中要在循环中写完或者读完所有数据确保不要丢掉一些数据

因为epoll ET模式只在两种边缘更改的时候触发,对于读事件只在内核缓冲区由空变为

非空通知一次用户,对于写事件,内核缓冲区只在由满变为非满的情况通知用户一次。

下面是代码

  1. int main()
  2. {
  3. int eventsize = 20;
  4. struct epoll_event * epoll_eventsList = (struct epoll_event *)malloc(sizeof(struct epoll_event)
  5. *eventsize);
  6. //打开一个空的描述符
  7. int idlefd = open("/dev/null",O_RDONLY|O_CLOEXEC);
  8. cout << "idlefd" <<idlefd <<endl;
  9. //生成listen描述符
  10. int listenfd = socket(PF_INET, SOCK_CLOEXEC | SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP);
  11. if(listenfd < 0)
  12. {
  13. ERR_EXIT("socketfd");
  14. }
  15. //初始化地址信息
  16. struct sockaddr_in servaddr;
  17. memset(&servaddr,0 ,sizeof(struct sockaddr_in));
  18. servaddr.sin_family = AF_INET;
  19. servaddr.sin_port = htons(6667);
  20. servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
  21. int on = 1;
  22. if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
  23. ERR_EXIT("setsockopt");
  24. if(bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)))
  25. ERR_EXIT("bindError");
  26. if (listen(listenfd, SOMAXCONN) < 0)
  27. ERR_EXIT("listen");
  28. //记录客户端连接对应的socket
  29. std::vector<int> clients;
  30. //创建epollfd, 用于管理epoll事件表
  31. int epollfd;
  32. epollfd = epoll_create1(EPOLL_CLOEXEC);
  33. struct epoll_event event;
  34. event.data.fd = listenfd;
  35. event.events = EPOLLIN|EPOLLET;
  36. //将listenfd加入到epollfd管理的表里
  37. epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd, &event);
  38. //用于接收新连接的客户端地址
  39. struct sockaddr_in peeraddr;
  40. socklen_t peerlen;
  41. int connfd;
  42. //为了简单起见写了个很大的数组,根据文件描述符存储内容
  43. //其实很多项目代码采epoll.data.ptr回调具体的读写
  44. vector<string> recievebuf;
  45. for(int i = 0 ; i < 22222; i++)
  46. {
  47. recievebuf.push_back("");
  48. }
  49. while(1)
  50. {
  51. int nready = epoll_wait(epollfd, epoll_eventsList, eventsize, -1);
  52. if (nready == -1)
  53. {
  54. if (errno == EINTR)
  55. continue;
  56. ERR_EXIT("epoll_wait");
  57. }
  58. if (nready == 0)
  59. continue;
  60. //大小不够重新开辟
  61. if ((size_t)nready == eventsize)
  62. {
  63. if(eventsize * 2 >= 22222)
  64. {
  65. ERR_EXIT("too many fds");
  66. }
  67. struct epoll_event * epoll_eventsList2 = (struct epoll_event *)malloc(sizeof(struct epoll_event) *
  68. eventsize *2);
  69. if(epoll_eventsList2)
  70. {
  71. memcpy(epoll_eventsList2,epoll_eventsList,sizeof(struct epoll_event) * eventsize);
  72. eventsize = eventsize * 2;
  73. free(epoll_eventsList);
  74. epoll_eventsList = epoll_eventsList2;
  75. }
  76. }
  77. for (int i = 0; i < nready; ++i)
  78. {
  79. //判断wait返回的events数组状态是否正常
  80. if ((epoll_eventsList[i].events & EPOLLERR) ||
  81. (epoll_eventsList[i].events & EPOLLHUP))
  82. {
  83. fprintf (stderr, "epoll error\n");
  84. close (epoll_eventsList[i].data.fd);
  85. continue;
  86. }
  87. if (epoll_eventsList[i].data.fd == listenfd)
  88. {
  89. peerlen = sizeof(peeraddr);
  90. //ET模式accept放在while循环里
  91. do
  92. {
  93. connfd = ::accept4(listenfd, (struct sockaddr*)&peeraddr,
  94. &peerlen, SOCK_NONBLOCK | SOCK_CLOEXEC);
  95. if(connfd <= 0)
  96. break;
  97. std::cout<<"ip="<<inet_ntoa(peeraddr.sin_addr)<<
  98. " port="<<ntohs(peeraddr.sin_port)<<std::endl;
  99. clients.push_back(connfd);
  100. //将connd加入epoll表里,关注读事件
  101. event.data.fd = connfd;
  102. event.events = EPOLLIN |EPOLLET;
  103. epoll_ctl(epollfd, EPOLL_CTL_ADD, connfd, &event);
  104. cout << "loop" <<endl;
  105. cout << "loop" << connfd << endl;
  106. }while(1);
  107. //accept失败,判断是否接收全所有的fd
  108. cout << connfd << endl;
  109. if (connfd == -1){
  110. if (errno != EAGAIN && errno != ECONNABORTED
  111. && errno != EPROTO && errno != EINTR)
  112. {
  113. cout << "error" <<endl;
  114. ERR_EXIT("accept");
  115. }
  116. }
  117. //所有请求都处理完成
  118. cout << "continue"<<endl;
  119. continue;
  120. }//endif
  121. else if(epoll_eventsList[i].events & EPOLLIN)
  122. {
  123. connfd = epoll_eventsList[i].data.fd;
  124. if(connfd > 22222)
  125. {
  126. close(connfd);
  127. event = epoll_eventsList[i];
  128. epoll_ctl(epollfd, EPOLL_CTL_DEL, connfd, &event);
  129. clients.erase(std::remove(clients.begin(), clients.end(), connfd), clients.end());
  130. continue;
  131. }
  132. char buf[1024] = {0};
  133. if(connfd < 0)
  134. continue;
  135. int ret = 0;
  136. int total = 0;
  137. std::string strtemp;
  138. while(1)
  139. {
  140. cout << "begin read" <<endl;
  141. ret = read(connfd, buf, 1024);
  142. if(ret <= 0)
  143. {
  144. break;
  145. }
  146. strtemp += string(buf);
  147. total += ret;
  148. memset(buf, 0, 1024);
  149. if(ret < 1024)
  150. {
  151. break;
  152. }
  153. }//endwhile(1)
  154. cout << "end read" <<endl;
  155. recievebuf[connfd] = strtemp.c_str();
  156. cout << "buff data :" << recievebuf[connfd]<<endl;
  157. if(ret == -1)
  158. {
  159. if((errno == EAGAIN) ||
  160. (errno == EWOULDBLOCK))
  161. {
  162. //由于内核缓冲区空了,下次有数据到来是会触发epollin
  163. continue;
  164. }
  165. ERR_EXIT("read");
  166. }//endif ret == -1
  167. //连接断开
  168. if(ret == 0)
  169. {
  170. std::cout<<"client close"<<std::endl;
  171. close(connfd);
  172. event = epoll_eventsList[i];
  173. epoll_ctl(epollfd, EPOLL_CTL_DEL, connfd, &event);
  174. clients.erase(std::remove(clients.begin(), clients.end(), connfd), clients.end());
  175. continue;
  176. }
  177. cout << "turn to write" << endl;
  178. //更改为写模式
  179. event.data.fd = connfd;
  180. event.events = EPOLLOUT | EPOLLET;
  181. epoll_ctl(epollfd, EPOLL_CTL_MOD, connfd, &event);
  182. cout << "epoll mod change success" << endl;
  183. }//end elif
  184. else //写事件
  185. {
  186. if(epoll_eventsList[i].events & EPOLLOUT)
  187. {
  188. cout << "begin write" <<endl;
  189. connfd = epoll_eventsList[i].data.fd;
  190. int count = 0;
  191. int totalsend = 0;
  192. char buf[1024];
  193. strcpy(buf, recievebuf[connfd].c_str());
  194. cout << "write buff" <<buf<<endl;
  195. while(1)
  196. {
  197. int totalcount = strlen(buf);
  198. int pos = 0;
  199. count = write(epoll_eventsList[i].data.fd, buf + pos, totalcount);
  200. cout << "write count:" << count;
  201. if(count < 0)
  202. {
  203. break;
  204. }
  205. if(count < totalcount)
  206. {
  207. totalcount = totalcount - count;
  208. pos += count;
  209. }
  210. else
  211. {
  212. break;
  213. }
  214. }//end while
  215. if(count == -1)
  216. {
  217. if((errno == EAGAIN) ||
  218. (errno == EWOULDBLOCK))
  219. {
  220. //由于内核缓冲区满了
  221. //于内核缓冲区满了
  222. continue;
  223. }
  224. ERR_EXIT("write");
  225. }
  226. if(count == 0)
  227. {
  228. std::cout<<"client close"<<std::endl;
  229. close(connfd);
  230. event = epoll_eventsList[i];
  231. epoll_ctl(epollfd, EPOLL_CTL_DEL, connfd, &event);
  232. clients.erase(std::remove(clients.begin(), clients.end(), connfd),
  233. clients.end());
  234. continue;
  235. }
  236. event.data.fd = connfd;
  237. event.events = EPOLLIN|EPOLLET;
  238. epoll_ctl(epollfd, EPOLL_CTL_MOD, connfd, &event);
  239. }
  240. }//end eles 写事件
  241. }
  242. }
  243. }

源代码下载地址:http://download.csdn.net/detail/secondtonone1/9486222
我的微信公众号:
https://cdn.llfc.club/gzh.jpg

热门评论

热门文章

  1. 解密定时器的实现细节

    喜欢(566) 浏览(1829)
  2. C++ 类的继承封装和多态

    喜欢(588) 浏览(2544)
  3. slice介绍和使用

    喜欢(521) 浏览(1703)
  4. Linux环境搭建和编码

    喜欢(594) 浏览(5452)
  5. windows环境搭建和vscode配置

    喜欢(587) 浏览(1708)

最新评论

  1. 利用内存模型优化无锁栈 卡西莫多的礼物:感谢博主指点,好人一生平安o(* ̄▽ ̄*)ブ
  2. 类和对象 陈宇航:支持!!!!
  3. 泛型算法的定制操作 secondtonone1:lambda和bind是C11新增的利器,善于利用这两个机制可以极大地提升编程安全性和效率。
  4. 基于锁实现线程安全队列和栈容器 secondtonone1:我是博主,你认真学习的样子的很可爱,哈哈,我画的是链表由空变成1个的情况。其余情况和你思考的类似,只不过我用了一个无效节点表示tail的指向,最初head和tail指向的都是这个节点。
  5. 解决博客回复区被脚本注入的问题 secondtonone1:走到现在我忽然明白一个道理,无论工作也好生活也罢,最重要的是开心,即使一份安稳的工作不能给我带来事业上的积累也要合理的舍弃,所以我还是想去做喜欢的方向。

个人公众号

个人微信