c半同步半异步进程池模型之cgi服务器
对半同步半异步进程池模型垂涎已久,这次中秋放假撸了下代码,代码写了几个模块,分别是:
util:封装了套接字创建、unix族socket管道创建、中断信号、简单屏幕输出(可自行替换为日志文件输出)
epoll_wrapper:封装了epoll相关操作包括创建epfd、添加epoll监听事件、删除epoll监听事件
myhshappool(我的半同步半异步进程池 - -!…):封装了进程池初始化、启动进程池进行事件监听
client_handle:进程池监听到客户事件、即调用client_handle封装的处理事件,这里封装的是执行cgi文件向客户端浏览器返回服务器时间(最近在看unix网络编程,里面都是时间获取的服务器,借鉴下拿来搞事,当然,嵌入式里拿来控制个灯泡开关想来特别带劲,用android做个网页app,板子接wifi模块接智能灯,cgi负责开关灯泡 。。)
cgisrv:入口,初始化进程池,启动进程池
代码快1k行,不知道一个博客文章能不能写下,不太会用github,况且这种玩具demo代码就不往github放了。代码中凑合写了注释(有时候不想切换中英文因此用了蹩脚的英文注释),限(wo)于(tai)篇(lan)幅(le)没有写文件头注释和函数头注释。
util.h:
#ifndef _UTIL_H #define _UTIL_H #include #include #include #include #include #include #include #include #include #include //这里不用 #转字符串了,不方便管理等级 #ifdef LEVELNUMPRINT #define LEVEL0 "LEVEL0" #define LEVEL1 "LEVEL1" #define LEVEL2 "LEVEL2" #define LEVEL3 "LEVEL3" #define LEVEL4 "LEVEL4" #define LEVEL4 "LEVEL5" #else #define LEVEL0 "DEBUG" #define LEVEL1 "INFO" #define LEVEL2 "NOTICE" #define LEVEL3 "WARN" #define LEVEL4 "ERROR" #define LEVEL5 "FATAL" #endif #define PRINTINFO( LEVEL, format, args... ) \ do { \ printf("[%s]:(pid:%d)/(file:%s)/(func:%s)/(line:%d)", \ LEVEL, getpid(), __FILE__, __func__, __LINE__); \ /*printf("\t" #LEVEL ":");*/ \ printf("--|--"); \ printf( format, ##args ); \ printf("\n"); \ } while( 0 ) #define PRINTINFO_ERR( LEVEL, format, args... ) \ do { \ printf("[%s]:(pid:%d)/(file:%s)/(func:%s)/(line:%d)", \ LEVEL, getpid(), __FILE__, __func__, __LINE__); \ /*printf("\t" #LEVEL ":");*/ \ printf("--|--"); \ printf( format, ##args ); \ printf("(errmsg:%s)", strerror(errno)); \ printf("\n"); \ } while( 0 ) void Add_sig( int sig, void (*handler)(int), int restart_syscall ); void Socketpair( int *pairpipefd ); int Socket_create( char *ipaddr, int port ); void Setnonblocking( int fd ); #endif
util.c:
#include "util.h" static int add_sig( int sig, void (*handler)(int), int restart_syscall ) { struct sigaction act; bzero( &act, sizeof(act) ); act.sa_handler = handler; act.sa_flags = 0; //早期unix系统对于进程在执行一个低速系统调用(如ioctl、 //read、write、wait)而阻塞期间捕捉到一个信号,则系统 //调用被中断不再执行,该系统调用返回错误,设置errno为 //EINTR,随后的bsd系统引入了自动重启,即再次进行此系统 //调用。unix衍生系统默认的方式可能为可选、总是等,类 //unix系统的linux系统可能默认为不重启,因此添加重启标识 if ( restart_syscall ) { act.sa_flags |= SA_RESTART; } //宏定义: //#define sigfillset(*p) (*p) = ~(0,0) sigfillset( &act.sa_mask ); if ( -1 == sigaction(sig, &act, NULL) ) { PRINTINFO_ERR( LEVEL4, "sigaction error" ); return -1; } return 0; } void Add_sig( int sig, void (*handler)(int), int restart_syscall ) { if ( add_sig(sig, handler, restart_syscall) < 0 ) { PRINTINFO( LEVEL5, "add_sig error" ); exit( 0 ); } } void Socketpair( int *pairpipefd ) { int ret; ret = socketpair( PF_UNIX, SOCK_STREAM, 0, pairpipefd ); if ( ret < 0 ) { PRINTINFO_ERR( LEVEL5, "socketpair error!!" ); exit( 0 ); } } static int socket_create( char *ipaddr, int port, int backlog ) { int sockfd; int ret; sockfd = socket( AF_INET, SOCK_STREAM, 0 ); if ( sockfd < 0 ) { PRINTINFO_ERR( LEVEL4, "socket error!!!" ); return -1; } struct sockaddr_in addr; bzero( &addr, sizeof(addr) ) ; addr.sin_family = AF_INET; addr.sin_port = htons( port ); inet_pton( AF_INET, ipaddr, &addr.sin_addr ); int reuseaddr = 1; setsockopt( sockfd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(int) ); ret = bind( sockfd, (struct sockaddr *)&addr, (socklen_t)sizeof(addr) ) ; if ( ret < 0 ) { PRINTINFO_ERR( LEVEL4, "bind error!!!" ); return -1; } ret = listen( sockfd, backlog ); if ( ret < 0 ) { PRINTINFO_ERR( LEVEL4, "listen error!!!" ); return -1; } return sockfd; } int Socket_create( char *ipaddr, int port ) { int ret; ret = socket_create( ipaddr, port, 5 ); if ( ret < 0 ) { PRINTINFO( LEVEL5, "socket_creaet error!!!" ); exit( 0 ); } return ret; } static int setnonblocking( int fd ) { int old_opt = fcntl( fd, F_GETFL ); int new_opt = old_opt | O_NONBLOCK; fcntl( fd, F_SETFL, new_opt ); return old_opt; } void Setnonblocking( int fd ) { setnonblocking( fd ); } int Send ( int socket_fd, const unsigned char * send_buf, int buf_size, int flag ) { int snd_bytes = 0; int snd_total_bytes = 0; int snd_count = 3; while ( snd_count -- ) { snd_bytes = send( socket_fd, send_buf, buf_size, flag ); if ( snd_bytes <= 0 ) { if ( EAGAIN == errno || EINTR == errno || EWOULDBLOCK == errno ) { //暂时发送失败,需要重复发送 usleep( 50 ); continue; }else { //连接不正常,返回-1交由上层清理此套接字 PRINTINFO_ERR( LEVEL4, "send return error!!!" ); return -1; } } snd_total_bytes += snd_bytes; if ( snd_total_bytes >= buf_size ) { break; } } if ( !snd_count ) { PRINTINFO( LEVEL4, "send timeout!!!" ); return -1; } return snd_total_bytes; } #if 0 int main() { PRINTINFO( LEVEL0, "likun:%d", 123 ); PRINTINFO( LEVEL1, "likun:" ); //PRINTINFO( likun, "likun:" ); return 0; } #endif
epoll_wrapper.h:
#ifndef _EPOLL_WRAPPER_H #define _EPOLL_WRAPPER_H #include #include int Epoll_create( int size ); int Epoll_wait( int epfd, struct epoll_event *events, int maxevents, int timeout ); void Epoll_add_fd( int epfd, int fd ); void Epoll_del_fd( int epfd, int fd ); #endif
epoll_wrapper.c:
#include "epoll_wrapper.h" #include "util.h" static int epoll_create0( int size ) { int ret; ret = epoll_create( size ); if ( ret <= 0 ) { PRINTINFO_ERR( LEVEL3, "epoll_create error!!!" ); return -1; } return ret; } int Epoll_create( int size ) { int ret; if ( (ret = epoll_create0(size)) < 0 ) { PRINTINFO( LEVEL5, "epoll_create0 error!!!" ); exit( 0 ); } return ret; } int Epoll_wait( int epfd, struct epoll_event *events, int maxevents, int timeout ) { return epoll_wait( epfd, events, maxevents, timeout ); } static int epoll_add_fd( int epfd, int fd ) { struct epoll_event event; event.data.fd = fd; event.events = EPOLLIN | EPOLLET; epoll_ctl( epfd, EPOLL_CTL_ADD, fd, &event ); Setnonblocking( fd ); return 0; } void Epoll_add_fd( int epfd, int fd ) { epoll_add_fd( epfd, fd ); } void Epoll_del_fd( int epfd, int fd ) { epoll_ctl( epfd, EPOLL_CTL_DEL, fd, NULL ); } #if 0 int main() {} #endif
myhshappool.h:
#ifndef _MY_HS_HA_P_POOL_H #define _MY_HS_HA_P_POOL_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include typedef struct process { //当前进程id号 pid_t pid; //与父进程通信用的管道 //0端父进程写 //1端子进程读 int pipefd[2]; } process; typedef struct processpool { //进程池最大进程数 int max_process_num; //每个进程最大处理客户数量 int max_user_num; //epoll最多处理的事件数 int max_epoll_event; //当前进程池进程总数 int cur_process_num; //当前子进程在进程池序号,从0开始 int index; //每个进程一个epoll内核事件表 int epollfd; //监听socket int listenfd; //停止线程池 int stop; //进程池子进程管理 struct process *sub_process; } processpool; int init_process_pool( processpool *ppool, int maxpnum, int maxunum, int maxeevent, int curpnum, int listenfd ); void run( processpool *ppool ); #endif
myhshappool.c:
#include "myhshappool.h" #include "client_handle.h" #include "util.h" //用于信号中断时主进程通信, //统一处理事件,即将客户端连接 //事件、信号事件都统一用epoll //监听处理,0端信号处理函数写, //1端进程读 static int sig_pipefd[2]; static void sig_handler( int sig ) { //保存旧的errno,对后续的send不 //进行错误判定,但send假如返回 //失败会设置errno,信号中断调用 //结束后影响进程其它模块判断 int old_errno = errno; char signo = (char)sig; send( sig_pipefd[0], (char *)&signo, 1, 0 ); errno = old_errno; } static void init_signal( processpool *ppool ) { Socketpair( sig_pipefd ); Setnonblocking( sig_pipefd[0] ); //Epoll_add_fd( ppool->epollfd, sig_pipefd[1] ); Add_sig( SIGCHLD, sig_handler, 1 ); Add_sig( SIGTERM, sig_handler, 1 ); Add_sig( SIGINT, sig_handler, 1 ); Add_sig( SIGPIPE, SIG_IGN, 1 ); } int init_process_pool( processpool *ppool, int maxpnum, int maxunum, int maxeevent, int curpnum, int listenfd ) { if ( !ppool ) { PRINTINFO( LEVEL4, "ppool is null!!!" ); return -1; } ppool->max_process_num = maxpnum; ppool->max_user_num = maxunum; ppool->max_epoll_event = maxeevent; ppool->cur_process_num = curpnum; ppool->listenfd = listenfd; // ppool->epollfd = Epoll_create( 5 ); ppool->index = -1; ppool->stop = 0; ppool->sub_process = (process *)calloc( sizeof(process), curpnum ); if ( !ppool->sub_process ) { PRINTINFO( LEVEL4, "sub_process calloc error!!!" ); return -1; } int i = 0; int pid; //先模拟一下进程池创建之前的情况,假设终端 //bash shell进程id为1000,运行此程序进程id //为1001,其父进程为1000,fork之后主进程不 //变,子进程id为1002,其父进程为1001,因此 //明白fork的过程,下面可以走一下进程池创建 //的流程(条件均为以上假设): //第一次fork:创建亲缘进程的管道,父进程1001 //,子进程1002,其父进程为1001,子进程不再 //执行for循环,且主进程与1002子进程有单独通 //信的管道 //第二次fork:创建亲缘进程的管道,父进程1001 //,子进程1003,其父进程为1001,子进程不再 //执行for循环,且主进程与1003子进程有单独通 //信的管道 //第三次fork .....1004..... // ........ //通过以上过程,可以看到for循环次数为创建的 //子进程数量,且每个子进程可以单独与父进程 //通信 // //这里进程创建,没有脱离当前终端的会话, //我觉得可以setsid()来摆脱终端影响 for ( ; i < curpnum; i++ ) { Socketpair( ppool->sub_process[i].pipefd ); pid = fork(); if ( pid > 0 ) { //parent fork close( ppool->sub_process[i].pipefd[1] ); ppool->sub_process[i].pid = pid; Setnonblocking( ppool->sub_process[i].pipefd[0] ); continue; } else if ( pid == 0 ) { //child ppool->index = i; PRINTINFO( LEVEL0, "child(%d):%d\tparent:%d", i + 1, getpid(), getppid() ); close( ppool->sub_process[i].pipefd[0] ); //每次只由父进程去创建进程 break; } else { PRINTINFO( LEVEL5, "fork error!!!" ); exit( 0 ); } } } static int client_signal_handle( processpool *ppool, char *signals, int signals_num ) { int i = 0; for ( ; i < signals_num; i++ ) { switch( signals[i] ) { case SIGCHLD: { PRINTINFO( LEVEL0, "child receive a SIGCHLD signal" ); pid_t pid; int stat; //catch SIGCHLD while ( (pid = waitpid(-1, &stat, WNOHANG)) > 0 ) { continue; } break; } case SIGTERM: { PRINTINFO( LEVEL0, "child receive a SIGTERM signal" ); ppool->stop = 1; break; } case SIGINT: { PRINTINFO( LEVEL0, "child receive a SIGINT signal" ); ppool->stop = 1; break; } default: { break; } } } } static void run_child( processpool *ppool ) { if ( !ppool ) { PRINTINFO( LEVEL4, "ppool is null!!!" ); return; } init_signal( ppool ); ppool->epollfd = Epoll_create( 5 ); PRINTINFO( LEVEL0, "child cur process:%d", ppool->cur_process_num ); int pipefd = ppool->sub_process[ppool->index].pipefd[1]; Epoll_add_fd( ppool->epollfd, pipefd ); Epoll_add_fd( ppool->epollfd, sig_pipefd[1] ); struct epoll_event *events = (struct epoll_event *) calloc( sizeof(struct epoll_event), ppool->max_epoll_event ); if ( !events ) { PRINTINFO( LEVEL5, "calloc error!!!" ); goto _free_source; } struct client_param *cparam = NULL; cparam = (struct client_param *) calloc( sizeof(struct client_param), ppool->max_user_num ); if ( !cparam ) { PRINTINFO( LEVEL5, "calloc error!!!" ); goto _free_source; } int event_num, event_fd; int i, j, ret, onebyte; while ( !ppool->stop ) { event_num = Epoll_wait( ppool->epollfd, events, ppool->max_epoll_event, -1); //PRINTINFO( LEVEL0, "child event num:%d", event_num ); if ( (event_num < 0) && (errno != EINTR) ) { PRINTINFO_ERR( LEVEL4, "Epoll_wait error!!!" ); ppool->stop = 1; break; } for ( i = 0; i < event_num; i++ ) { event_fd = events[i].data.fd; //parent process notify that there is a new client connect to. if ( event_fd == pipefd && events[i].events & EPOLLIN ) { PRINTINFO( LEVEL0, "receive signal from parent there is a new client connection" ); ret = recv( event_fd, (char *)&onebyte, 1, 0 ); if ( ret <= 0 ) { continue; } else { struct sockaddr_in clientaddr; socklen_t addrlen = sizeof(clientaddr); bzero( &clientaddr, addrlen ); int connfd = accept( ppool->listenfd, (struct sockaddr *)&clientaddr, &addrlen ); if ( connfd < 0 ) { PRINTINFO_ERR( LEVEL3, "accept a new client error!!!" ); continue; } PRINTINFO( LEVEL0, "one client conntect(fd:%d)", connfd ); Epoll_add_fd( ppool->epollfd, connfd ); client_param_init( &cparam[connfd], connfd, &clientaddr ); } } //process catch a signal else if ( event_fd == sig_pipefd[1] && events[i].events & EPOLLIN ) { int sig; char signals[1024] = {0}; ret = recv( sig_pipefd[1], signals, sizeof(signals), 0 ); if ( ret <= 0 ) { continue; } client_signal_handle( ppool, signals, ret ); } //client socket fd has readable event,maybe a //request else if ( events[i].events & EPOLLIN ) { client_handle( &cparam[event_fd] ); } else { continue; } } } _free_source: free( events ); events = NULL; free( cparam ); cparam = NULL; close( pipefd ); close( ppool->epollfd ); } static int parent_signal_handle( processpool *ppool, char *signals, int signals_num ) { int i = 0, j = 0; for ( ; i < signals_num; i++ ) { switch( signals[i] ) { case SIGCHLD: { PRINTINFO( LEVEL0, "parent receive SIGCHLD signal" ); pid_t pid; int stat; while ( (pid = waitpid(-1, &stat, WNOHANG)) > 0 ) { PRINTINFO( LEVEL0, "parent receive SIGCHLD signal(pid:%d)", pid ); for ( j = 0; j < ppool->cur_process_num; j++ ) { if ( ppool->sub_process[j].pid == pid ) { PRINTINFO( LEVEL3, "child process(%d) exit.", pid ); close( ppool->sub_process[j].pipefd[1] ); ppool->sub_process[j].pid = -1; } } } ppool->stop = 1; for ( j = 0; j < ppool->cur_process_num; j++ ) { PRINTINFO( LEVEL0, "pid:%d", ppool->sub_process[j].pid ); if ( ppool->sub_process[j].pid != -1 ) { ppool->stop = 0; break; } } break; } case SIGTERM: case SIGINT: { PRINTINFO( LEVEL2, "recv SIGINT/SIGTERM, kill all child process now." ); //PRINTINFO( LEVEL0, "cur_process_num:%d", ppool->cur_process_num ); for ( i = 0; i < ppool->cur_process_num; i++ ) { int pid = ppool->sub_process[i].pid; if ( pid != -1 ) { PRINTINFO( LEVEL0, "kill process:%d", pid ); ppool->sub_process[i].pid = -1; kill( pid, SIGTERM ); } } ppool->stop = 1; break; } default: { break; } } } } static void run_parent( processpool *ppool ) { if ( !ppool ) { PRINTINFO( LEVEL4, "ppool is null!!!" ); return; } init_signal( ppool ); ppool->epollfd = Epoll_create( 5 ); Epoll_add_fd( ppool->epollfd, ppool->listenfd ); Epoll_add_fd( ppool->epollfd, sig_pipefd[1] ); struct epoll_event *events = NULL; events = (struct epoll_event *) calloc( sizeof(struct epoll_event), ppool->max_epoll_event ); if ( !events ) { PRINTINFO( LEVEL5, "calloc error!!!" ); char sig = SIGINT; parent_signal_handle( ppool, &sig, 1 ); goto _free_source; } int event_num; int i, onebyte = 1, ret, j; //p_idx specifies current dispatched child process. //roll_index specifies the next child process. int p_idx , roll_index = 0; //PRINTINFO( LEVEL0, "epollfd:%d", ppool->epollfd ); while ( !ppool->stop ) { //Specifying a timeout of -1 causes epoll_wait() //to block indefinitely. event_num = Epoll_wait( ppool->epollfd, events, ppool->max_epoll_event, -1 ); if ( event_num < 0 && errno != EINTR ) { PRINTINFO_ERR( LEVEL5, "Epoll_wait error!!!" ); ppool->stop = 1; break; } //PRINTINFO( LEVEL0, "event_num:%d", event_num ); for ( i = 0; i < event_num; i++ ) { int event_fd = events[i].data.fd; //listenfd,there is a new client connection. //notify child process to accept if ( event_fd == ppool->listenfd ) { PRINTINFO( LEVEL0, "event:parent listenfd" ); //round robin dispatch //easily roll polling p_idx = roll_index; do { if ( ppool->sub_process[p_idx].pid != -1 ) { break; } p_idx = ( p_idx + 1 ) % ppool->cur_process_num; } while ( p_idx != roll_index ); //roll polling all the child process,but they are //all run error.so p_idx equals to roll_index. if ( ppool->sub_process[p_idx].pid < 0 ) { ppool->stop = 1; break; } roll_index = ( p_idx + 1 ) % ppool->cur_process_num; if ( Send( ppool->sub_process[p_idx].pipefd[0], (char *)&onebyte, 1, 0 ) < 0 ) { PRINTINFO( LEVEL5, "Send error!!!" ); ppool->stop = 1; break; } } //receive signal from signal handler. else if ( (event_fd == sig_pipefd[1]) && (events[i].events & EPOLLIN) ) { PRINTINFO( LEVEL0, "event:parent receive signal" ); int sig; char signals[1024]; ret = recv( sig_pipefd[1], signals, sizeof(signals), 0 ); if ( ret <= 0 ) { continue; } else { parent_signal_handle( ppool, signals, ret ); } } } } _free_source: free( events ); events = NULL; close( ppool->epollfd ); } void run( processpool *ppool ) { if ( ppool->index != -1 ) { run_child( ppool ); return; } run_parent( ppool ); }
client_handle.h:
#ifndef _CLIENT_HANDLE_H #define _CLIENT_HANDLE_H #include #include #include #include #include #include #include #include #define MAXRECVBUF 1024 typedef struct client_param { //用于recv返回出错移除sockfd int epollfd; int sockfd; struct sockaddr_in addr; char buf[ MAXRECVBUF ]; } client_param; void client_param_init( client_param *cparam, int connfd, struct sockaddr_in *addr ); void client_handle( client_param *cparam ); #endif
client_handle.c:
#include "client_handle.h" #include "util.h" void client_param_init( client_param *cparam, int connfd, struct sockaddr_in *addr ) { if ( !cparam ) { PRINTINFO( LEVEL4, "cparam is null!!!" ); return; } cparam->sockfd = connfd; memcpy( &cparam->addr, addr, sizeof(struct sockaddr_in) ); memset( cparam->buf, 0, sizeof(cparam->buf) ); } void client_handle( client_param *cparam ) { int i, ret; while ( 1 ) { memset( cparam->buf, 0, sizeof(cparam->buf) ); ret = recv( cparam->sockfd, cparam->buf, sizeof(cparam->buf), 0 ); if ( ret < 0 ) { if ( errno != EAGAIN && errno != EWOULDBLOCK && errno != EINTR ) { Epoll_del_fd( cparam->epollfd, cparam->sockfd ); } close( cparam->sockfd ); break; } else if ( 0 == ret ) { Epoll_del_fd( cparam->epollfd, cparam->sockfd ); close( cparam->sockfd ); break; } else { if ( ret < 15 ) { close( cparam->sockfd ); break; } //PRINTINFO( LEVEL0, "child receive buf:\n%s", cparam->buf ); fflush(stdout); char *p_get = strstr( cparam->buf, "GET" ); if ( !p_get ) { close( cparam->sockfd ); break; } char *p_http = strstr( cparam->buf, "HTTP" ); if ( !p_http ) { close( cparam->sockfd ); break; } cparam->buf[ret] = '\0'; //GET filename HTTP/1.1 ..... char file_name[20] = {0}; int file_name_len = p_http - p_get - 6; memcpy( file_name, p_get + 5, file_name_len ); if ( access( file_name, F_OK ) == -1 ) { PRINTINFO( LEVEL3, "file:(%s) dosen't exist!!", file_name ); Epoll_del_fd( cparam->epollfd, cparam->sockfd ); close( cparam->sockfd ); break; } PRINTINFO( LEVEL0, "file name:%s--", file_name ); ret = fork(); if ( ret == -1 ) { Epoll_del_fd( cparam->epollfd, cparam->sockfd ); close( cparam->sockfd ); break; } else if ( ret > 0 ) { Epoll_del_fd( cparam->epollfd, cparam->sockfd ); close( cparam->sockfd ); break; } else { close( STDOUT_FILENO ); //relocate the stdou to sockfd PRINTINFO( LEVEL0, "sockfd:%d", cparam->sockfd ); dup( cparam->sockfd ); //printf("likun\n"); execl( file_name, file_name, NULL ); fflush(stdout); Epoll_del_fd( cparam->epollfd, cparam->sockfd ); close( cparam->sockfd ); exit( 0 ); } } } }
cgisrv.c:
#include "myhshappool.h" #include "client_handle.h" #include "util.h" #include "epoll_wrapper.h" //进程池最大数量 #define MAXPROCESSNUMBER 16 //每个进程支持客户连接任务 #define USERPERPROCESS 65535 //epool最大支持的监听事件数 #define MAXEPOLLEVENT 10000 processpool ppool; int main() { int listenfd = Socket_create( "192.168.1.250", 8888 ); init_process_pool( &ppool, MAXPROCESSNUMBER, USERPERPROCESS, MAXEPOLLEVENT, 5, listenfd ); run( &ppool ); close( listenfd ); return 0; }
贴一下makefile:
CC = gcc ROOTDIR = $(shell pwd) OBJ = util.o myhshappool.o epoll_wrapper.o \ client_handle.o cgisrc.o BIN = cgisrv.bin CFLAG = -Wall -O2 -I./ LDFLAG += -c $(BIN):${OBJ} $(CC) $(CFLAG) -o $@ $^ %:%.c $(CC) $(CFLAG) -o $@ $< $(LDFLAG) .PHONY:clean clean: rm $(OBJ) $(BIN) -rf
还有cgi执行程序:
#include #include #include /*.. */ int main( int argc, char **argv ) { time_t tt = time(NULL); printf(""); printf(""); printf(""); printf(""); printf(""); printf(""); printf(""); printf(""); printf("当前服务器时间:%s", ctime(&tt)); printf(""); printf(""); printf(""); return 0; }
测试:代码编译了,即可执行cgisrv.bin,主进程处于监听客户端连接情况,打开浏览器输入: http://xxx.xxx.xxx.xxx:8888/now.cgi,可以看到出现一行加下划线的绿字:当前服务器时间:Sat Sep 17 21:49:07 2016。
ip、端口、进程池数、最大epoll监听事件数等在入口模块(cgisrv)可以改。
模型的简单介绍:主进程创建几个子进程作为工作进程,主进程监听客户端connect事件,一旦有连接事件,即通过round robin(简单轮询)选取一个子进程, 通过父子进程间的通信管道通知子进程有连接事件,子进程epoll监听到管道通信时间,即知道有客户端连接,因此进程accept,并将客户端连接套接字加入epoll监听事件,客户端浏览器发送get请求,客户端监听到请求事件,即调用封装好的客户端事件进行处理,本例子的客户端处理为recv客户的请求,从中提取文件名(now.cgi),并重定向客户端连接套接字到stdout,然后执行对应cgi文件,cgi文件打印html页面字符串,因为重定向的缘故,打印的html字符串发送给客户端,客户端浏览器即显示了html页面。
本例子写完,调试了几个地方,运行几个客户端发送get请求就没有做测试了,因此也不知道高并发是否会跑出bug(epoll模型+进程池处理上百上千的并发应该不是问题,主要是怕我代码有bug),其实应当弄个压力测试来跑一下的,不过也没时间弄了,还要学习半同步半反应堆的模型。
下一篇: js 闭包原理