提示:改編自tinyproxy,向原作者致敬!
在程序的開頭,可以定義以下几個常量:
CODE:
#defineMAXSERVICES
128
#define STARTSERVERS
32
#define MAXSPARESERVERS
32
#define MINSPARESERVERS
8
#define STARTSERVERS
#define MAXSPARESERVERS
#define MINSPARESERVERS
使用者只需要在程序最下面修改handle_connection函數,在裡面實現對客戶請求的處理邏輯即可,信號處理及進程組控制都由框架完成。在RHES3 2.4kernel和Debian Etch 2.6kernel下測試通過。
歡迎指正。
CODE:
#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>
#define MAXLISTEN
1024
#define MAXCLIENTS
64
#define MAXSERVICES
128
#define STARTSERVERS
32
#define MAXSPARESERVERS
32
#define MINSPARESERVERS
8
#define PORT
8000
int listenfd;
int received_sighup = 0;
int quit = 0;
#define SERVER_COUNT_LOCK()
_child_lock_wait()
#define SERVER_COUNT_UNLOCK() _child_lock_release()
static struct flock lock_it, unlock_it;
static int lock_fd = -1;
enum child_status_t { T_EMPTY, T_WAITING, T_CONNECTED };
struct child_s {
pid_t tid;
unsigned int connects;
enum child_status_t status;
};
static struct child_s *child_ptr;
static unsigned int* servers_waiting;
static void*
malloc_shared_memory( size_t size )
{
int fd;
void* ptr;
char buffer[32];
static char* 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 );
return ptr;
}
static void*
calloc_shared_memory( size_t nmemb, size_t size )
{
void* ptr;
long length;
assert( nmemb > 0 );
assert( size > 0 );
length = nmemb * size;
ptr = malloc_shared_memory( length );
if ( ptr == MAP_FAILED )
return ptr;
memset( ptr, 0, length );
return ptr;
}
static void
_child_lock_init(void)
{
char lock_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;
}
static void
_child_lock_wait(void)
{
int rc;
while ( (rc = fcntl( lock_fd, F_SETLKW,&lock_it )) < 0 ) {
if (errno == EINTR)
continue;
else
return;
}
}
static void
_child_lock_release(void)
{
if (fcntl(lock_fd, F_SETLKW,&unlock_it) < 0)
return;
}
#define SERVER_INC() do { \
SERVER_COUNT_LOCK(); \
++(*servers_waiting); \
SERVER_COUNT_UNLOCK();\
} while (0)
#define SERVER_DEC() do { \
SERVER_COUNT_LOCK(); \
assert(*servers_waiting> 0); \
--(*servers_waiting); \
SERVER_COUNT_UNLOCK();\
} while (0)
static void
start_listen_socket( unsigned short port )
{
int sockfd;
int on = 1;
struct sockaddr_in addr;
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, (struct sockaddr *)&addr, sizeof (addr) ) < 0 ) {
fprintf( stderr, "Unable tobind listening socket because of %s\n",
strerror(errno));
exit(-1);
}
if ( listen( sockfd, MAXLISTEN )< 0 ) {
fprintf( stderr, "Unable tostart listening socket because of %s\n",
strerror(errno) );
exit(-1);
}
listenfd = sockfd;
}
void
close_listen_socket(void)
{
close( listenfd );
}
void
handle_connection( int connfd );
static void
child_main( struct child_s* ptr )
{
int connfd;
struct sockaddr *cliaddr;
socklen_t clilen;
clilen = sizeof( struct sockaddr );
cliaddr = (struct sockaddr*) malloc( clilen);
if ( !cliaddr ) {
fprintf( stderr,
"Could notallocate memory for child address." );
exit(0);
}
ptr->connects = 0;
while ( !quit ) {
ptr->status =T_WAITING;
connfd = accept( listenfd,cliaddr, &clilen );
if ( connfd <0 ) {
fprintf(stderr,
"Acceptreturned an error (%s) ... retrying.",
strerror(errno));
continue;
}
ptr->status =T_CONNECTED;
SERVER_DEC();
handle_connection( connfd);
ptr->connects++;
if (ptr->connects == MAXSERVICES ) {
fprintf(stderr,
"Child hasreached MaxRequestsPerChild (%u). Killing child.\n",
ptr->connects);
break;
}
SERVER_COUNT_LOCK();
if ( *servers_waiting> MAXSPARESERVERS ) {
fprintf(stderr,
"Waitingservers (%d) exceeds MaxSpareServers (%d). Killing child.\n",
*servers_waiting,MAXSPARESERVERS );
SERVER_COUNT_UNLOCK();
break;
} else {
SERVER_COUNT_UNLOCK();
}
SERVER_INC();
}
ptr->status = T_EMPTY;
free( cliaddr );
exit(0);
}
static int
child_make( struct child_s* ptr )
{
pid_t pid;
if ((pid = fork()) != 0)
return pid;
signal( SIGCHLD, SIG_DFL );
signal( SIGTERM, SIG_DFL );
signal( SIGHUP, SIG_DFL );
child_main(ptr);
return -1;
}
int
child_pool_create(void)
{
child_ptr = (struct child_s*)
calloc_shared_memory(MAXCLIENTS,
sizeof(struct child_s) );
if ( child_ptr == MAP_FAILED ) {
fprintf( stderr, "Could notallocate shared memory for children.\n" );
return -1;
}
servers_waiting = (unsigned int*)
malloc_shared_memory(sizeof(unsigned int) );
if ( servers_waiting == MAP_FAILED ) {
fprintf( stderr, "Could notallocate shared memory for child counting.\n" );
return -1;
}
*servers_waiting = 0;
_child_lock_init();
int i;
for ( i = 0; i < MAXCLIENTS; i++) {
child_ptr[i].status =T_EMPTY;
child_ptr[i].connects =0;
}
for ( i = 0; i < STARTSERVERS;i++ ) {
child_ptr[i].status =T_WAITING;
child_ptr[i].tid =child_make( &child_ptr[i] );
if ( child_ptr[i].tid< 0 ) {
fprintf(stderr,
"Could notcreate child number %d of %d\n",
i,STARTSERVERS );
return-1;
} else {
fprintf(stderr,
"Creatingchild number %d of %d ...\n",
i + 1,STARTSERVERS );
SERVER_INC();
}
}
return 0;
}
void
kill_children(void)
{
unsigned int i;
for ( i = 0; i < MAXCLIENTS; i++) {
if ( child_ptr[i].status !=T_EMPTY )
kill(child_ptr[i].tid, SIGTERM );
}
}
void
child_main_loop(void)
{
unsigned int i;
while (1) {
if ( quit )
return;
SERVER_COUNT_LOCK();
if ( *servers_waiting< MINSPARESERVERS ) {
fprintf(stderr,
"Waitingservers (%d) is less than MinSpareServers (%d). Creating newchild.",
*servers_waiting,MINSPARESERVERS );
SERVER_COUNT_UNLOCK();
for ( i =0; i < MAXCLIENTS; i++ ) {
if ( child_ptr[i].status == T_EMPTY ) {
child_ptr[i].status =T_WAITING;
child_ptr[i].tid =child_make( &child_ptr[i] );
if ( child_ptr[i].tid< 0 ) {
fprintf(stderr, "Could not create child" );
child_ptr[i].status = T_EMPTY;
break;
}
SERVER_INC();
break;
}
}
} else {
SERVER_COUNT_UNLOCK();
}
sleep(5);
if ( received_sighup ){
received_sighup = 0;
}
}
}
void
takesig( int sig )
{
pid_t pid;
int status;
switch (sig) {
case SIGHUP:
received_sighup = 1;
break;
case SIGTERM:
quit = 1;
break;
case SIGCHLD:
while ( (pid = waitpid(-1,&status, WNOHANG)) > 0 )
;
break;
}
return;
}
int
main( int argc, char ** argv )
{
int godaemon = 1;
unsigned short port = PORT;
assert( MINSPARESERVERS <=MAXSPARESERVERS );
assert( STARTSERVERS <=MAXCLIENTS );
assert( MAXCLIENTS <= MAXLISTEN);
if ( godaemon == 1 ) {
daemon(1, 1);
}
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();
close_listen_socket();
exit(0);
return 0;
}
void
handle_connection( int connfd )
{
char buf[128] = {};
sprintf( buf, "%u: ", (unsigned int) getpid());
int len = strlen(buf);
read( connfd, buf + len, sizeof(buf) - len - 1);
len = strlen(buf);
write( connfd, buf, len );
close( connfd );
}
#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>
#define MAXLISTEN
#define MAXCLIENTS
#define MAXSERVICES
#define STARTSERVERS
#define MAXSPARESERVERS
#define MINSPARESERVERS
#define PORT
int listenfd;
int received_sighup = 0;
int quit = 0;
#define SERVER_COUNT_LOCK()
#define SERVER_COUNT_UNLOCK() _child_lock_release()
static struct flock lock_it, unlock_it;
static int lock_fd = -1;
enum child_status_t { T_EMPTY, T_WAITING, T_CONNECTED };
struct child_s {
};
static struct child_s *child_ptr;
static unsigned int* servers_waiting;
static void*
malloc_shared_memory( size_t size )
{
}
static void*
calloc_shared_memory( size_t nmemb, size_t size )
{
}
static void
_child_lock_init(void)
{
}
static void
_child_lock_wait(void)
{
}
static void
_child_lock_release(void)
{
}
#define SERVER_INC() do { \
} while (0)
#define SERVER_DEC() do { \
} while (0)
static void
start_listen_socket( unsigned short port )
{
}
void
close_listen_socket(void)
{
}
void
handle_connection( int connfd );
static void
child_main( struct child_s* ptr )
{
}
static int
child_make( struct child_s* ptr )
{
}
int
child_pool_create(void)
{
}
void
kill_children(void)
{
}
void
child_main_loop(void)
{
}
void
takesig( int sig )
{
}
int
main( int argc, char ** argv )
{
}
void
handle_connection( int connfd )
{
}