本文无太多内容,主要是几个前面提到过的注意点:

一是epoll的fd需要重新装填。我们将tcp_connection_t的指针保存在数组中,所以我们以这个数组为依据,重新装填fd的监听事件。

  1. //重新装填epoll内fd的监听事件
  2. int i;
  3. for(i = 0; i < EVENTS_SIZE; ++i)
  4. {
  5. if(connsets[i] != NULL)
  6. {
  7. int fd = i; //fd
  8. tcp_connection_t *pt = connsets[i]; //tcp conn
  9. uint32_t event = 0;
  10. if(buffer_is_readable(&pt->buffer_))
  11. event |= kWriteEvent;
  12. if(buffer_is_writeable(&pt->buffer_))
  13. event |= kReadEvent;
  14. //重置监听事件
  15. epoll_mod_fd(epollfd, fd, event);
  16. }
  17. }

二是,建立连接时,需要做的工作是:

1.新建tcp_connection_t结构,初始化

2.将fd加入epoll,不监听任何事件

3.将tcp_connection_t的指针加入数组。

代码如下:

  1. //建立连接
  2. int peerfd = accept4(listenfd, NULL, NULL, SOCK_NONBLOCK | SOCK_CLOEXEC);
  3. if(peerfd == -1)
  4. ERR_EXIT("accept4");
  5. //新建tcp连接
  6. tcp_connection_t *pt = (tcp_connection_t*)malloc(sizeof(tcp_connection_t));
  7. buffer_init(&pt->buffer_);
  8. //将该tcp连接放入connsets
  9. connsets[peerfd] = pt;
  10. epoll_add_fd(epollfd, peerfd, 0);

连接关闭时需要:

  1. //close
  2. epoll_del_fd(epollfd, fd);
  3. close(fd);
  4. free(pt);
  5. connsets[fd] = NULL;

 

还有一点:前面我们记录fd和connsets的关系,采用的是数组下标的方式,其实我们还可以将指针存入epoll的data中,其中:

  1. typedef union epoll_data {
  2. void *ptr;
  3. int fd;
  4. uint32_t u32;
  5. uint64_t u64;
  6. } epoll_data_t;
  7.  
  8. struct epoll_event {
  9. uint32_t events; /* Epoll events */
  10. epoll_data_t data; /* User data variable */
  11. };

我们对于data这个联合体,不再使用fd,而是使用ptr,指向一个tcp_connection_t的指针。不过我们需要将fd存储在tcp_connection_t数据结构中。

这里为了简便起见,仍采用以前的方法,读者可以自行尝试。

 

完整的代码如下:

  1. #define _GNU_SOURCE /* See feature_test_macros(7) */
  2. #include <sys/socket.h>
  3. #include "sysutil.h"
  4. #include "buffer.h"
  5. #include <assert.h>
  6. #include <sys/epoll.h>
  7.  
  8. #define EVENTS_SIZE 1024
  9.  
  10. typedef struct{
  11. buffer_t buffer_;
  12. } tcp_connection_t; //表示一条TCP连接
  13.  
  14. tcp_connection_t *connsets[EVENTS_SIZE]; //提供从fd到TCP连接的映射
  15.  
  16. int main(int argc, char const *argv[])
  17. {
  18. //获取监听fd
  19. int listenfd = tcp_server("localhost", 9981);
  20. //将监听fd设置为非阻塞
  21. activate_nonblock(listenfd);
  22.  
  23. //初始化connsets
  24. int ix;
  25. for(ix = 0; ix < EVENTS_SIZE; ++ix)
  26. {
  27. connsets[ix] = NULL;
  28. }
  29.  
  30. //初始化epoll
  31. int epollfd = epoll_create1(0);
  32. epoll_add_fd(epollfd, listenfd, kReadEvent);
  33. struct epoll_event events[1024];
  34.  
  35. while(1)
  36. {
  37. //重新装填epoll内fd的监听事件
  38. int i;
  39. for(i = 0; i < EVENTS_SIZE; ++i)
  40. {
  41. if(connsets[i] != NULL)
  42. {
  43. int fd = i; //fd
  44. tcp_connection_t *pt = connsets[i]; //tcp conn
  45. uint32_t event = 0;
  46. if(buffer_is_readable(&pt->buffer_))
  47. event |= kWriteEvent;
  48. if(buffer_is_writeable(&pt->buffer_))
  49. event |= kReadEvent;
  50. //重置监听事件
  51. epoll_mod_fd(epollfd, fd, event);
  52. }
  53. }
  54.  
  55. //epoll监听fd
  56. int nready = epoll_wait(epollfd, events, 1024, 5000);
  57. if(nready == -1)
  58. ERR_EXIT("epoll wait");
  59. else if(nready == 0)
  60. {
  61. printf("epoll timeout.\n");
  62. continue;
  63. }
  64.  
  65. //处理fd
  66. for(i = 0; i < nready; ++i)
  67. {
  68. int fd = events[i].data.fd;
  69. uint32_t revents = events[i].events;
  70. if(fd == listenfd) //处理listen fd
  71. {
  72. if(revents & kReadREvent)
  73. {
  74. //建立连接
  75. int peerfd = accept4(listenfd, NULL, NULL, SOCK_NONBLOCK | SOCK_CLOEXEC);
  76. if(peerfd == -1)
  77. ERR_EXIT("accept4");
  78. //新建tcp连接
  79. tcp_connection_t *pt = (tcp_connection_t*)malloc(sizeof(tcp_connection_t));
  80. buffer_init(&pt->buffer_);
  81. //将该tcp连接放入connsets
  82. connsets[peerfd] = pt;
  83. epoll_add_fd(epollfd, peerfd, 0);
  84. }
  85. }
  86. else //处理普通客户的fd
  87. {
  88. //取出指针
  89. tcp_connection_t *pt = connsets[fd];
  90. assert(pt != NULL);
  91. if(revents & kReadREvent)
  92. {
  93. if(buffer_read(&pt->buffer_, fd) == 0)
  94. {
  95. //close
  96. epoll_del_fd(epollfd, fd);
  97. close(fd);
  98. free(pt);
  99. connsets[fd] = NULL;
  100. continue; //继续下一次循环
  101. }
  102. }
  103.  
  104. if(revents & kWriteREvent)
  105. {
  106. buffer_write(&pt->buffer_, fd);
  107. }
  108. }
  109. }
  110. }
  111.  
  112. close(listenfd);
  113.  
  114. return 0;
  115. }

 

下文使用epoll的ET模式。

Linux非阻塞IO(八)使用epoll重新实现非阻塞的回射服务器的更多相关文章

  1. Linux非阻塞IO(二)网络编程中非阻塞IO与IO复用模型结合

    上文描述了最简易的非阻塞IO,采用的是轮询的方式,这节我们使用IO复用模型.   阻塞IO   过去我们使用IO复用与阻塞IO结合的时候,IO复用模型起到的作用是并发监听多个fd. 以简单的回射服务器 ...

  2. epoll 实现回射服务器

    epoll是I/O复用模型中相对epoll和select更高效的实现对套接字管理的函数. epoll有两种模式 LT 和 ET 二者的差异在于 level-trigger 模式下只要某个 socket ...

  3. Linux下select的用法--实现一个简单的回射服务器程序

    1.先看man手册 SYNOPSIS       /* According to POSIX.1-2001 */       #include <sys/select.h>       / ...

  4. Linux非阻塞IO(五)使用poll实现非阻塞的回射服务器客户端

    前面几节我们讨论了非阻塞IO的基本概念.Buffer的设计以及非阻塞connect的实现,现在我们使用它们来完成客户端的编写. 我们在http://www.cnblogs.com/inevermore ...

  5. linux网络编程 IO多路复用 select epoll

    本文以我的小型聊天室为例,对于服务器端的代码,做了三次改进,我将分别介绍阻塞式IO,select,epoll . 一:阻塞式IO 对于聊天室这种程序,我们最容易想到的是在服务器端accept之后,然后 ...

  6. 实例浅析epoll的水平触发和边缘触发,以及边缘触发为什么要使用非阻塞IO

    一.基本概念                                                          我们通俗一点讲: Level_triggered(水平触发):当被监控的 ...

  7. epoll的水平触发和边缘触发,以及边缘触发为什么要使用非阻塞IO

    转自:http://www.cnblogs.com/yuuyuu/p/5103744.html 一.基本概念                                               ...

  8. Linux非阻塞IO(四)非阻塞IO中connect的实现

    我们为客户端的编写再做一些工作. 这次我们使用非阻塞IO实现connect函数. int connect(int sockfd, const struct sockaddr *addr, sockle ...

  9. Linux非阻塞IO(三)非阻塞IO中缓冲区Buffer的实现

    本文我们来实现回射服务器的Buffer.   Buffer的实现   上节提到了非阻塞IO必须具备Buffer.再次将Buffer的设计描述一下: 这里必须补充一点,writeIndex指向空闲空间的 ...

随机推荐

  1. Socket学习进阶之基础通信

    服务端代码: using System; using System.Text; using System.Net; using System.Net.Sockets; public class ser ...

  2. [ CodeVS冲杯之路 ] P3117

    不充钱,你怎么AC? 题目:http://codevs.cn/problem/3117/ 啊啊啊,基础的高精度乘法被我写得又臭又长,以后再来优化代码(DP着哪天能够把加减乘除全部写一边贴上来,哦对还有 ...

  3. 培训补坑(day4:网络流建模与二分图匹配)

    补坑时间到QAQ 好吧今天讲的是网络流建模与二分图匹配... day3的网络流建模好像说的差不多了.(囧) 那就接着补点吧.. 既然昨天讲了建图思想,那今天就讲讲网络流最重要的技巧:拆点. 拆点,顾名 ...

  4. (转)Python 操作 Windows 粘贴板

    转自: http://outofmemory.cn/code-snippet/3939/Python-operation-Windows-niantie-board Python 操作 Windows ...

  5. MFC学习之EDIT控件初始化

    //四种方法为EDIT控件初始化     //调用系统API     HWND hEidt = ::GetDlgItem(m_hWnd,IDC_EDIT1);     ::SetWindowText( ...

  6. myeclipse10.7配置resin4.0.36

    Resin-4.0.35 (built Tue, 12 Feb 2013 10:05:50 PST) Copyright(c) 1998-2012 Caucho Technology.  All ri ...

  7. Python_Tips[2] -> 函数延后估值及字节码分析

    函数延后估值及字节码分析 在一个循环中定义了函数 f 但是并未对其进行调用,在循环结束后调用,此时i值为3故最终3个函数输出均为9.而非1, 4, 9. 这是由于在定义闭包函数 f 时,传入变量 i, ...

  8. #420 Div2 D

    #420 Div2 D 题意 给出一个方格矩阵,其中存在亮着的方格,只能在亮着的方格上行走,可以在初始亮的方格上花费一枚硬币临时点亮任意一行或一列,地图上同一时间只能存在一个这样的行或列,问走到终点最 ...

  9. 「Codeforces Round #441」 Classroom Watch

    Discription Eighth-grader Vova is on duty today in the class. After classes, he went into the office ...

  10. Unity3D的主要类图

    原文:http://blog.teotigraphix.com/2011/05/17/unity3d-uml-gameobject-cheat-sheet/ 1. GameObject_Structu ...