《unix/linux编程实践教程》学习笔记:第12章 编写web服务器

18 篇文章 0 订阅

本章编写的web服务器本质上只是一个集成了cat和ls的unix shell。
学习要点:
(1)HTTP协议
浏览器与web服务器之间的交互主要包含客户的请求和服务器的应答。请求和应答的格式在超文本传输协议(HTTP)中有定义。
1.HTTP请求:GET

GET /index.html HTTP/1.0

因此我们需要知道如何识别命令和根据参数进行处理。

process_rq(char *rq,int fd)
{
    char cmd[BUFSIZ],arg[BUFSIZ];
    if(fork() != 0)   //建立新进程
            return;
    strcpy(arg,"./");

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

2.HTTP应答:OK
服务器读取请求,检查请求,然后返回一个请求,应答分为头部和内容。
头部

header(FILE *fp,char * content_type)
{
    fprintf(fp,"HTTP/1.0 200 OK\r\n");
    if(content_type)
            fprintf(fp,"Content-type:%s\r\n",content_type);

}

(2)设计web服务器
1.建立服务器
使用socklib.c中的make_server_socket
2.接收请求
使用accept来得到指向客户端的文件描述符。可以使用fdopen使得该文件描述符转换为缓冲流
3.读取请求
上面已经说明
4.处理请求
如何列出目录信息,cat文件以及运行程序
5.发送应答
已说明

代码实现:

编译命令

gcc socklib.c webserv.c -o webserv

socklib.c

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

#define HOSTLEN 256
#define BACKLOG 1

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;
    struct hostent *hp;
    char hostname[HOSTLEN];
    int sock_id;
    //向内核申请一个socket
    sock_id = socket(PF_INET,SOCK_STREAM,0);
    if(sock_id == -1)
            return -1;
    //绑定地址到socket,地址包括主机名和端口
    bzero((void *)&saddr,sizeof(saddr));
    gethostname(hostname,HOSTLEN);
    hp = gethostbyname(hostname);

    bcopy((void *)hp->h_addr,(void *)&saddr.sin_addr,hp->h_length);
    saddr.sin_port = htons(portnum);
    saddr.sin_family = AF_INET;
    if(bind(sock_id,(struct sockaddr *)&saddr,sizeof(saddr)) != 0)
            return -1;
    //监听socket上的连接
    if(listen(sock_id,backlog) != 0)
            return -1;
    return sock_id;

}
//未用到
int connect_to_server(char *host,int portnum)
{
    int sock;
    struct sockaddr_in servadd;
    struct hostent *hp;

    sock = socket(AF_INET,SOCK_STREAM,0);
    if(sock == -1)
            return -1;

    bzero(&servadd,sizeof(servadd));
    hp = gethostbyname(host);
    if(hp == NULL)
            return -1;
    bcopy(hp->h_addr,(struct sockaddr *)&servadd.sin_addr,hp->h_length);

    servadd.sin_port = htons(portnum);
    servadd.sin_family = AF_INET;

    if(connect(sock,(struct sockaddr *)&servadd,sizeof(servadd))!=0)
            return -1;
    return sock;
}

webserv.c

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>

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

    if(ac == 1){
        fprintf(stderr,"usage:ws portnum\n");
        exit(1);
    }

    sock = make_server_socket(atoi(av[1]));
    if(sock == -1)
            exit(2);

    while(1){
        fd = accept(sock,NULL,NULL);//接收socket连接
        fpin = fdopen(fd,"r");//使用fdopen使得该文件描述符转换为缓冲流
        fgets(request,BUFSIZ,fpin);//获取请求字符串
        printf("got a call:request = %s",request);
        read_til_crnl(fpin);

        process_rq(request,fd);//处理请求
        fclose(fpin);
    }

}

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


process_rq(char *rq,int fd)
{
    char cmd[BUFSIZ],arg[BUFSIZ];
    if(fork() != 0)
            return;
    strcpy(arg,"./");

    if(sscanf(rq,"%s%s",cmd,arg+2) != 2)//分解请求和参数
            return;
    if(strcmp(cmd,"GET") != 0) //非get类型不处理
            cannot_do(fd);
    else if(not_exist(arg))   //不存在打印404
            do_404(arg,fd);
    else if(isadir(arg))      //目录执行ls -l 
            do_ls(arg,fd);
    else if(ends_in_cgi(arg)) //cgi执行对应的命令
            do_exec(arg,fd);
    else 
            do_cat(arg,fd);  //显示html文件等
}


header(FILE *fp,char * content_type)  //响应头
{
    fprintf(fp,"HTTP/1.0 200 OK\r\n");
    if(content_type)
            fprintf(fp,"Content-type:%s\r\n",content_type);

}

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

    fprintf(fp,"HTTP/1.0 501 Not Implemented\r\n");
    fprintf(fp,"Content-type:text/palain\r\n");
    fprintf(fp,"\r\n");

    fprintf(fp,"That command is not yet implemented\r\n");
    fclose(fp);

}

do_404(char *item,int fd)
{
    FILE *fp = fdopen(fd,"w");
    fprintf(fp,"HTTP/1.0 404 Not Found\r\n");
    fprintf(fp,"Content-type:text/plain\r\n");
    fprintf(fp,"\r\n");

    fprintf(fp,"The item you requested:%s\r\n is not found\r\n",item);
    fclose(fp);
}


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,"\r\n");
    fflush(fp);

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

char *file_type(char *f)
{
    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(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,"\r\n");
        while((c = getc(fpfile)) != EOF)//响应内容
                putc(c,fpsock);
        fclose(fpfile);
        fclose(fpsock);

    }
    exit(0);
}

功能验证
(1)运行服务器

./webserv 端口号

这里写图片描述

(2)在浏览器地址栏输入

主机名:端口号/文件路径

这里写图片描述

(3)查看目录
这里写图片描述

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值