一个多线程的web服务器程序

多线程的web服务器

多线程版本增加了内部统计功能:
     服务器的运行时间
     接收的客户端请求的数目
     发送回客户端的数据量

这里使用独立线程(Detached Threads)防止僵尸线程(Zombie Threads)


/* webserv_pthread.c - a threaded  web server 
 * building: gcc webserv_pthread.c -lpthread -o webserv_pthread
 */

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sys/stat.h>
#include <string.h>

#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>

#include <dirent.h>
#include <time.h>

#define   HOSTLEN  256
#define   BACKLOG  1

/* server facts here */

time_t  server_started ;
int  server_bytes_sent;
int  server_requests;

main(int ac, char *av[])
{
int  sock, fd;
int *fdptr;
pthread_t worker;
pthread_attr_t attr;

void *handle_call(void *);

if ( ac == 1 ){
fprintf(stderr,"usage: tws portnum/n");
exit(1);
}
sock = make_server_socket( atoi(av[1]) );
if ( sock == -1 ) { perror("making socket"); exit(2); }

setup(&attr);

/* main loop here: take call, handle call in new thread  */

while(1){
fd = accept( sock, NULL, NULL );
server_requests++;

fdptr = malloc(sizeof(int));
*fdptr = fd;
pthread_create(&worker,&attr,handle_call,fdptr);
}
}


/* ------------------------------------------------------ *
   make_server_socket(int portnum)
   创建一个服务器套接字,并调用listen监听
   ------------------------------------------------------ */

int make_server_socket_q(int , int );

int make_server_socket(int portnum)
{
return make_server_socket_q(portnum, BACKLOG);
}
int make_server_socket_q(int portnum, int backlog)
{
struct  sockaddr_in   saddr;   /* build our address here */
struct hostent *hp;   /* this is part of our    */
char hostname[HOSTLEN];     /* address           */
int sock_id;        /* the socket             */

sock_id = socket(PF_INET, SOCK_STREAM, 0);  /* get a socket */
if ( sock_id == -1 ) 
return -1;

/** build address and bind it to socket **/

bzero((void *)&saddr, sizeof(saddr));   /* clear out struct     */
gethostname(hostname, HOSTLEN);         /* where am I ?         */
hp = gethostbyname(hostname);           /* get info about host  */
                                        /* fill in host part    */
bcopy( (void *)hp->h_addr, (void *)&saddr.sin_addr, hp->h_length);
saddr.sin_port = htons(portnum);        /* fill in socket port  */
saddr.sin_family = AF_INET ;            /* fill in addr family  */
if ( bind(sock_id, (struct sockaddr *)&saddr, sizeof(saddr)) != 0 )
       return -1;

/** arrange for incoming calls **/

if ( listen(sock_id, backlog) != 0 ) 
return -1;
return sock_id;
}

/*
 * initialize the status variables and
 * set the thread attribute to detached
 */
setup(pthread_attr_t *attrp)
{
pthread_attr_init(attrp);
pthread_attr_setdetachstate(attrp,PTHREAD_CREATE_DETACHED);

time(&server_started);
server_requests = 0;
server_bytes_sent = 0;
}

void *handle_call(void *fdptr)
{
FILE *fpin;
char request[BUFSIZ];
int fd ;

fd = *(int *)fdptr;
free(fdptr); /* get fd from arg  */

fpin = fdopen(fd, "r"); /* buffer input */
fgets(request,BUFSIZ,fpin); /* read client request */
printf("got a call on %d: request = %s", fd, request);
skip_rest_of_header(fpin);

process_rq(request, fd); /* process client rq */

fclose(fpin);
}

/* ------------------------------------------------------ *
   skip_rest_of_header(FILE *)
   skip over all request info until a CRNL is seen
   ------------------------------------------------------ */
skip_rest_of_header(FILE *fp)
{
char buf[BUFSIZ];
while( fgets(buf,BUFSIZ,fp) != NULL && strcmp(buf,"/r/n") != 0 )
;
}

/* ------------------------------------------------------ *
   process_rq( char *rq, int fd )
   do what the request asks for and write reply to fd 
   handles request in a new process
   rq is HTTP command:  GET /foo/bar.html HTTP/1.0
   ------------------------------------------------------ */
process_rq( char *rq, int fd)
{
char cmd[BUFSIZ], arg[BUFSIZ];

if ( sscanf(rq, "%s%s", cmd, arg) != 2 )
return;
sanitize(arg);
printf("sanitized version is %s/n", arg);

if ( strcmp(cmd,"GET") != 0 )
not_implemented();
else if ( built_in(arg, fd) )
;
else if ( not_exist( arg ) )
do_404(arg, fd);
else if ( isadir( arg ) )
do_ls( arg, fd );
else
do_cat( arg, fd );
}
/*
 * make sure all paths are below the current directory
 */
sanitize(char *str)
{
char *src, *dest;

src = dest = str;

while( *src ){
if( strncmp(src,"/../",4) == 0 )
src += 3;
else if ( strncmp(src,"//",2) == 0 )
src++;
else
*dest++ = *src++;
}
*dest = '/0';
if ( *str == '/' )
strcpy(str,str+1);

if ( str[0]=='/0' || strcmp(str,"./")==0 || strcmp(str,"./..")==0 )
strcpy(str,".");
}

/* handle built-in URLs here.  Only one so far is "status" */
built_in(char *arg, int fd)
{
FILE *fp;

if ( strcmp(arg,"status") != 0 )
return 0;
http_reply(fd, &fp, 200, "OK", "text/plain",NULL);

fprintf(fp,"Server started: %s", ctime(&server_started));
fprintf(fp,"Total requests: %d/n", server_requests);
fprintf(fp,"Bytes sent out: %d/n", server_bytes_sent);
fclose(fp);
return 1;
}

http_reply(int fd, FILE **fpp, int code, char *msg, char *type, char *content)
{
FILE *fp = fdopen(fd, "w");
int bytes = 0;

if ( fp != NULL ){
bytes = fprintf(fp,"HTTP/1.0 %d %s/r/n", code, msg);
bytes += fprintf(fp,"Content-type: %s/r/n/r/n", type);
if ( content )
bytes += fprintf(fp,"%s/r/n", content);
}
fflush(fp);
if ( fpp )
*fpp = fp;
else
fclose(fp);
return bytes;
}

/* ------------------------------------------------------ *
   simple functions first:
        not_implemented(fd)      unimplemented HTTP command
        and do_404(item,fd)     no such object
   ------------------------------------------------------ */
not_implemented(int fd)
{
http_reply(fd,NULL,501,"Not Implemented","text/plain",
"That command is not implemented");
}

do_404(char *item, int fd)
{
http_reply(fd,NULL,404,"Not Found","text/plain",
"The item you seek is not here");
}

/* ------------------------------------------------------ *
   the directory listing section
   isadir() uses stat, not_exist() uses stat
   ------------------------------------------------------ */
isadir(char *f)
{
struct stat info;
return ( stat(f, &info) != -1 && S_ISDIR(info.st_mode) );
}

not_exist(char *f)
{
struct stat info;
return( stat(f,&info) == -1 );
}

do_ls(char *dir, int fd)
{
DIR       *dirptr;
struct dirent *direntp;
FILE       *fp;
int       bytes = 0;

bytes = http_reply(fd,&fp,200,"OK","text/plain",NULL);
bytes += fprintf(fp,"Listing of Directory %s/n", dir);

if ( (dirptr = opendir(dir)) != NULL ){
while( direntp = readdir(dirptr) ){
bytes += fprintf(fp, "%s/n", direntp->d_name);
}
closedir(dirptr);
}
fclose(fp);
server_bytes_sent += bytes;
}

/* ------------------------------------------------------ *
   functions to cat files here.
   file_type(filename) returns the 'extension': cat uses it
   ------------------------------------------------------ */
char * file_type(char *f)
{
char *cp;
if ( (cp = strrchr(f, '.' )) != NULL )
return cp+1;
return "";
}

/* do_cat(filename,fd): sends header then the contents */

do_cat(char *f, int fd)
{
char *extension = file_type(f);
char *type = "text/plain";
FILE *fpsock, *fpfile;
int c;
int bytes = 0;

if ( strcmp(extension,"html") == 0 )
type = "text/html";
else if ( strcmp(extension, "gif") == 0 )
type = "image/gif";
else if ( strcmp(extension, "jpg") == 0 )
type = "image/jpeg";
else if ( strcmp(extension, "jpeg") == 0 )
type = "image/jpeg";

fpsock = fdopen(fd, "w");
fpfile = fopen( f , "r");
if ( fpsock != NULL && fpfile != NULL )
{
bytes = http_reply(fd,&fpsock,200,"OK",type,NULL);
while( (c = getc(fpfile) ) != EOF ){
putc(c, fpsock);
bytes++;
}
fclose(fpfile);
fclose(fpsock);
}
server_bytes_sent += bytes;
}  
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值