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. vscode搭建windows C++开发环境

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

    喜欢(507) 浏览(6091)
  3. 使用hexo搭建个人博客

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

    喜欢(594) 浏览(13513)
  5. Qt环境搭建

    喜欢(517) 浏览(24625)

最新评论

  1. 堆排序 secondtonone1:堆排序非常实用,定时器就是这个原理制作的。
  2. 创建项目和编译 secondtonone1:谢谢支持
  3. 类和对象 陈宇航:支持!!!!
  4. boost::asio之socket的创建和连接 项空月:发现一些错别字 :每隔vector存储  是不是是每个. asio::mutable_buffers_1 o或者    是不是多打了个o
  5. 聊天项目(9) redis服务搭建 pro_lin:redis线程池的析构函数,除了pop出队列,还要free掉redis连接把
  6. 利用栅栏实现同步 Dzher:作者你好!我觉得 std::thread a(write_x); std::thread b(write_y); std::thread c(read_x_then_y); std::thread d(read_y_then_x); 这个例子中的assert fail并不会发生,原子变量设定了非relaxed内存序后一个线程的原子变量被写入,那么之后的读取一定会被同步的,c和d线程中只可能同时发生一个z++未执行的情况,最终z不是1就是2了,我测试了很多次都没有assert,请问我这个观点有什么错误,谢谢!
  7. C++ 并发三剑客future, promise和async Yunfei:大佬您好,如果这个线程池中加入的异步任务的形参如果有右值引用,这个commit中的返回类型推导和bind绑定就会出现问题,请问实际工程中,是不是不会用到这种任务,如果用到了,应该怎么解决?
  8. string类 WangQi888888:确实错了,应该是!isspace(sind[index]). 否则不进入循环,还是原来的字符串“some string”
  9. 答疑汇总(thread,async源码分析) Yagus:如果引用计数为0,则会执行 future 的析构进而等待任务执行完成,那么看到的输出将是 这边应该不对吧,std::future析构只在这三种情况都满足的时候才回block: 1.共享状态是std::async 创造的(类型是_Task_async_state) 2.共享状态没有ready 3.这个future是共享状态的最后一个引用 这边共享状态类型是“_Package_state”,引用计数即使为0也不应该block啊
  10. Qt 对话框 Spade2077:QDialog w(); //这里是不是不需要带括号
  11. visual studio配置boost库 一giao里我离giaogiao:请问是修改成这样吗:.\b2.exe toolset=MinGW
  12. 面试题汇总(一) secondtonone1:看到网络上经常提问的go的问题,做了一下汇总,结合自己的经验给出的答案,如有纰漏,望指正批评。
  13. 聊天项目(15) 客户端实现TCP管理者 lkx:已经在&QTcpSocket::readyRead 回调函数中做了处理了的。
  14. interface应用 secondtonone1:interface是万能类型,但是使用时要转换为实际类型来使用。interface丰富了go的多态特性,也降低了传统面向对象语言的耦合性。
  15. 处理网络粘包问题 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前面是不是更好
  16. 构造函数 secondtonone1:构造函数是类的基础知识,要着重掌握
  17. Qt MVC结构之QItemDelegate介绍 胡歌-此生不换:gpt, google
  18. 聊天项目(7) visualstudio配置grpc diablorrr:cmake文件得改一下 find_package(Boost REQUIRED COMPONENTS system filesystem),要加上filesystem。在target_link_libraries中也同样加上
  19. 解决博客回复区被脚本注入的问题 secondtonone1:走到现在我忽然明白一个道理,无论工作也好生活也罢,最重要的是开心,即使一份安稳的工作不能给我带来事业上的积累也要合理的舍弃,所以我还是想去做喜欢的方向。
  20. 无锁并发队列 TenThousandOne:_head  和 _tail  替换为原子变量。那里pop的逻辑,val = _data[h] 可以移到循环外面吗
  21. 网络编程学习方法和图书推荐 Corleone:啥程度可以找工作
  22. 再谈单例模式 secondtonone1:是的,C++11以后返回局部static变量对象能保证线程安全了。
  23. protobuf配置和使用 熊二:你可以把dll放到系统目录,也可以配置环境变量,还能把dll丢到lib里

个人公众号

个人微信