linux C学习之实现简单的web服务器

来源:互联网 发布:内存卡数据恢复软件 编辑:程序博客网 时间:2024/05/22 01:33

实现简单的web服务器

        实现一个简单的Web服务器myhttpd,当浏览器向服务器请求文件时,服务器就从服务目录(例如/var/www)中找出相应的文件,加上HTTP协议头一起发给浏览器。服务器不发送完整的HTTP协议头。

       一、基本HTTP协议

          打开浏览器,输入服务器IP,例如 http://127.0.1.1 ,如果端口号不是80,例如是8000,则输入http://127.0.1.1:8000 。这时浏览器向服务器发送的HTTP协议头如下:

       GET /favicon.ico HTTP/1.1
       Host: 127.0.1.1:8000
       User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:54.0) Gecko/20100101 Firefox/54.0
       Accept: */*
       Accept-Language: en-US,en;q=0.5
       Accept-Encoding: gzip, deflate
       Connection: keep-alive

         注意,其中每一行的末尾都是回车加换行(C语言的"\r\n"),第一行是GET请求和协议版本,其余几行选项字段我们不讨论,HTTP协议头的最后有一个空行,也是回车加换行。我们实现的Web服务器只要能正确解析第一行就行了,这是一个GET请求,请求的是服务目录
的根目录,Web服务器应该把该目录下的索引页(默认是index.html)发给浏览器,也就是把/var/www/index.html发给浏览器。假如该文件的内容如下(HTML文件没必要以"\r\n"换行,以"\n"换行就可以了):

        <html>
        <head><title>Test Page</title></head>
        <body>
        <p>Test OK</p>
        <img src='mypic.jpg'>
        </body>
        </html>

        显示一行字和一幅图片,图片的相对路径(相对当前的index.html文件的路径)是mypic.jpg,也就是/var/www/mypic.jpg,如果用绝对路径表示应该是:

        <img src='/mypic.jpg'>

        服务器应按如下格式应答浏览器:

        HTTP/1.1 200 OK
        Content-Type: text/html

        <html>
        <head><title>Test Page</title></head>
        <body>
        <p>Test OK</p>
        <img src='/mypic.jpg'>
        </body>
        </html>

        服务器应答的HTTP头也是每行末尾以回车加换行结束,最后跟一个空行的回车加换行。

        HTTP头的第一行是协议版本和应答码,200表示成功,后面的消息OK其实可以随意写,浏览器是不关心的,主要是为了调试时给开发人员看的。HTTP头的第二行表示即将发送的文件的类型(称为MIME类型),这里是text/html,纯文本文件是text/plain,图片则是image/jpg、image/png等,这个简单的服务器则只发送jpg图片。然后就发送文件的内容,发送完毕之后主动关闭连接,这样浏览器就知道文件发送完了。这一点比较特殊:通常网络通信都是客户端主动发起连接,主动发起请求,主动关闭连接,服务器只是被动地处理各种情况,而HTTP协议规定服务器主动关闭连接.

        浏览器收到index.html之后,发现其中有一个图片文件,就会再发一个GET请求。服务器收到这个请求应该把图片发过去然后关闭连接。

        总结一下服务器的处理步骤:

         1、  解析浏览器的请求,在服务目录中查找相应的文件,如果找不到该文件就返回404错误页面 。

         2、  发送HTTP/1.1 200 OK给客户端
         3、  如果是一个图片文件,根据图片的扩展名发送相应的Content-Type给客户端
         4、  如果不是图片文件,这里我们简化处理,都当作Content-Type: text/html
         5、  简单的HTTP协议头有这两行就足够了,再发一个空行表示结束
         6、  读取文件的内容发送到客户端
         7、  关闭连接

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <fcntl.h>

#define RECV_PORT    8000
#define QLEN        10
#define MAX_SIZE    1024

char browser_com_buf[256];
    
int mylisten (void)
{
    int listenfd;
    int err;
    socklen_t addrlen;
    struct sockaddr_in server_addr;
    char *address = "192.168.0.3";
    
    listenfd = socket ( AF_INET, SOCK_STREAM, 0 );   /*creat a new socket*/
    if ( listenfd < 0 )
    {
        printf ( "listen error\n" );
        return -1;
    }
    memset ( &server_addr, 0, sizeof ( server_addr ) );    
    server_addr.sin_family = AF_INET;    /*IPV4*/
    server_addr.sin_port = htons ( RECV_PORT );
    server_addr.sin_addr.s_addr = htonl ( INADDR_ANY );
    
    addrlen = sizeof ( server_addr );
    err = bind ( listenfd, ( struct sockaddr * )&server_addr, addrlen );
    if ( err < 0 )
    {
        printf ( "bind error\n" );
        return -2;
    }
    err = listen ( listenfd, QLEN );
    if ( err < 0 )
    {
        printf ( "listen error" );
        return -3;
    }
    return listenfd;
}

int myaccept ( int listenfd )
{
    int clientfd;
    int err;
    socklen_t addrlen;
    struct sockaddr_in client_addr;
    
    addrlen = sizeof ( client_addr );
    clientfd = accept ( listenfd, ( struct sockaddr * )&client_addr, &addrlen );
    if ( clientfd < 0 )
    {
        printf ( "accept error\n" );
        return -1;
    }
    return clientfd;
}

void browser_com_analysis ( char *com, char *buf )
{
    int i = 0;
    unsigned int flag = 0;
    char *locate;
    
    locate = strchr ( com, '/' );
    if ( locate == NULL )
    {
        printf ( "not find\n" );
        exit ( 1 );
    }
    else
    {
        while ( *locate != ' ' )
        {
            buf[i++] = *locate;
            locate++;
        }
        buf[i] = '\0';
    }
    //printf ( "%s\n", buf );
}

int state_estimat ( char *buf )
{
    int len;
    unsigned int flag = 0;
    int i = 0, j = 0;
    char buf1[256];
    char *image_style = ".jpg";
    char file_name[256] = { "/var/www" };
    struct stat statbuf;
    
    len = strlen ( buf );
    memset ( buf1, '\0', sizeof ( buf1 ) );
    while ( i < len )
    {
        if ( buf[i] == '.' )
        {
            flag = 1;
        }
        if ( flag == 1 )
        {
            if ( buf[i] == ' ' || buf[i] == '\n' )
            {
                break;
            }
            else
            {
                buf1[j++] = buf[i];
            }
        }
        i++;
    }
    //printf ( "%s%d\n", buf1, len );
    if ( len == 0 )
    {
        printf ( "http have not send comand\n" );
        exit ( 1 );
    }
    else if ( len == 1 )
    {
        return 1;
    }
    else if ( len > 1 )
    {
        strcat ( file_name, buf );
        stat ( file_name, &statbuf );
        if ( statbuf.st_mode == S_IXOTH )   //其他用户具有可执行权限
        {
            //如果具有可执行权限,则执行该文件,将结果发送到浏览器上
            return 4;
        }
        else
        {
            //不具有可执行权限则判断图片还是文件
            if ( strcmp ( buf1, image_style ) == 0 )
            {
                return 2;    //浏览器请求的是图片
            }
            return 3;    //浏览器请求的是文件
        }
    }
}

int find_file ( char *buf, int clientfd )
{
    int state;
    char my_file_buf[65536 * 2];
    int n;
    char fault[] = { "HTTP/1.1 404 Not Found\nContent-Type: text/html\n<html><body>request file not found</body></html>\n\n" };
    char head[] = { "HTTP/1.1 200 OK\nContent-Type: text/html\n\n" };
    char head2[] = { "HTTP/1.1 200 OK\nContent-Type: image/jpg\n\n" };
    char head3[] = { "HTTP/1.1 200 OK\n\n" };
    char file_name[] = { "/var/www" };
    
    memset ( &my_file_buf, '\0', MAX_SIZE );
    state = state_estimat ( buf );
    if ( state == 1 )  //server send file to browser
    {
        int filefd;
        
        filefd = open ( "/var/www/index.html", O_RDWR );
        if ( filefd < 0 )
        {
            printf ( "find file failed\n" );
            write ( clientfd, fault, strlen ( fault ) );
            close ( clientfd );
        }
        else
        {
            n = read ( filefd, my_file_buf, MAX_SIZE );
            if ( n < 0 )
            {
                printf ( "read the root file failed\n" );
                exit ( 1 );
            }
            strcat ( head, my_file_buf );
            printf ( "%s", head );
            write ( clientfd, head, strlen ( head ) );
            close ( clientfd );  
        }
    }
    else if ( state == 2 )   
    {
        printf ( "picture\n" );
        int picture_fd;    
        int n;
        
        write ( clientfd, head2, strlen ( head2 ) );
        memset ( &my_file_buf, '\0', sizeof ( my_file_buf ) );
        strcat ( file_name, buf );
        picture_fd = open ( file_name, O_RDONLY );   //只读打开图片文件
        if ( picture_fd  < 0 )
        {
            printf ( "open picutre failed\n" );
            exit ( 1 );
        }
        n = read ( picture_fd, my_file_buf, sizeof ( my_file_buf ) );
        if ( n < 0 )
        {
            printf ( "read picutre data failed\n" );
            exit ( 1 );
        }
        write ( clientfd, my_file_buf, sizeof ( my_file_buf ) );
        close ( clientfd );
    }
    else if ( state == 3 )
    {
        printf ( "file\n" );
        int file_fd;
        int n;
        
        write ( clientfd, head, strlen ( head ) );
        memset ( &my_file_buf, '\0', sizeof ( my_file_buf ) );
        strcat ( file_name, buf );
        
        file_fd = open ( file_name, O_RDONLY );
        if ( file_fd < 0 )
        {
            printf ( "open picutre failed\n" );
            exit ( 1 );
        }
        n = read ( file_fd, my_file_buf, sizeof ( my_file_buf ) );
        if ( n < 0 )
        {
            printf ( "read file failed \n" );
            exit ( 1 );
        }
        write ( clientfd, my_file_buf, sizeof ( my_file_buf ) );
    }
    else if ( state == 4 )
    {
        printf ( "executable file\n" );
        pid_t pid;
    
        pid = fork ();
        if ( pid < 0 )
        {
            printf ( "creat child failed\n" );
            exit ( 1 );
        }
        else if ( pid > 0 )    //parent
        {
            int stateval;
            waitpid ( pid, &stateval, 0 );
            close ( clientfd );
        }
        else    //child
        {
            int err;
            char *argv[1];
            
            strcat ( file_name, buf );
            argv[1] = file_name;
            dup2 ( STDOUT_FILENO, clientfd );
            err = execv ( "/bin/bash", argv );
            if ( err < 0 )
            {
                printf ( "execut file failed\n" );
                exit ( 1 );
            }
            close ( clientfd );
        }
    }
}

int main ( int argc, char *argv[] )
{
    int listenfd, clientfd;
    int filefd, n;
    char recvbuf[MAX_SIZE];
    char data_buf[MAX_SIZE];
    
    listenfd = mylisten();
    if ( listenfd < 0 )
    {
        exit ( 0 );
    }
    
    printf ( "listening and listen fd is %d......\n", listenfd );
    while ( 1 )
    {
        clientfd = myaccept ( listenfd );
        printf ( "accept success......\n" );
        if ( clientfd < 0 )
        {
            exit ( 1 );
        }
        memset ( &recvbuf, '\0', MAX_SIZE );
        memset ( &data_buf, '\0', MAX_SIZE );
        n = read ( clientfd, recvbuf, MAX_SIZE );
        printf ( "read OK\n" );
        if ( n < 0 )
        {
            printf ( "read error\n" );
            exit ( 2 );
        }
        
        filefd = open ( "/tmp/read_html.txt", O_RDWR | O_CREAT | O_TRUNC );   //open o new file
        write ( filefd, recvbuf, n );  //save the http information
        browser_com_analysis ( recvbuf, data_buf );
        find_file ( data_buf, clientfd );
    }
    close ( clientfd );    
    
    return 0;
}


       

 

原创粉丝点击