学习Nginx源码有一段时间了,从中确实收获颇丰,其中蕴含的许多编程思想,编程模式都非常值得借鉴,我采用的方法就是,边研读源码,边调试,边修改运行程序,或者干脆自己写一个,当然,不可能像Nginx那么复杂,但是主要是为了测试某些框架和机制吧,以下就是一个很简单的框架,用于说明Nginx的大致事件处理模型,功能很简单,监听请求,然后发送response。
- #include "func.h"
- int main(void)
- {
- cycle_init();
-
- epoll_init();
- tcp_listen();
- int events, i;
- struct event *rev;
- struct connection *c;
- struct epoll_event *ee;
- for ( ; ; ) {
-
- if ((events = epoll_wait(epfd, event_list, nevents, -1)) < 0)
- err_sys("epoll wait error");
-
- for (i = 0; i < events; i++) {
- ee = &event_list[i];
- c = ee->data.ptr;
- rev = c->rev;
- rev->handler(rev);
- }
- }
- return 0;
- }
从以上这一段代码可以看出,首先是一系列的初始化,包括事件框架的初始化,epoll初始化,还有监听端口的初始化,之后就在一个循环中进入事件处理过程。不过我们注意到这里的处理过程和我们通常所用到的有所区别,先通过返回的epoll_event中的data获取一个connection_t的指针,该指针表示一个连接,而每个连接中都有两个事件read和write, 事件用event_t表示,我们这里只关心读事件,因此只调用读事件的回调函数。对于注册的监听端口来说,其读事件的回调函数为accept_handler,而对于已accept后的连接来说,其读事件的回调函数为read_handler。以下是几个重要的结构体:
- #ifndef _func_h_
- #define _func_h_
- #include <apue.h>
- #define SERV_PORT 10000
- typedef struct event event_t;
- typedef struct connection connection_t;
- typedef struct listening listening_t;
- typedef struct cycle cycle_t;
- typedef struct sockaddr SA;
- typedef void (*event_handler_pt)(event_t *ev);
- struct listening {
- struct sockaddr sockaddr;
- socklen_t socklen;
- connection_t *connection;
- int fd;
- };
- struct connection {
- struct sockaddr *sockaddr;
- socklen_t socklen;
- int fd;
- unsigned accept:1;
- struct event *rev, *wev;
- struct listening *listening;
- char addr_text[32];
- };
- struct event {
- event_handler_pt handler;
- struct connection *connection;
- unsigned accept:1;
- };
- struct cycle {
- listening_t listening[1];
- connection_t connections[512];
- event_t read_events[512];
- event_t write_events[512];
- };
- void cycle_init(void);
- void tcp_listen(void);
- void epoll_init(void);
- connection_t *get_connection(int fd);
- void free_connection(connection_t *c);
- void accept_handler(event_t *rev);
- void event_add(event_t *ev, int event, int flag);
- void read_handler(event_t *ev);
- extern int epfd;
- extern struct epoll_event event_list[];
- extern int nevents;
- #endif
以下这段代码中重点看两个主要的回调函数,accept_handler和read_handler。在accept_handler中,首先
调用accept接受连接,然后为这个新fd分配一个connection结构体,并设置该结构体内的读事件的回调函数,然后将该事件注册到epoll中。注意,这里向epoll注册时传递的是rev参数,其实这个rev内部保存了其对应的connection指针,而通过connection又可以获得其fd。
- #include "func.h"
- int epfd = -1;
- struct epoll_event event_list[1024];
- int nevents = 1024;
- cycle_t *ngx_cycle;
- void cycle_init(void)
- {
- int i;
- ngx_cycle = calloc(1, sizeof *ngx_cycle);
-
- for (i = 0; i < 512; i++) {
- ngx_cycle->connections[i].fd = -1;
- ngx_cycle->connections[i].rev = &ngx_cycle->read_events[i];
- ngx_cycle->connections[i].wev = &ngx_cycle->write_events[i];
- ngx_cycle->read_events[i].connection = &ngx_cycle->connections[i];
- ngx_cycle->write_events[i].connection = &ngx_cycle->connections[i];
- ngx_cycle->connections[i].sockaddr = calloc(1, sizeof(struct sockaddr));
- ngx_cycle->connections[i].socklen = sizeof(struct sockaddr);
- }
- }
- connection_t *
- get_connection(int fd)
- {
- int i;
- struct connection *c;
- event_t *rev, *wev, *sockaddr;
- for (i = 0; i < 512; i++) {
- if (ngx_cycle->connections[i].fd == -1)
- break;
- }
- assert(i < 512);
- c = &ngx_cycle->connections[i];
-
- c->fd = fd;
- return c;
- }
- void free_connection(connection_t *c)
- {
- c->fd == -1;
- }
- void
- accept_handler(event_t *rev)
- {
- int s, connfd;
- socklen_t socklen;
- struct connection *c, *lc;
- struct listening *ls;
- char addr_text[64];
- struct sockaddr_in *addr;
- char sa[sizeof(SA)];
- const int on = 1;
- lc = rev->connection;
- ls = lc->listening;
- do {
- socklen = sizeof(SA);
-
- s = accept(lc->fd, (SA *)sa, &socklen);
- if (s == -1) {
- if (errno == EAGAIN)
- return;
- if (errno == ECONNABORTED)
- continue;
- return;
- }
- c = get_connection(s);
- memcpy(c->sockaddr, sa, socklen);
-
- if (ioctl(s, FIONBIO, &on) < 0)
- err_sys("nonblocking error");
-
- c->socklen = socklen;
-
- addr = (struct sockaddr_in *) c->sockaddr;
- inet_ntop(AF_INET, &addr->sin_addr, addr_text, c->socklen);
- snprintf(c->addr_text, sizeof c->addr_text, "%s : %d",
- addr_text, ntohs(addr->sin_port));
- rev = c->rev;
- rev->handler = read_handler;
- event_add(rev, EPOLLIN | EPOLLOUT, EPOLLET);
- } while (1);
- }
- void read_handler(event_t *rev)
- {
- int fd, n;
- char buf[1024], *p;
- struct connection *c;
- c = rev->connection;
-
- fd = c->fd;
-
- p = buf;
- do {
- n = read(fd, p, 1024 - (p - buf));
- if (n == -1) {
- if (errno == EAGAIN)
- break;;
- if (errno == EINTR)
- continue;
- }
- assert(n != 0);
- p += n;
- } while (1);
- *p = 0;
- printf("\nReceived Message from %s ...\n%s\n",
- c->addr_text, buf);
- 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";
-
- write(fd, reply, strlen(reply));
- close(fd);
- printf("Server close the connection with client %s\n", c->addr_text);
- free_connection(c);
- }
- void
- epoll_init(void) {
- if ((epfd = epoll_create(1024)) < 0)
- err_sys("epoll create error");
- }
- void
- tcp_listen(void)
- {
- int listenfd;
- listening_t *ls;
- struct sockaddr_in servaddr;
- connection_t *c;
- event_t *rev;
- ls = ngx_cycle->listening;
- if ((listenfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
- err_sys("socket error");
- memset(&servaddr, 0, sizeof servaddr);
- servaddr.sin_family = AF_INET;
- servaddr.sin_port = htons(SERV_PORT);
- servaddr.sin_addr.s_addr = htonl(0);
-
- memcpy(&ls->sockaddr, (SA *) &servaddr, sizeof(SA));
- ls->socklen = sizeof(SA);
- ls->fd = listenfd;
- c = get_connection(listenfd);
-
- ls->connection = c;
- c->listening = ls;
- const int on = 1;
- if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof on) < 0)
- err_sys("setsockopt error");
- if (ioctl(listenfd, FIONBIO, &on) < 0)
- err_sys("set nonblocking error");
- if (bind(listenfd, (struct sockaddr *) &servaddr, sizeof servaddr) < 0)
- err_sys("bind error");
- if (listen(listenfd, 1000) < 0)
- err_sys("listen error");
-
- rev = c->rev;
- rev->handler = accept_handler;
- event_add(c->rev, EPOLLIN, 0);
- }
- void
- event_add(event_t *ev, int event, int flag)
- {
- struct epoll_event ee;
- struct connection *c;
- c = ev->connection;
- ee.data.ptr = ev->connection;
- ee.events = event | flag;
- if (epoll_ctl(epfd, EPOLL_CTL_ADD, c->fd, &ee) < 0)
- err_sys("epoll ctl error");
- }
阅读(1883) | 评论(2) | 转发(0) |