Chinaunix首页 | 论坛 | 博客
  • 博客访问: 188667
  • 博文数量: 27
  • 博客积分: 725
  • 博客等级: 上士
  • 技术积分: 347
  • 用 户 组: 普通用户
  • 注册时间: 2008-10-04 09:01
文章分类

全部博文(27)

文章存档

2012年(15)

2011年(12)

分类: LINUX

2012-03-27 19:34:33

学习Nginx源码有一段时间了,从中确实收获颇丰,其中蕴含的许多编程思想,编程模式都非常值得借鉴,我采用的方法就是,边研读源码,边调试,边修改运行程序,或者干脆自己写一个,当然,不可能像Nginx那么复杂,但是主要是为了测试某些框架和机制吧,以下就是一个很简单的框架,用于说明Nginx的大致事件处理模型,功能很简单,监听请求,然后发送response。

  1. #include "func.h"

  2. int main(void)
  3. {
  4.     cycle_init();
  5.     
  6.     epoll_init();

  7.     tcp_listen();

  8.     int events, i;
  9.     struct event        *rev;
  10.     struct connection    *c;
  11.     struct epoll_event    *ee;

  12.     for ( ; ; ) {
  13.         
  14.         if ((events = epoll_wait(epfd, event_list, nevents, -1)) < 0)
  15.             err_sys("epoll wait error");
  16.         
  17.         for (i = 0; i < events; i++) {
  18.             ee = &event_list[i];
  19.             c = ee->data.ptr;
  20.             rev = c->rev;
  21.             rev->handler(rev);
  22.         }
  23.     }

  24.     return 0;
  25. }
从以上这一段代码可以看出,首先是一系列的初始化,包括事件框架的初始化,epoll初始化,还有监听端口的初始化,之后就在一个循环中进入事件处理过程。不过我们注意到这里的处理过程和我们通常所用到的有所区别,先通过返回的epoll_event中的data获取一个connection_t的指针,该指针表示一个连接,而每个连接中都有两个事件read和write, 事件用event_t表示,我们这里只关心读事件,因此只调用读事件的回调函数。对于注册的监听端口来说,其读事件的回调函数为accept_handler,而对于已accept后的连接来说,其读事件的回调函数为read_handler。以下是几个重要的结构体:
  1. #ifndef _func_h_
  2. #define _func_h_

  3. #include <apue.h>

  4. #define SERV_PORT 10000

  5. typedef struct event         event_t;
  6. typedef struct connection    connection_t;
  7. typedef struct listening    listening_t;
  8. typedef struct cycle        cycle_t;
  9. typedef struct sockaddr        SA;

  10. typedef void (*event_handler_pt)(event_t *ev);

  11. struct listening {
  12.     struct sockaddr         sockaddr;
  13.     socklen_t             socklen;
  14.     connection_t        *connection;
  15.     int                     fd;
  16. };

  17. struct connection {
  18.     struct sockaddr         *sockaddr;
  19.     socklen_t             socklen;
  20.     int                     fd;
  21.     unsigned             accept:1;
  22.     struct    event        *rev, *wev;
  23.     struct listening    *listening;
  24.     char                  addr_text[32];
  25. };

  26. struct event {
  27.     event_handler_pt    handler;
  28.     struct connection *connection;
  29.     unsigned            accept:1;
  30. };

  31. struct cycle {
  32.     listening_t            listening[1];
  33.     connection_t        connections[512];
  34.     event_t                read_events[512];
  35.     event_t                write_events[512];
  36. };

  37. void              cycle_init(void);
  38. void               tcp_listen(void);
  39. void               epoll_init(void);
  40. connection_t    *get_connection(int fd);
  41. void             free_connection(connection_t    *c);
  42. void              accept_handler(event_t *rev);
  43. void              event_add(event_t    *ev, int event, int flag);
  44. void              read_handler(event_t *ev);

  45. extern int epfd;
  46. extern struct epoll_event    event_list[];
  47. extern int nevents;

  48. #endif
以下这段代码中重点看两个主要的回调函数,accept_handler和read_handler。在accept_handler中,首先
调用accept接受连接,然后为这个新fd分配一个connection结构体,并设置该结构体内的读事件的回调函数,然后将该事件注册到epoll中。注意,这里向epoll注册时传递的是rev参数,其实这个rev内部保存了其对应的connection指针,而通过connection又可以获得其fd。
  1. #include "func.h"

  2. int epfd = -1;
  3. struct epoll_event    event_list[1024];
  4. int nevents = 1024;

  5. cycle_t *ngx_cycle;

  6. void cycle_init(void)
  7. {
  8.     int i;

  9.     ngx_cycle = calloc(1, sizeof *ngx_cycle);
  10.     
  11.     for (i = 0; i < 512; i++) {
  12.         ngx_cycle->connections[i].fd = -1;
  13.         ngx_cycle->connections[i].rev = &ngx_cycle->read_events[i];
  14.         ngx_cycle->connections[i].wev = &ngx_cycle->write_events[i];
  15.         ngx_cycle->read_events[i].connection = &ngx_cycle->connections[i];
  16.         ngx_cycle->write_events[i].connection = &ngx_cycle->connections[i];
  17.         ngx_cycle->connections[i].sockaddr = calloc(1, sizeof(struct sockaddr));
  18.         ngx_cycle->connections[i].socklen = sizeof(struct sockaddr);
  19.     }
  20. }

  21. connection_t *
  22. get_connection(int fd)
  23. {
  24.     int i;
  25.     struct connection *c;
  26.     event_t *rev, *wev, *sockaddr;

  27.     for (i = 0; i < 512; i++) {
  28.         if (ngx_cycle->connections[i].fd == -1)
  29.             break;
  30.     }

  31.     assert(i < 512);

  32.     c = &ngx_cycle->connections[i];
  33.     
  34.     c->fd = fd;

  35.     return c;
  36. }

  37. void free_connection(connection_t *c)
  38. {
  39.     c->fd == -1;
  40. }

  41. void
  42. accept_handler(event_t *rev)
  43. {
  44.     int                      s, connfd;
  45.     socklen_t                 socklen;
  46.     struct connection         *c, *lc;
  47.     struct listening        *ls;
  48.     char                      addr_text[64];
  49.     struct sockaddr_in        *addr;
  50.     char                      sa[sizeof(SA)];

  51.     const int on = 1;

  52.     lc = rev->connection;
  53.     ls = lc->listening;

  54.     do {
  55.         socklen = sizeof(SA);
  56.         
  57.         s = accept(lc->fd, (SA *)sa, &socklen);

  58.         if (s == -1) {
  59.             if (errno == EAGAIN)
  60.                 return;
  61.             if (errno == ECONNABORTED)
  62.                 continue;
  63.             return;
  64.         }

  65.         c = get_connection(s);
  66.         memcpy(c->sockaddr, sa, socklen);
  67.         
  68.         if (ioctl(s, FIONBIO, &on) < 0)
  69.             err_sys("nonblocking error");
  70.         
  71.         c->socklen = socklen;
  72.         
  73.         addr = (struct sockaddr_in *) c->sockaddr;
  74.         inet_ntop(AF_INET, &addr->sin_addr, addr_text, c->socklen);
  75.         snprintf(c->addr_text, sizeof c->addr_text, "%s : %d",
  76.                 addr_text, ntohs(addr->sin_port));

  77.         rev = c->rev;

  78.         rev->handler = read_handler;

  79.         event_add(rev, EPOLLIN | EPOLLOUT, EPOLLET);

  80.     } while (1);
  81. }

  82. void read_handler(event_t *rev)
  83. {
  84.     int                  fd, n;
  85.     char                  buf[1024], *p;
  86.     struct connection    *c;

  87.     c = rev->connection;
  88.     
  89.     fd = c->fd;
  90.     
  91.     p = buf;

  92.     do {
  93.         n = read(fd, p, 1024 - (p - buf));

  94.         if (n == -1) {
  95.             if (errno == EAGAIN)
  96.                 break;;
  97.             if (errno == EINTR)
  98.                 continue;
  99.         }

  100.         assert(n != 0);

  101.         p += n;

  102.     } while (1);

  103.     *p = 0;

  104.     printf("\nReceived Message from %s ...\n%s\n",
  105.             c->addr_text, buf);

  106.     char *reply = "\n\nWelcome to my first server !\nAlthouth it's very simple, I sincerely hope it can give you the best services.\nI promise the second edition will be much better and wish you have a nice experience ...\n\n";
  107.     
  108.     write(fd, reply, strlen(reply));

  109.     close(fd);

  110.     printf("Server close the connection with client %s\n", c->addr_text);

  111.     free_connection(c);
  112. }

  113. void
  114. epoll_init(void) {
  115.     if ((epfd = epoll_create(1024)) < 0)
  116.         err_sys("epoll create error");
  117. }

  118. void
  119. tcp_listen(void)
  120. {
  121.     int listenfd;
  122.     listening_t            *ls;
  123.     struct sockaddr_in      servaddr;
  124.     connection_t        *c;
  125.     event_t                *rev;

  126.     ls = ngx_cycle->listening;

  127.     if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
  128.         err_sys("socket error");

  129.     memset(&servaddr, 0, sizeof servaddr);
  130.     servaddr.sin_family = AF_INET;
  131.     servaddr.sin_port = htons(SERV_PORT);
  132.     servaddr.sin_addr.s_addr = htonl(0);
  133.     
  134.     memcpy(&ls->sockaddr, (SA *) &servaddr, sizeof(SA));
  135.     ls->socklen = sizeof(SA);
  136.     ls->fd = listenfd;

  137.     c = get_connection(listenfd);
  138.     
  139.     ls->connection = c;
  140.     c->listening = ls;

  141.     const int on = 1;
  142.     if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof on) < 0)
  143.         err_sys("setsockopt error");

  144.     if (ioctl(listenfd, FIONBIO, &on) < 0)
  145.         err_sys("set nonblocking error");

  146.     if (bind(listenfd, (struct sockaddr *) &servaddr, sizeof servaddr) < 0)
  147.         err_sys("bind error");

  148.     if (listen(listenfd, 1000) < 0)
  149.         err_sys("listen error");
  150.     
  151.     rev = c->rev;
  152.     rev->handler = accept_handler;

  153.     event_add(c->rev, EPOLLIN, 0);
  154. }

  155. void
  156. event_add(event_t *ev, int event, int flag)
  157. {
  158.     struct epoll_event      ee;
  159.     struct connection    *c;

  160.     c = ev->connection;

  161.     ee.data.ptr = ev->connection;
  162.     ee.events = event | flag;

  163.     if (epoll_ctl(epfd, EPOLL_CTL_ADD, c->fd, &ee) < 0)
  164.         err_sys("epoll ctl error");
  165. }







阅读(1833) | 评论(2) | 转发(0) |
给主人留下些什么吧!~~

jmy24462672012-04-01 07:37:18

GFree_Wind: 加上一些设计说明就好了。。。因为很少有人看博文的时候,会一行行的读代码的。.....
朋友,谢谢您的建议

GFree_Wind2012-03-29 11:48:20

加上一些设计说明就好了。。。因为很少有人看博文的时候,会一行行的读代码的。