zoukankan      html  css  js  c++  java
  • tinyhttpd-0.1.0_hacking

      1 /****************************************************************************
      2  *
      3  *                        tinyhttpd-0.1.0_hacking
      4  *
      5  * 1.这是tinyhttpd-0.1.0版本中httpd.c(主程序)的源码,源码不到500行(除去注释).
      6  * 2.通过分析、阅读该源码,可以一窥web服务器的大致工作机制.
      7  * 3.知识量:
      8  *     1.C语言;
      9  *     2.Unix或类Unix系统编程;
     10  *     3.微量的http协议(请求行、消息头、实体内容);
     11  *     4.如何阅读别人的代码( 从main函数开始 :) );
     12  * 4.tinyhttpd-0.1.0 文件结构如下:
     13  *     .
     14  *     |-- Makefile           -------->makefile 文件
     15  *     |-- README             -------->说明文档
     16  *     |-- htdocs             -------->程序会到该文件夹下找对应html、cgi文件
     17  *     |   |-- README         -------->说明文档
     18  *     |   |-- check.cgi      -------->cgi 程序
     19  *     |   |-- color.cgi      ----^
     20  *     |   `-- index.html     -------->默认的 web 首页文件
     21  *     |-- httpd.c            -------->你接下来要阅读的文件
     22  *     `-- simpleclient.c     -------->没发现该文件有任何用处 @_@
     23  * 5.如何阅读该文档:
     24  *     1.linux下使用vi/vim配和ctags,windows下使用Source Insight,当然你也
     25  *          可以用其他文本编辑器看.
     26  *     2.先找到main函数,然后就可以开始阅读了,遇到对应的函数,就去看对应的
     27  *          函数.
     28  *     3.对于有些函数,本人没有添加注释,或者说本人觉得没必要.
     29  *     4.祝您好运.  :)
     30  *
     31  * 6.tinyhttpd-0.1.0版本下载url: http://sourceforge.net/projects/tinyhttpd/
     32  * 
     33  * 如果您对本文有任何意见、提议,可以发邮件至zengjf42@163.com,会尽快回复.
     34  * 本文的最终解释权归本人(曾剑锋)所有,仅供学习、讨论.
     35  *
     36  *                                          2015-3-1 阴 深圳 尚观 Var
     37  *
     38  ***************************************************************************/
     39 
     40 
     41 /* J. David's webserver */
     42 /* This is a simple webserver.
     43  * Created November 1999 by J. David Blackstone.
     44  * CSE 4344 (Network concepts), Prof. Zeigler
     45  * University of Texas at Arlington
     46  */
     47 /* This program compiles for Sparc Solaris 2.6.
     48  * To compile for Linux:
     49  *  1) Comment out the #include <pthread.h> line.
     50  *  2) Comment out the line that defines the variable newthread.
     51  *  3) Comment out the two lines that run pthread_create().
     52  *  4) Uncomment the line that runs accept_request().
     53  *  5) Remove -lsocket from the Makefile.
     54  */
     55 #include <stdio.h>
     56 #include <sys/socket.h>
     57 #include <sys/types.h>
     58 #include <netinet/in.h>
     59 #include <arpa/inet.h>
     60 #include <unistd.h>
     61 #include <ctype.h>
     62 #include <strings.h>
     63 #include <string.h>
     64 #include <sys/stat.h>
     65 #include <pthread.h>
     66 #include <sys/wait.h>
     67 #include <stdlib.h>
     68 
     69 #define ISspace(x) isspace((int)(x))
     70 
     71 #define SERVER_STRING "Server: jdbhttpd/0.1.0
    "
     72 
     73 void accept_request(int);
     74 void bad_request(int);
     75 void cat(int, FILE *);
     76 void cannot_execute(int);
     77 void error_die(const char *);
     78 void execute_cgi(int, const char *, const char *, const char *);
     79 int  get_line(int, char *, int);
     80 void headers(int, const char *);
     81 void not_found(int);
     82 void serve_file(int, const char *);
     83 int  startup(u_short *);
     84 void unimplemented(int);
     85 
     86 /**
     87  * accept_request 函数说明:
     88  *     1.获取请求方式,目前只支持GET、POST请求;
     89  *     2.在本程序中所有的POST请求、带参数的GET请求都都被定义为访问cgi程序;
     90  *     3.从带参数的GET请求中分离出请求参数;
     91  *     4.如果没有指定需要访问的文件,使用index.html文件作为默认访问文件;
     92  *     5.检查需要访问的文件是否存在,以及其是否具有对应的权限;
     93  *     6.根据是否是cgi程序访问,来执行对应的任务.
     94  */
     95 void accept_request(int client)
     96 {
     97     /**
     98      * 局部变量说明:
     99      *     1.buf      : buffer缩写,主要用于暂存从socket中读出来的数据;
    100      *     2.numchars : 用于保存每次从socket中读到的字符的个数;
    101      *     3.method   : 用于保存请求方式,目前该软件只支持GET、POST这两种方式;
    102      *     4.url      : 用于保存访问文件信息,有些地方叫uri;
    103      *     5.path     : 用于保存文件路径;
    104      *     6.i, j     : 处理数据时的下标;
    105      *     7.st       : 在判断文件类型、是否存在的时候用到;
    106      *     8.cgi      : 是否调用cgi程序的标志.
    107      */
    108     char buf[1024];
    109     int  numchars;    
    110     char method[255]; 
    111     char url[255];    
    112     char path[512];
    113     size_t i, j;
    114     struct stat st;
    115     int cgi = 0;      /* becomes true if server decides this is a CGI
    116                        * program */
    117     char *query_string = NULL;
    118     
    119     /**
    120      * 判断程序是否是GET、POST请求两种的其中一种,如果不是则报错.
    121      */
    122     numchars = get_line(client, buf, sizeof(buf));
    123     i = 0; j = 0;
    124     while (!ISspace(buf[j]) && (i < sizeof(method) - 1))
    125     {
    126         method[i] = buf[j];
    127         i++; j++;
    128     }
    129     method[i] = '';
    130     
    131     if (strcasecmp(method, "GET") && strcasecmp(method, "POST"))
    132     {
    133         unimplemented(client);
    134         return;
    135     }
    136     
    137     /**
    138      * 该程序把POST请求定义为cgi请求.
    139      */
    140     if (strcasecmp(method, "POST") == 0)
    141         cgi = 1;
    142     
    143     /**
    144      * 获取当前url,这里的url不过括网址,而是除去网址之后的东西,
    145      * 如浏览器中输入:http://127.0.0.1:8080/example/index.html
    146      * 得到的url:/example/index.html
    147      * 在有些地方不称这个为url,称之为uri
    148      */
    149     i = 0;
    150     while (ISspace(buf[j]) && (j < sizeof(buf)))
    151         j++;
    152     while (!ISspace(buf[j]) && (i < sizeof(url) - 1) && (j < sizeof(buf)))
    153     {
    154         url[i] = buf[j];
    155         i++; j++;
    156     }
    157     url[i] = '';
    158 
    159     /**
    160      * 每次运行的时候都会出现2次这个,目前还不知道是什么原因导致的原因,
    161      * 这是本人在源代码的基础上添加的调试输出.
    162      * url: /favicon.ico
    163      * url: /favicon.ico
    164      */
    165     printf("url: %s
    ", url);
    166     
    167     /**
    168      * 如果是GET请求,如果带了请求参数,那么也是cgi请求,并且从url中分离出请求参数
    169      */
    170     if (strcasecmp(method, "GET") == 0)
    171     {
    172         query_string = url;
    173         while ((*query_string != '?') && (*query_string != ''))
    174             query_string++;
    175         if (*query_string == '?')
    176         {
    177             cgi = 1;
    178             *query_string = '';
    179             query_string++;
    180         }
    181     }
    182     
    183     /**
    184      * 所有的需要的html文件、cgi程序都在htdocs文件夹中,
    185      * 如果没有指定html文件,或者cgi程序,那么使用默认的index.html文件
    186      * 作为目标输出文件.
    187      */
    188     sprintf(path, "htdocs%s", url);
    189     if (path[strlen(path) - 1] == '/')
    190         strcat(path, "index.html");
    191     
    192     /**
    193      * 检查要访问的文件的状态,如:
    194      *     1.是否存在;
    195      *     2.是否是一个文件夹;
    196      *     3.如果是cgi程序,是否用于对应的权限.
    197      * 当然如果执行stat时就出错了,那么,直接将socket中的数据读完,
    198      * 然后返回没有找到相关内容的信息提示.
    199      */
    200     if (stat(path, &st) == -1) {
    201         while ((numchars > 0) && strcmp("
    ", buf))  /* read & discard headers */
    202             numchars = get_line(client, buf, sizeof(buf));
    203         not_found(client);
    204     }
    205     else
    206     {
    207         if ((st.st_mode & S_IFMT) == S_IFDIR) /* 如果是一个文件夹 */
    208             strcat(path, "/index.html");
    209         if ((st.st_mode & S_IXUSR) ||
    210             (st.st_mode & S_IXGRP) ||
    211             (st.st_mode & S_IXOTH)    )  /* 权限问题 */
    212             cgi = 1;
    213         
    214         /**
    215          * 通过cgi变量来判断是执行cgi程序,还是仅仅是返回一个html页面.
    216          */
    217         if (!cgi)
    218             serve_file(client, path); /* 向客户端返回一个html文件 */
    219         else
    220             execute_cgi(client, path, method, query_string); /* 执行一个cgi程序 */
    221     }
    222     
    223     close(client);
    224 }
    225 
    226 void bad_request(int client)
    227 {
    228     char buf[1024];
    229 
    230     sprintf(buf, "HTTP/1.0 400 BAD REQUEST
    ");
    231     send(client, buf, sizeof(buf), 0);
    232     sprintf(buf, "Content-type: text/html
    ");
    233     send(client, buf, sizeof(buf), 0);
    234     sprintf(buf, "
    ");
    235     send(client, buf, sizeof(buf), 0);
    236     sprintf(buf, "<P>Your browser sent a bad request, ");
    237     send(client, buf, sizeof(buf), 0);
    238     sprintf(buf, "such as a POST without a Content-Length.
    ");
    239     send(client, buf, sizeof(buf), 0);
    240 }
    241 
    242 /**
    243  * 主要完成将resource指向的文件内容拷贝输出到客户端浏览器中
    244  */
    245 void cat(int client, FILE *resource)
    246 {
    247     char buf[1024];
    248 
    249     fgets(buf, sizeof(buf), resource);
    250     while (!feof(resource))
    251     {
    252         send(client, buf, strlen(buf), 0);
    253         fgets(buf, sizeof(buf), resource);
    254     }
    255 }
    256 
    257 void cannot_execute(int client)
    258 {
    259     char buf[1024];
    260 
    261     sprintf(buf, "HTTP/1.0 500 Internal Server Error
    ");
    262     send(client, buf, strlen(buf), 0);
    263     sprintf(buf, "Content-type: text/html
    ");
    264     send(client, buf, strlen(buf), 0);
    265     sprintf(buf, "
    ");
    266     send(client, buf, strlen(buf), 0);
    267     sprintf(buf, "<P>Error prohibited CGI execution.
    ");
    268     send(client, buf, strlen(buf), 0);
    269 }
    270 
    271 void error_die(const char *sc)
    272 {
    273     perror(sc);
    274     exit(1);
    275 }
    276 
    277 void execute_cgi(int client, const char *path,
    278                  const char *method, const char *query_string)
    279 {
    280     /**
    281      * 局部变量说明:
    282      *     1.buf        : buffer缩写;
    283      *     2.cgi_output : 用于保存输出管道的文件描述符;
    284      *     3.cgi_input  : 用于保存输入管道的文件描述符;
    285      *     4.pid        : 进程pid,最后父进程退出之前,等待子进程先退出,
    286      *                  并回收相关的资源,这部分工作主要由waitpid()来完成;
    287      *     5.status     : 在waitpid()中用于保存子进程的退出状态,本程序没有具体使用;
    288      *     6.i          : 计数器;
    289      *     7.c          : POST读取请求参数时,读取到的字符保存在这里;
    290      *     8.numchars   : 读取的字符个数;
    291      *     9.conten_length : 内容实体的字符数;
    292      */
    293     char buf[1024];
    294     int cgi_output[2];
    295     int cgi_input[2];
    296     pid_t pid;
    297     int status;
    298     int i;
    299     char c;
    300     int numchars = 1;
    301     int content_length = -1;
    302 
    303     /**
    304      * 在本程序中,GET请求的消息头没有任何用处,直接处理掉就行了,
    305      * 而如果是POST请求,需要的消息头中的获取实体的大小,也就是Content-Length:后面跟的数字
    306      */
    307     buf[0] = 'A'; buf[1] = '';
    308     if (strcasecmp(method, "GET") == 0)
    309         while ((numchars > 0) && strcmp("
    ", buf))  /* read & discard headers */
    310             numchars = get_line(client, buf, sizeof(buf));
    311     else    /* POST */
    312     {
    313         numchars = get_line(client, buf, sizeof(buf));
    314         while ((numchars > 0) && strcmp("
    ", buf))
    315         {
    316             buf[15] = '';
    317             if (strcasecmp(buf, "Content-Length:") == 0)
    318                 content_length = atoi(&(buf[16]));
    319             numchars = get_line(client, buf, sizeof(buf));
    320         }
    321         if (content_length == -1) {
    322             bad_request(client);
    323             return;
    324         }
    325     }
    326 
    327     /**
    328      * 返回返回行信息.
    329      */
    330     sprintf(buf, "HTTP/1.0 200 OK
    ");
    331     send(client, buf, strlen(buf), 0);
    332 
    333     /**
    334      * 父子进程通过管道通信.
    335      */
    336     if (pipe(cgi_output) < 0) {
    337         cannot_execute(client);
    338         return;
    339     }
    340     if (pipe(cgi_input) < 0) {
    341         cannot_execute(client);
    342         return;
    343     }
    344 
    345     /**
    346      * 创建子进程,用于执行cgi程序,父进程接受子进程的结果,并返回给浏览器
    347      */
    348     if ( (pid = fork()) < 0 ) {
    349         cannot_execute(client);
    350         return;
    351     }
    352     if (pid == 0)  /* child: CGI script */
    353     {
    354         char meth_env[255];   //cgi 请求方式环境变量
    355         char query_env[255];  //cgi GET请求参数环境变量
    356         char length_env[255]; //cgi POST请求参数内容大小环境变量
    357 
    358         /**
    359          * 重定向标准输入输出,并设置好对应的环境变量.
    360          */
    361         dup2(cgi_output[1], 1);
    362         dup2(cgi_input[0], 0);
    363         close(cgi_output[0]);
    364         close(cgi_input[1]);
    365         sprintf(meth_env, "REQUEST_METHOD=%s", method);
    366         putenv(meth_env);
    367         if (strcasecmp(method, "GET") == 0) {
    368             sprintf(query_env, "QUERY_STRING=%s", query_string);
    369             putenv(query_env);
    370         }
    371         else {   /* POST */
    372             sprintf(length_env, "CONTENT_LENGTH=%d", content_length);
    373             putenv(length_env);
    374         }
    375         /* 执行对应的程序 */
    376         execl(path, path, NULL);
    377         exit(0);
    378     } else {    /* parent */
    379         close(cgi_output[1]);
    380         close(cgi_input[0]);
    381         /**
    382          * 对于POST请求,将实体中的请求参数通过管道传送到cgi程序中
    383          */
    384         if (strcasecmp(method, "POST") == 0)
    385             for (i = 0; i < content_length; i++) {
    386                 recv(client, &c, 1, 0);
    387                 write(cgi_input[1], &c, 1);
    388             }
    389         /**
    390          * 读取cgi程序的执行结果,返回给浏览器
    391          */
    392         while (read(cgi_output[0], &c, 1) > 0)
    393             send(client, &c, 1, 0);
    394 
    395         close(cgi_output[0]);
    396         close(cgi_input[1]);
    397         /**
    398          * 等待子进程运行结束,并回收子进程的资源,
    399          * 防止出现孤儿进程
    400          */
    401         waitpid(pid, &status, 0);
    402     }
    403 }
    404 
    405 int get_line(int sock, char *buf, int size)
    406 {
    407     /**
    408      * 局部变量说明:
    409      *     1.i : 数组下标计数,不能大于size;
    410      *     2.c : 每次读到的字符保存在这里面;
    411      *     3.n : 每次读到的字符个数.
    412      */
    413     int i = 0;
    414     char c = '';
    415     int n;
    416 
    417     /**
    418      * 一直读到buf满了,或者遇到了'
    '为止.
    419      */
    420     while ((i < size - 1) && (c != '
    '))
    421     {
    422         n = recv(sock, &c, 1, 0);
    423         /* DEBUG printf("%02X
    ", c); */
    424         if (n > 0)
    425         {
    426             /**
    427              * 读到'
    '也算是结束,通过判断后面有没有跟'
    '来判断是否要将下
    428              * 一个字符取出来,并且无论'
    '后面跟不跟'
    ',都将'
    '换成'
    '.
    429              */
    430             if (c == '
    ')
    431             {
    432                 n = recv(sock, &c, 1, MSG_PEEK);
    433                 /* DEBUG printf("%02X
    ", c); */
    434                 if ((n > 0) && (c == '
    '))
    435                     recv(sock, &c, 1, 0);
    436                 else
    437                     c = '
    ';
    438             }
    439             buf[i] = c;
    440             i++;
    441         }
    442         else
    443             c = '
    ';
    444     }
    445     buf[i] = ''; /* 字符串结尾 */
    446     
    447     return(i);
    448 }
    449 
    450 void headers(int client, const char *filename)
    451 {
    452     char buf[1024];
    453     (void)filename;  /* could use filename to determine file type */
    454 
    455     strcpy(buf, "HTTP/1.0 200 OK
    ");
    456     send(client, buf, strlen(buf), 0);
    457     strcpy(buf, SERVER_STRING);
    458     send(client, buf, strlen(buf), 0);
    459     sprintf(buf, "Content-Type: text/html
    ");
    460     send(client, buf, strlen(buf), 0);
    461     strcpy(buf, "
    ");
    462     send(client, buf, strlen(buf), 0);
    463 }
    464 
    465 void not_found(int client)
    466 {
    467     char buf[1024];
    468 
    469     sprintf(buf, "HTTP/1.0 404 NOT FOUND
    ");
    470     send(client, buf, strlen(buf), 0);
    471     sprintf(buf, SERVER_STRING);
    472     send(client, buf, strlen(buf), 0);
    473     sprintf(buf, "Content-Type: text/html
    ");
    474     send(client, buf, strlen(buf), 0);
    475     sprintf(buf, "
    ");
    476     send(client, buf, strlen(buf), 0);
    477     sprintf(buf, "<HTML><TITLE>Not Found</TITLE>
    ");
    478     send(client, buf, strlen(buf), 0);
    479     sprintf(buf, "<BODY><P>The server could not fulfill
    ");
    480     send(client, buf, strlen(buf), 0);
    481     sprintf(buf, "your request because the resource specified
    ");
    482     send(client, buf, strlen(buf), 0);
    483     sprintf(buf, "is unavailable or nonexistent.
    ");
    484     send(client, buf, strlen(buf), 0);
    485     sprintf(buf, "</BODY></HTML>
    ");
    486     send(client, buf, strlen(buf), 0);
    487 }
    488 
    489 void serve_file(int client, const char *filename)
    490 {
    491     /**
    492      * 局部变量说明:
    493      *     1.resource : 打开的文件的文件指针;
    494      *     2.numchars : 每次读到的字符个数;
    495      *     3.buf      : buffer的缩写.
    496      */
    497     FILE *resource = NULL;
    498     int numchars = 1;
    499     char buf[1024];
    500 
    501     /**
    502      * 在本程序中消息头对于纯GET请求没有什么用,直接读取丢掉.
    503      */
    504     buf[0] = 'A'; buf[1] = '';
    505     while ((numchars > 0) && strcmp("
    ", buf))  /* read & discard headers */
    506         numchars = get_line(client, buf, sizeof(buf));
    507 
    508     resource = fopen(filename, "r");
    509     if (resource == NULL)
    510         not_found(client);
    511     else
    512     {
    513         /* 发送消息头 */
    514         headers(client, filename);
    515         /* 发送内容实体 */
    516         cat(client, resource);
    517     }
    518     fclose(resource);
    519 }
    520 
    521 /**
    522  * startup 函数完成内容:
    523  *     1.获取一个作为服务器的socket;
    524  *     2.绑定服务器端的socket;
    525  *     3.通过判断参数port的值,确定是否需要动态分配端口号;
    526  *     4.服务器开启监听;
    527  *     5.返回服务器段的socket文件描述符.
    528  */
    529 int startup(u_short *port)
    530 {
    531     /**
    532      * 局部变量说明:
    533      *     1.httpd : 保存服务器socket描述符,并作为返回值返回;
    534      *     2.name  : 用于保存服务器本身的socket信息,创建服务器.
    535      */
    536     int httpd = 0;
    537     struct sockaddr_in name;
    538 
    539     httpd = socket(PF_INET, SOCK_STREAM, 0); 
    540     if (httpd == -1)
    541         error_die("socket");
    542 
    543     memset(&name, 0, sizeof(name));
    544     name.sin_family = AF_INET;
    545     name.sin_port = htons(*port);
    546     name.sin_addr.s_addr = htonl(INADDR_ANY);
    547 
    548     if (bind(httpd, (struct sockaddr *)&name, sizeof(name)) < 0)
    549         error_die("bind");
    550 
    551     if (*port == 0)  /* if dynamically allocating a port */
    552     {
    553         int namelen = sizeof(name);
    554         if (getsockname(httpd, (struct sockaddr *)&name, &namelen) == -1)
    555             error_die("getsockname");
    556         *port = ntohs(name.sin_port);
    557     }
    558 
    559     if (listen(httpd, 5) < 0)
    560         error_die("listen");
    561     return(httpd);
    562 }
    563 
    564 void unimplemented(int client)
    565 {
    566     char buf[1024];
    567 
    568     sprintf(buf, "HTTP/1.0 501 Method Not Implemented
    ");
    569     send(client, buf, strlen(buf), 0);
    570     sprintf(buf, SERVER_STRING);
    571     send(client, buf, strlen(buf), 0);
    572     sprintf(buf, "Content-Type: text/html
    ");
    573     send(client, buf, strlen(buf), 0);
    574     sprintf(buf, "
    ");
    575     send(client, buf, strlen(buf), 0);
    576     sprintf(buf, "<HTML><HEAD><TITLE>Method Not Implemented
    ");
    577     send(client, buf, strlen(buf), 0);
    578     sprintf(buf, "</TITLE></HEAD>
    ");
    579     send(client, buf, strlen(buf), 0);
    580     sprintf(buf, "<BODY><P>HTTP request method not supported.
    ");
    581     send(client, buf, strlen(buf), 0);
    582     sprintf(buf, "</BODY></HTML>
    ");
    583     send(client, buf, strlen(buf), 0);
    584 }
    585 
    586 /**********************************************************************/
    587 
    588 int main(void)
    589 {
    590     /**
    591      * 局部变量说明:
    592      *     1.server_sock : 服务器端的socket描述符;
    593      *     2.port          : 服务器端的socket端口号,如果是0的,startup()将会采用
    594      *                   自动生成的方式生成新的端口号供使用;
    595      *     3.client_sock : 客户端连接进来产生的客户端socket描述符;
    596      *     4.client_name : 用于保存客户端连接进来的socket信息;
    597      *     5.client_name_len : struct sockaddr_in结构体的大小,在accpet的时候
    598      *                   需要用到,这个参数必须传,否则会出错;
    599      *     6.newthread   : 用于保存新创建的线程的ID.
    600      */
    601     int server_sock = -1; 
    602     u_short port = 0;       
    603     int client_sock = -1; 
    604     struct sockaddr_in client_name; 
    605     int client_name_len = sizeof(client_name);
    606     pthread_t newthread;
    607 
    608     /**
    609      * startup 函数完成内容:
    610      *     1.获取一个作为服务器的socket;
    611      *     2.帮定服务器断的sockt;
    612      *     3.通过判断参数port的值,确定是否需要动态分配端口号;
    613      *     4.服务器开启监听.
    614      */
    615     server_sock = startup(&port);
    616     printf("httpd running on port %d
    ", port);
    617 
    618     while (1)
    619     {
    620         /**
    621          * 等待客户端的连接,使用client_name保存客户端socket信息,
    622          * client_name_len是client_name对应结构体的长度.
    623          */
    624         client_sock = accept(server_sock,
    625                              (struct sockaddr *)&client_name,
    626                              &client_name_len);
    627         if (client_sock == -1)
    628             error_die("accept");
    629         /**
    630          * 创建一个新的线程来处理任务,并把客户端的socket描述符作为参数传给accept_request,
    631          * accept_request 函数说明:
    632          *     1.获取请求方式,目前只支持GET、POST请求;
    633          *     2.在本程序中所有的POST请求、带参数的GET请求都都被定义为访问cgi程序;
    634          *     3.从带参数的GET请求中分离出请求参数;
    635          *     4.如果没有指定需要访问的文件,使用index.html文件作为默认访问文件;
    636          *     5.检查需要访问的文件是否存在,以及其是否具有对应的权限;
    637          *     6.根据是否是cgi程序访问,来执行对应的任务.
    638          */
    639         if (pthread_create(&newthread , NULL, accept_request, client_sock) != 0)
    640             perror("pthread_create");
    641     }
    642 
    643     /**
    644      * 不知道为什么,这条语句在while外边,竟然会影响到程序的关闭 :(
    645      * 这行代码注释掉才能连续访问,不注释,只能访问一次,所以直接注释了
    646      * 反正程序停止都使用ctrl+c,不影响程序的运行.
    647      */
    648     //close(server_sock);
    649 
    650     return(0);
    651 }
  • 相关阅读:
    关于如何学习C语言
    2020软件工程作业04
    2020软件工程作业03
    2020软件工程作业02
    2020软件工程作业01
    3.语法树,短语,直接短语,句柄
    2.文法和语言
    Linux——如何将Red Hat Enterprise Linux 6的语言改为中文?
    编译原理概述
    编译原理概述-第一次作业
  • 原文地址:https://www.cnblogs.com/zengjfgit/p/4308459.html
Copyright © 2011-2022 走看看