zoukankan      html  css  js  c++  java
  • Tinyhttpd阅读笔记

    1、简介

    tinyhttpd是一个开源的超轻量型Http Server,阅读其源码,可以对http协议,微型服务器有进一步的了解。

    源码链接

    参考博客:tinyhttpd源码分析

    2、笔记

    ------------------------------------------------------2016年5月7日21:31:35----------------------------------------------------

    函数原型目录,列举出整个项目所要用到的所有函数原型

     1 void accept_request(void *);
     2 void bad_request(int);
     3 void cat(int, FILE *);
     4 void cannot_execute(int);
     5 void error_die(const char *);
     6 void execute_cgi(int, const char *, const char *, const char *);
     7 int get_line(int, char *, int);
     8 void headers(int, const char *);
     9 void not_found(int);
    10 void serve_file(int, const char *);
    11 int startup(u_short *);
    12 void unimplemented(int);

    每个函数的作用在项目的readme文件中有简单的描述:

    函数 描述
    accept_request 处理从套接字上监听到的一个 HTTP 请求,在这里可以很大一部分地体现服务器处理请求流程
    bad_request 返回给客户端这是个错误请求,HTTP 状态吗 400 BAD REQUEST
    cat 读取服务器上某个文件写到 socket 套接字
    cannot_execute 主要处理发生在执行 cgi 程序时出现的错误
    error_die 把错误信息写到 perror 并退出
    execute_cgi 运行 cgi 程序的处理,也是个主要函数
    get_line 读取套接字的一行,把回车换行等情况都统一为换行符结束
    headers 把 HTTP 响应的头部写到套接字
    not_found 主要处理找不到请求的文件时的情况
    sever_file 调用 cat 把服务器文件返回给浏览器
    startup 初始化 httpd 服务,包括建立套接字,绑定端口,进行监听等
    unimplemented 返回给浏览器表明收到的 HTTP 请求所用的 method 不被支持

    建议的源码阅读顺序:

    main--->startup--->accept_request--->execute_cgi

    按照这个顺序理清整个服务器工作流程。

    main函数

     1 int main(void)
     2 {
     3     int server_sock = -1;
     4     u_short port = 4000;
     5     int client_sock = -1;
     6     struct sockaddr_in client_name;
     7     socklen_t  client_name_len = sizeof(client_name);
     8     pthread_t newthread;
     9 
    10     server_sock = startup(&port);
    11     printf("httpd running on port %d
    ", port);
    12 
    13     while (1)
    14     {
    15         client_sock = accept(server_sock,
    16                 (struct sockaddr *)&client_name,
    17                 &client_name_len);
    18         if (client_sock == -1)
    19             error_die("accept");
    20         /* accept_request(client_sock); */
    21         if (pthread_create(&newthread , NULL, (void *)accept_request, (void *)&client_sock) != 0)
    22             perror("pthread_create");
    23     }
    24 
    25     close(server_sock);
    26 
    27     return(0);
    28 }

    第3-7行是socket相关变量定义,有关socket的详细介绍可以上网查阅相关资料或者查看书籍《Unix网络编程》;

    第10行调用startup函数,有关该函数的内容后续简要解释;

    第13-23行,监听客户端向服务端发来的请求连接,当接收一个连接之后,创建一个线程执行相应的请求,线程入口函数是accept_request,另外有关Linux中pthread_create的使用需要注意一些细节问题。

    第25行关闭服务端的套接字。

    startup函数

     函数参数是一个端口号,用于创建对应端口号的tcp套接字

     1 int startup(u_short *port)
     2 {
     3     int httpd = 0;
     4     struct sockaddr_in name;
     5 
     6     httpd = socket(PF_INET, SOCK_STREAM, 0);
     7     if (httpd == -1)
     8         error_die("socket");
     9     memset(&name, 0, sizeof(name));
    10     name.sin_family = AF_INET;
    11     name.sin_port = htons(*port);
    12     name.sin_addr.s_addr = htonl(INADDR_ANY);
    13     if (bind(httpd, (struct sockaddr *)&name, sizeof(name)) < 0)
    14         error_die("bind");
    15     if (*port == 0)  /* if dynamically allocating a port */
    16     {
    17         socklen_t namelen = sizeof(name);
    18         if (getsockname(httpd, (struct sockaddr *)&name, &namelen) == -1)
    19             error_die("getsockname");
    20         *port = ntohs(name.sin_port);
    21     }
    22     if (listen(httpd, 5) < 0)
    23         error_die("listen");
    24     return(httpd);
    25 }

    第6行创建一个socket套接字,由于其参数是SOCK_STREAM,因此创建的是tcp套接字;

    第7-8行,如果套接字创建不成功,则调用封装好的错误处理函数error_die,该函数比较简单,就是打印错误信息并结束整个程序。

    第13-23则是创建socket服务端的基本流程,先是bind操作,接下来listen操作;

    注意第15行,如果port为0,表示端口号是通过系统自动分配的,需要通过getsockname获取,有关这方面的知识查看《Unix网络编程》书籍

    Startup函数执行完成后,服务端的准备工作完成,将进入监听状态,等待客户端的连接,通过accept函数判断是否有客户端请求连接,如果有,服务端创建一个新的线程执行客户请求,线程的入口函数是accept_request;

    accept_request函数

    该函数的参数是个void*,实际传递给函数的参数是一个套接字描述符。

     1 void accept_request(void *arg)
     2 {
     3     int client = *(int*)arg;
     4     char buf[1024];
     5     size_t numchars;
     6     char method[255];
     7     char url[255];
     8     char path[512];
     9     size_t i, j;
    10     struct stat st;
    11     int cgi = 0;      /* becomes true if server decides this is a CGI
    12                        * program */
    13     char *query_string = NULL;
    14 
    15     numchars = get_line(client, buf, sizeof(buf));
    16     i = 0; j = 0;
    17     while (!ISspace(buf[i]) && (i < sizeof(method) - 1))
    18     {
    19         method[i] = buf[i];
    20         i++;
    21     }
    22     j=i;
    23     method[i] = '';
    24 
    25     if (strcasecmp(method, "GET") && strcasecmp(method, "POST"))
    26     {
    27         unimplemented(client);
    28         return;
    29     }
    30 
    31     if (strcasecmp(method, "POST") == 0)
    32         cgi = 1;
    33 
    34     i = 0;
    35     while (ISspace(buf[j]) && (j < numchars))
    36         j++;
    37     while (!ISspace(buf[j]) && (i < sizeof(url) - 1) && (j < numchars))
    38     {
    39         url[i] = buf[j];
    40         i++; j++;
    41     }
    42     url[i] = '';
    43 
    44     if (strcasecmp(method, "GET") == 0)
    45     {
    46         query_string = url;
    47         while ((*query_string != '?') && (*query_string != ''))
    48             query_string++;
    49         if (*query_string == '?')
    50         {
    51             cgi = 1;
    52             *query_string = '';
    53             query_string++;
    54         }
    55     }
    56 
    57     sprintf(path, "htdocs%s", url);
    58     if (path[strlen(path) - 1] == '/')
    59         strcat(path, "index.html");
    60     if (stat(path, &st) == -1) {
    61         while ((numchars > 0) && strcmp("
    ", buf))  /* read & discard headers */
    62             numchars = get_line(client, buf, sizeof(buf));
    63         not_found(client);
    64     }
    65     else
    66     {
    67         if ((st.st_mode & S_IFMT) == S_IFDIR)
    68             strcat(path, "/index.html");
    69         if ((st.st_mode & S_IXUSR) ||
    70                 (st.st_mode & S_IXGRP) ||
    71                 (st.st_mode & S_IXOTH)    )
    72             cgi = 1;
    73         if (!cgi)
    74             serve_file(client, path);
    75         else
    76             execute_cgi(client, path, method, query_string);
    77     }
    78 
    79     close(client);
    80 }

    第3-13行,变量说明部分;

    第15行,通过get_line函数,从指定的套接字描述符的缓存中读取一行数据,数据长度为size,但是如果遇到' ',函数直接返回,也就是这一行内容最多有size字节,函数返回实际读取到的字节数。

    第17-23行,获取http请求头部;理解这一部分需要对http协议报文格式有所了解

  • 相关阅读:
    常用模块介绍
    正则表达式/re模块
    模块简介/模块的导入/模块的查找顺序/绝对导入和相对导入/软件开发目录规范
    迭代器/for循环本质/生成器/常用内置方法
    函数递归/二分法/列表,字典生成式/三元表达式/匿名函数/内置函数
    闭包函数/装饰器
    函数对象/函数的嵌套定义与调用/名称空间和作用域
    初识函数
    文件处理/光标移动/实时检测
    7-5字符编码和文件处理
  • 原文地址:https://www.cnblogs.com/LCCRNblog/p/5469266.html
Copyright © 2011-2022 走看看