提示:改編自tinyproxy,向原作者致敬!
在程序的開頭,可以定義以下几個常量:
使用者只需要在程序最下面修改handle_connection函數,在裡面實現對客戶請求的處理邏輯即可,信號處理及進程組控制都由框架完成。在RHES32.4kernel和DebianEtch2.6kernel下測試通過。
歡迎指正。
在程序的開頭,可以定義以下几個常量:
#defineMAXSERVICES 128 /*每一個進程最大服務用戶數,防止錯誤積累*/
#defineSTARTSERVERS 32 /*初始啟動服務進程數*/
#defineMAXSPARESERVERS 32 /*最大空閒服務進程數*/
#defineMINSPARESERVERS 8 /*最小空閒服務進程數*/
使用者只需要在程序最下面修改handle_connection函數,在裡面實現對客戶請求的處理邏輯即可,信號處理及進程組控制都由框架完成。在RHES32.4kernel和DebianEtch2.6kernel下測試通過。
歡迎指正。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<assert.h>
#include<unistd.h>
#include<errno.h>
#include<signal.h>
#include<sys/types.h>
#include<sys/mman.h>
#include<sys/file.h>
#include<sys/wait.h>
#include<sys/socket.h>
#include<arpa/inet.h>
/*
*福瑞哈哥改編自tinyproxy
*/
#defineMAXLISTEN 1024 /*最大監聽數,listen函數用*/
#defineMAXCLIENTS 64 /*最大服務進程數*/
#defineMAXSERVICES 128 /*每一個進程最大服務用戶數,定期更新進程組,防止錯誤積累*/
#defineSTARTSERVERS 32 /*初始啟動服務進程數*/
#defineMAXSPARESERVERS 32 /*最大空閒服務進程數*/
#defineMINSPARESERVERS 8 /*最小空閒服務進程數*/
#definePORT 8000 /*監聽端口號*/
/*全局變量區*/
intlistenfd; /*服務socket*/
intreceived_sighup=0; /*收到hup信號標誌*/
intquit=0; /*退出標誌*/
#defineSERVER_COUNT_LOCK()_child_lock_wait()
#defineSERVER_COUNT_UNLOCK()_child_lock_release()
/*共享變量鎖*/
staticstructflocklock_it,unlock_it;
staticintlock_fd=-1;
enumchild_status_t{T_EMPTY,T_WAITING,T_CONNECTED};
structchild_s{
pid_ttid;
unsignedintconnects;
enumchild_status_tstatus;
};
/*
*子進程數組-位於共享內存區
*/
staticstructchild_s*child_ptr;
/*
*正等待用戶連接的服務進程數
*/
staticunsignedint*servers_waiting;
/*
*分配一塊共享內存。
*/
staticvoid*
malloc_shared_memory(size_tsize)
{
intfd;
void*ptr;
charbuffer[32];
staticchar*shared_file="/tmp/mps.shared.XXXXXX";
assert(size>0);
strncpy(buffer,shared_file,sizeof(buffer));
if((fd=mkstemp(buffer))==-1)
return(void*)MAP_FAILED;
unlink(buffer);
if(ftruncate(fd,size)==-1)
return(void*)MAP_FAILED;
ptr=mmap(NULL,size,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
returnptr;
}
/*
*分配一塊共享內存,並清0。
*/
staticvoid*
calloc_shared_memory(size_tnmemb,size_tsize)
{
void*ptr;
longlength;
assert(nmemb>0);
assert(size>0);
length=nmemb*size;
ptr=malloc_shared_memory(length);
if(ptr==MAP_FAILED)
returnptr;
memset(ptr,0,length);
returnptr;
}
staticvoid
_child_lock_init(void)
{
charlock_file[]="/tmp/mps.servers.lock.XXXXXX";
lock_fd=mkstemp(lock_file);
unlink(lock_file);
lock_it.l_type=F_WRLCK;
lock_it.l_whence=SEEK_SET;
lock_it.l_start=0;
lock_it.l_len=0;
unlock_it.l_type=F_UNLCK;
unlock_it.l_whence=SEEK_SET;
unlock_it.l_start=0;
unlock_it.l_len=0;
}
staticvoid
_child_lock_wait(void)
{
intrc;
while((rc=fcntl(lock_fd,F_SETLKW,&lock_it))<0){
if(errno==EINTR)
continue;
else
return;
}
}
staticvoid
_child_lock_release(void)
{
if(fcntl(lock_fd,F_SETLKW,&unlock_it)<0)
return;
}
#defineSERVER_INC()do{/
SERVER_COUNT_LOCK();/
++(*servers_waiting);/
SERVER_COUNT_UNLOCK();/
}while(0)
#defineSERVER_DEC()do{/
SERVER_COUNT_LOCK();/
assert(*servers_waiting>0);/
--(*servers_waiting);/
SERVER_COUNT_UNLOCK();/
}while(0)
/*
*創建監聽socket
*/
staticvoid
start_listen_socket(unsignedshortport)
{
intsockfd;
inton=1;
structsockaddr_inaddr;
sockfd=socket(AF_INET,SOCK_STREAM,0);
setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
memset(&addr,0,sizeof(addr));
addr.sin_family=AF_INET;
addr.sin_port=htons(port);
/*
*需要指定到實際的地址上
*/
addr.sin_addr.s_addr=inet_addr("0.0.0.0");
if(bind(sockfd,(structsockaddr*)&addr,sizeof(addr))<0){
fprintf(stderr,"Unabletobindlisteningsocketbecauseof%s/n",
strerror(errno));
exit(-1);
}
if(listen(sockfd,MAXLISTEN)<0){
fprintf(stderr,"Unabletostartlisteningsocketbecauseof%s/n",
strerror(errno));
exit(-1);
}
listenfd=sockfd;
}
void
close_listen_socket(void)
{
close(listenfd);
}
/*
*在這個函數中,寫下對客戶請求的處理邏輯。
*處理完成後在退出這個函數時,關閉connfd。
*/
void
handle_connection(intconnfd);
/*
*子進程主循環
*/
staticvoid
child_main(structchild_s*ptr)
{
intconnfd;
structsockaddr*cliaddr;
socklen_tclilen;
clilen=sizeof(structsockaddr);
cliaddr=(structsockaddr*)malloc(clilen);
if(!cliaddr){
fprintf(stderr,
"Couldnotallocatememoryforchildaddress.");
exit(0);
}
ptr->connects=0;
while(!quit){
ptr->status=T_WAITING;
connfd=accept(listenfd,cliaddr,&clilen);
/*
*保證沒有錯誤發生
*/
if(connfd<0){
fprintf(stderr,
"Acceptreturnedanerror(%s)...retrying.",
strerror(errno));
continue;
}
ptr->status=T_CONNECTED;
SERVER_DEC();
handle_connection(connfd);
ptr->connects++;
if(ptr->connects==MAXSERVICES){
fprintf(stderr,
"ChildhasreachedMaxRequestsPerChild(%u).Killingchild./n",
ptr->connects);
break;
}
SERVER_COUNT_LOCK();
if(*servers_waiting>MAXSPARESERVERS){
/*
*有太多空閒服務進程,退出自己
*/
fprintf(stderr,
"Waitingservers(%d)exceedsMaxSpareServers(%d).Killingchild./n",
*servers_waiting,MAXSPARESERVERS);
SERVER_COUNT_UNLOCK();
break;
}else{
SERVER_COUNT_UNLOCK();
}
SERVER_INC();
}
ptr->status=T_EMPTY;
free(cliaddr);
exit(0);
}
/*
*Fork一個子進程並啟動child_main()函數(子進程主循環)
*/
staticint
child_make(structchild_s*ptr)
{
pid_tpid;
if((pid=fork())!=0)
returnpid; /*父進程*/
/*
*重設子進程的信號處理函數
*/
signal(SIGCHLD,SIG_DFL);
signal(SIGTERM,SIG_DFL);
signal(SIGHUP,SIG_DFL);
child_main(ptr);/*不會返回*/
return-1;
}
int
child_pool_create(void)
{
/*sleep(10);*/
child_ptr=(structchild_s*)
calloc_shared_memory(MAXCLIENTS,
sizeof(structchild_s));
if(child_ptr==MAP_FAILED){
fprintf(stderr,"Couldnotallocatesharedmemoryforchildren./n");
return-1;
}
servers_waiting=(unsignedint*)
malloc_shared_memory(sizeof(unsignedint));
if(servers_waiting==MAP_FAILED){
fprintf(stderr,"Couldnotallocatesharedmemoryforchildcounting./n");
return-1;
}
*servers_waiting=0;
/*在操作servers_waiting變量之前,創建加鎖文件*/
_child_lock_init();
inti;
/*初始化子進程共享數組*/
for(i=0;i<MAXCLIENTS;i++){
child_ptr.status=T_EMPTY;
child_ptr.connects=0;
}
/*fork子進程*/
for(i=0;i<STARTSERVERS;i++){
child_ptr.status=T_WAITING;
child_ptr.tid=child_make(&child_ptr);
if(child_ptr.tid<0){
fprintf(stderr,
"Couldnotcreatechildnumber%dof%d/n",
i,STARTSERVERS);
return-1;
}else{
fprintf(stderr,
"Creatingchildnumber%dof%d.../n",
i+1,STARTSERVERS);
SERVER_INC();
}
}
return0;
}
/*
*刪除所有服務進程
*/
void
kill_children(void)
{
unsignedinti;
for(i=0;i<MAXCLIENTS;i++){
if(child_ptr.status!=T_EMPTY)
kill(child_ptr.tid,SIGTERM);
}
}
/*
*監控進程主循環,它負責把服務進程的數量維持在一個合適的數量上。
*/
void
child_main_loop(void)
{
unsignedinti;
while(1){
if(quit)
return;
/*如果空閒服務進程數不足,則創建一些*/
SERVER_COUNT_LOCK();
if(*servers_waiting<MINSPARESERVERS){
fprintf(stderr,
"Waitingservers(%d)islessthanMinSpareServers(%d).Creatingnewchild.",
*servers_waiting,MINSPARESERVERS);
SERVER_COUNT_UNLOCK();
for(i=0;i<MAXCLIENTS;i++){
if(child_ptr.status==T_EMPTY){
child_ptr.status=T_WAITING;
child_ptr.tid=child_make(&child_ptr);
if(child_ptr.tid<0){
fprintf(stderr,"Couldnotcreatechild");
child_ptr.status=T_EMPTY;
break;
}
SERVER_INC();
break;
}
}
}else{
SERVER_COUNT_UNLOCK();
}
sleep(5);
if(received_sighup){
/*在收到hup信號後,可作一些維護工作,比如更新備份日志文件等*/
received_sighup=0;
}
}
}
/*
*處理信號
*/
void
takesig(intsig)
{
pid_tpid;
intstatus;
switch(sig){
caseSIGHUP:
received_sighup=1;
break;
caseSIGTERM:
quit=1;
break;
caseSIGCHLD:
while((pid=waitpid(-1,&status,WNOHANG))>0)
;
break;
}
return;
}
int
main(intargc,char**argv)
{
intgodaemon=1; /*是否轉為deamon模式*/
unsignedshortport=PORT; /*服務端口號*/
assert(MINSPARESERVERS<=MAXSPARESERVERS);
assert(STARTSERVERS<=MAXCLIENTS);
assert(MAXCLIENTS<=MAXLISTEN);
if(godaemon==1){
daemon(1,1);
}
/*啟動服務socket*/
start_listen_socket(port);
signal(SIGPIPE,SIG_IGN);
/*創建服務進程組*/
child_pool_create();
/*
*下面這些信號處理函數只有監控進程才有用
*/
signal(SIGCHLD,takesig);
signal(SIGTERM,takesig);
signal(SIGHUP,takesig);
/*開始監控工作*/
child_main_loop();
/*退出前,殺掉服務進程組*/
kill_children();
/*關閉服務socket*/
close_listen_socket();
exit(0);
return0;
}
/*
*在這個函數中,寫下對客戶請求的處理邏輯。
*處理完成後在退出這個函數時,關閉connfd。
*這只是一個示例!
*/
void
handle_connection(intconnfd)
{
charbuf[128]={};
sprintf(buf,"%u:",(unsignedint)getpid());
intlen=strlen(buf);
read(connfd,buf+len,sizeof(buf)-len-1);
len=strlen(buf);
write(connfd,buf,len);
close(connfd);
}
本文介绍了一个基于tinyproxy改編的多进程Web服务器框架。该框架通过定义一系列常量来控制服务进程的数量,并实现了信号处理及进桯组控制。用户只需实现handle_connection函数来处理客户端请求。
1771

被折叠的 条评论
为什么被折叠?



