c实现web服务器

2014-10-05
客户端(浏览器)与web服务器之间的交互主要包含客户的请求和服务器的应答。
请求和应答的格式在超文本传输协议(HTTP)中有定义。HTTP协议使用纯文本。

例如:
$telnet  www.ouc.edu.cn  80
Trying 211.64.150.68...
Connected to www.ouc.edu.cn(211.64.150.6
Escape character is '^]'.
GET /index.html HTTP/1.0

HTTP/1.1  200  OK
Date: Wed, 21 Jun 2006  08:26:04 GMT
Server: Apache/2.0.54(Unix) DAV/2
.................................. 

原理是telnet创建了一个socket并调用了connect来连接到web服务器。服务器
接受连接请求,并创建了一个基于socket的从客户端的键盘到web服务进程的数据通道。接下来,客户端输入请求:
GET /index.html HTTP/1.0

一个HTTP请求包含有3个字符串。第一个字符串是命令,第二个是参数,第三个是所用协议的版本号。  *注意还有一空行
web服务器读取请求,检查请求,然后返回一个请求。应答有两部分:头部和内容。头部以状态行起始,如下所示:
HTTP/1.1  200  ok
状态行含有两个或更多的字符串。第一个串是协议的版本,第二个串是返回码,这里是200,其文本解释是OK。头部的其余部分是关于应答的附加信息。
应答的其余部分是返回的具体内容。
下面这个webserv.c就是一个web服务器的具体实现。
编译程序,在某个端口运行它:
$gcc webserv.c  -o  webserv
$./webserv  12345

就可以访问web服务器,网址为http://yourhostname:12345/  

将html文件放到该目录中并且用http://yourhostname:12345//filename.html就可以访问。



/* webserv.c - a web server 
 *      build: gcc webserv.c -o webserv
 */
#include        <stdio.h>
#include        <unistd.h>
#include        <sys/types.h>
#include        <sys/socket.h>
#include        <netinet/in.h>
#include        <netdb.h>
#include        <sys/stat.h>
#include        <time.h>
#include        <string.h>


#define   HOSTLEN  256
#define          BACKLOG  1




main(int ac, char *av[])
{
        int         sock, fd;
        FILE        *fpin;
        char        request[BUFSIZ];


        if ( ac == 1 ){
                fprintf(stderr,"usage: ws portnumn");
                exit(1);
        }
        sock = make_server_socket( atoi(av[1]) );
        if ( sock == -1 ) exit(2);


        /* main loop here */


        while(1){
                /* take a call and buffer it */
                fd = accept( sock, NULL, NULL );
                fpin = fdopen(fd, "r" );


                /* read request */
                fgets(request,BUFSIZ,fpin);
                printf("got a call: request = %s", request);
                read_til_crnl(fpin);


                /* do what client asks */
                process_rq(request, fd);


                fclose(fpin);
        }
}


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


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;
}




/* ------------------------------------------------------ *
   read_til_crnl(FILE *)
   skip over all request info until a CRNL is seen
   ------------------------------------------------------ */


read_til_crnl(FILE *fp)
{
        char        buf[BUFSIZ];
        while( fgets(buf,BUFSIZ,fp) != NULL && strcmp(buf,"rn") != 0 )
                ;
}


/* ------------------------------------------------------ *
   process_rq( char *rq, int fd )
   分析客户端的请求,分析然后做出相应的应答
   rq is HTTP command:  GET /foo/bar.html HTTP/1.0
   ------------------------------------------------------ */


process_rq( char *rq, int fd )
{
        char        cmd[BUFSIZ], arg[BUFSIZ];


        /* create a new process and return if not the child */
        if ( fork() != 0 )
                return;


        strcpy(arg, "./");                /* precede args with ./ */
        if ( sscanf(rq, "%s%s", cmd, arg+2) != 2 )
                return;


        if ( strcmp(cmd,"GET") != 0 )
                cannot_do(fd);
        else if ( not_exist( arg ) )
                do_404(arg, fd );
        else if ( isadir( arg ) )
                do_ls( arg, fd );
        else if ( ends_in_cgi( arg ) )
                do_exec( arg, fd );
        else
                do_cat( arg, fd );
}


/* ------------------------------------------------------ *
   the reply header thing: all functions need one
   if content_type is NULL then don't send content type
   ------------------------------------------------------ */


header( FILE *fp, char *content_type )
{
        fprintf(fp, "HTTP/1.0 200 OKrn");
        if ( content_type )
                fprintf(fp, "Content-type: %srn", content_type );
}


/* ------------------------------------------------------ *
   simple functions first:
        cannot_do(fd)       unimplemented HTTP command
    and do_404(item,fd)     no such object
   ------------------------------------------------------ */


cannot_do(int fd)
{
        FILE        *fp = fdopen(fd,"w");


        fprintf(fp, "HTTP/1.0 501 Not Implementedrn");
        fprintf(fp, "Content-type: text/plainrn");
        fprintf(fp, "rn");


        fprintf(fp, "That command is not yet implementedrn");
        fclose(fp);
}


do_404(char *item, int fd)
{
        FILE        *fp = fdopen(fd,"w");


        fprintf(fp, "HTTP/1.0 404 Not Foundrn");
        fprintf(fp, "Content-type: text/plainrn");
        fprintf(fp, "rn");


        fprintf(fp, "The item you requested: %srnis not foundrn", 
                        item);
        fclose(fp);
}


/* ------------------------------------------------------ *
   the directory listing section
   isadir() uses stat, not_exist() uses stat
   do_ls runs ls. It should not
   ------------------------------------------------------ */


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)
{
        FILE        *fp ;


        fp = fdopen(fd,"w");
        header(fp, "text/plain");
        fprintf(fp,"rn");
        fflush(fp);


        dup2(fd,1);
        dup2(fd,2);
        close(fd);
        execlp("ls","ls","-l",dir,NULL);
        perror(dir);
        exit(1);
}


/* ------------------------------------------------------ *
   the cgi stuff.  function to check extension and
   one to run the program.
   ------------------------------------------------------ */


char * file_type(char *f)
/* returns 'extension' of file */
{
        char        *cp;
        if ( (cp = strrchr(f, '.' )) != NULL )
                return cp+1;
        return "";
}


ends_in_cgi(char *f)
{
        return ( strcmp( file_type(f), "cgi" ) == 0 );
}


do_exec( char *prog, int fd )
{
        FILE        *fp ;


        fp = fdopen(fd,"w");
        header(fp, NULL);
        fflush(fp);
        dup2(fd, 1);
        dup2(fd, 2);
        close(fd);
        execl(prog,prog,NULL);
        perror(prog);
}
/* ------------------------------------------------------ *
   do_cat(filename,fd)
   sends back contents after a header
   ------------------------------------------------------ */


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


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


        fpsock = fdopen(fd, "w");
        fpfile = fopen( f , "r");
        if ( fpsock != NULL && fpfile != NULL )
        {
                header( fpsock, content );
                fprintf(fpsock, "rn");
                while( (c = getc(fpfile) ) != EOF )
                        putc(c, fpsock);
                fclose(fpfile);
                fclose(fpsock);
        }
        exit(0);
}

类别:编程相关 | 阅读:1448 | 评论:1 | 标签:c web http

想收藏或者和大家分享这篇好文章→

“c实现web服务器”共有0条留言

发表评论

姓名:

邮箱:

网址:

验证码:

公告

2014年10月4日创建

标签云

最近评论

友情链接