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. MarkDown在线编辑器

    喜欢(514) 浏览(15807)
  2. 聊天项目(28) 分布式服务通知好友申请

    喜欢(507) 浏览(7154)
  3. vscode搭建windows C++开发环境

    喜欢(596) 浏览(98020)
  4. 使用hexo搭建个人博客

    喜欢(533) 浏览(13906)
  5. Linux环境搭建和编码

    喜欢(594) 浏览(15558)

最新评论

  1. 解决博客回复区被脚本注入的问题 secondtonone1:走到现在我忽然明白一个道理,无论工作也好生活也罢,最重要的是开心,即使一份安稳的工作不能给我带来事业上的积累也要合理的舍弃,所以我还是想去做喜欢的方向。
  2. 处理网络粘包问题 zyouth: //消息的长度小于头部规定的长度,说明数据未收全,则先将部分消息放到接收节点里 if (bytes_transferred < data_len) { memcpy(_recv_msg_node->_data + _recv_msg_node->_cur_len, _data + copy_len, bytes_transferred); _recv_msg_node->_cur_len += bytes_transferred; ::memset(_data, 0, MAX_LENGTH); _socket.async_read_some(boost::asio::buffer(_data, MAX_LENGTH), std::bind(&CSession::HandleRead, this, std::placeholders::_1, std::placeholders::_2, shared_self)); //头部处理完成 _b_head_parse = true; return; } 把_b_head_parse = true;放在_socket.async_read_some前面是不是更好
  3. C++ 线程池原理和实现 mzx2023:两种方法解决,一种是改排序算法,就是当线程耗尽的时候,使用普通递归,另一种是当在线程池commit的时候,判断线程是否耗尽,耗尽的话就直接当前线程执行task
  4. 利用指针和容器实现文本查询 越今朝:应该添加一个过滤功能以解决部分单词无法被查询的问题: eg: "I am a teacher."中的teacher无法被查询,因为在示例代码中teacher.被解释为一个单词从而忽略了teacher本身。
  5. 无锁并发队列 TenThousandOne:_head  和 _tail  替换为原子变量。那里pop的逻辑,val = _data[h] 可以移到循环外面吗

个人公众号

个人微信