Unix-Linux编程实践教程-chapter12-web-server

2022-03-02 16:59:09 浏览数 (1)

第12章 连接和协议:编写Web服务器

基于socket的客户/服务器程序遵循一个标准架构.服务器接收和处理请求,客户 发出请求

服务器建立服务器端socket.服务器端socket有具体的地址,用来接收连接

客户创建和使用客户端socket.客户并不关心客户端socket的地址

服务器可以用两种方法之一处理请求:自己处理,或fork创建新进程处理请求

Web服务器是最受欢迎的基于socket的程序.Web服务器处理3种类型的请求: 返回文件内容,目录列表和运行程序.请求和应答协议称为HTTP

服务器的设计问题:DIY或代理 DIY:服务器接收请求,自己处理工作.用于快速简单的任务 代理:服务器接收请求,然后创建一个新进程处理工作.用于慢速复杂的任务

Web服务器协议: HTTP请求:GET telnet创建socket并调用connect来连接到Web服务器. HTTP请求:GET /index.html HTTP/1.0 三部分分别是:命令,参数,协议版本号

HTTP应答:OK 服务器读取请求,检查请求,然后返回一个请求.应答包含头部和内容两部分. 头部:HTTP/1.1 200 OK 三部分分别是:协议版本号,返回码,文本解释 内容是具体的网页内容

Code

socklib.c 包含了建立服务器的封装好的函数 编译命令: cc webserv.c socklib.c -o webserv 输入命令: ./webserv 8888 即可将本地作为服务器,端口是8888 通过浏览器输入 http://ip:8888 即可访问服务

代码语言:javascript复制
/*
 * socklib.c
 *
 * This file contains functions used lots when writing internet
 * client/server programs. The two main functions here are:
 *
 * int make_server_socket(portnum) returns a server socket or -1 
 *      if error
 * int make_server_socket_q(portnum, backlog)
 *
 * int connect_to_server(char * hostname, int portnum)
 *      returns a connected socket or -1 if error
 */
#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 <strings.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;

    sock_id = socket(PF_INET, SOCK_STREAM, 0);
    if (sock_id == -1)
        return -1;

    bzero((void *)&saddr, sizeof(saddr));
    gethostname(hostname, HOSTLEN);
    printf("hostname %sn", hostname);
    hp = gethostbyname(hostname);
    char str[32];
    char **pptr;
    pptr = hp->h_addr_list;
    // printf("id %sn", inet_ntop(hp->h_addrtype, *pptr, str, sizeof(str)));
    for (int i = 0; i < hp->h_length;   i)
    {
        printf("%d",(int)hp->h_addr[i]);
        if (i != hp->h_length-1)
            printf(".");
    }
    printf("n");



    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;

    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;
}
代码语言:javascript复制
/*
 * webserv.c - a minimal web server(version 0.2)
 *
 * usage: ws portnumber
 * features: supports the GET command only
 *      runs in the current directory
 *      forks a new child to handle each request
 *      has MAJOR security holes, for demo purposes only
 *      has many other weaknesses, but is a good start
 * build: cc webserv.c socklib.c -o webserv
 */

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

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)
    {
        printf("Begin acceptn");
        // 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);
    }
}

/*
 * 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)
 * 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];

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

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

    printf("arg %sn", arg);

    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: %srn is not foundrn", item);
    fclose(fp);
}

/*
 * the directory listing section
 * isadir() uses stat, not_exist() uses stat
 * do_ls run 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)
{
    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/jpg";
    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);
}

0 人点赞