龙空技术网

C开源代码学习 Tinyhttpd

编程圈子 3976

前言:

现在兄弟们对“c语言解析http”可能比较讲究,小伙伴们都需要分析一些“c语言解析http”的相关资讯。那么小编也在网摘上收集了一些关于“c语言解析http””的相关知识,希望各位老铁们能喜欢,姐妹们一起来学习一下吧!

一、简介

Tinyhttpd是一个非常小巧的C语言写的http服务端,一个单独的C程序只有六百多行,支持静态文件输出和执行CGI,对于和我一样的C初学者来说,非常有利上手学习,而且有利于理解http服务原理。

官网:

仓库地址:

码云镜像:

二、环境搭建

本文开发环境:

MacOSClion1. 新建一个Clion C语言项目2. 把tinyhttpd的源代码拖进项目

这时目录可能是这样的:

(注意这里的cmake-build-debug后面编译才会有)

3. 这里不需要simpleclient.c,把它删除4. 编译

这时会生成 cmake-build-debug目录。

5. 把htdocs拷进 cmake-build-debug6. 设置一下权限

把 index.html index2.html取消可执行权限

chmod -x index.htmlchmod -x index2.html12
7. 运行程序

点main上的绿色倒三角运行程序。

8. 观察控制台输出的端口号,在浏览器打开网址

随便输入一种颜色,点提交,页面显示提交的颜色。

三、代码解析1. 程序入口

int main(void) {    int server_sock = -1;    u_short port = 0;    int client_sock = -1;    struct sockaddr_in client_name;    //这边要为socklen_t类型    socklen_t client_name_len = sizeof(client_name);    pthread_t newthread;    server_sock = startup(&port);    printf("httpd running on port %d\n", port);    while (1) {        //接受请求,函数原型        //#include <sys/types.h>        //#include <sys/socket.h>        //int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);        client_sock = accept(server_sock,                             (struct sockaddr *) &client_name,                             &client_name_len);        if (client_sock == -1)            error_die("accept");        /* accept_request(client_sock); */        //每次收到请求,创建一个线程来处理接受到的请求        //把client_sock转成地址作为参数传入pthread_create        if (pthread_create(&newthread, NULL, (void *) accept_request, (void *) (intptr_t) client_sock) != 0)            perror("pthread_create");    }    close(server_sock);    return (0);}

main里面有一个while循环,用来接收tcp连接,接收到以后建立新线程对请求进行处理。这里要引用系统的socket.h

u_short变量 port 是指定的本地端口号,0表示随机端口,也可以写一个固定值用来固定端口号。

pthread_create是c的建立新线程函数,下面是函数原型。可以看到 accept_request是处理请求的核心函数。client_sock会作为参数传入accept_request函数里。

#include <pthread.h>int pthread_create(                 pthread_t *restrict tidp,   //新创建的线程ID指向的内存单元。                 const pthread_attr_t *restrict attr,  //线程属性,默认为NULL                 void *(*start_rtn)(void *), //新创建的线程从start_rtn函数的地址开始运行                 void *restrict arg //默认为NULL。若上述函数需要参数,将参数放入结构中并将地址作为arg传入。                  );
2. 请求解析 accept_request 函数

这里引用了比较重要的一个函数是get_line,它用来从 socket 里读取一行。读取后会进行后面的处理。首先判断是不是POST/GET请求,如果不是则返回 unimplemented

    //判断是Get还是Post    if (strcasecmp(method, "GET") && strcasecmp(method, "POST")) {        unimplemented(client);        return;    }

未处理的方法会输出错误信息:

//如果方法没有实现,就返回此信息void unimplemented(int client) {    char buf[1024];    sprintf(buf, "HTTP/1.0 501 Method Not Implemented\r\n");    send(client, buf, strlen(buf), 0);    sprintf(buf, SERVER_STRING);    send(client, buf, strlen(buf), 0);    sprintf(buf, "Content-Type: text/html\r\n");    send(client, buf, strlen(buf), 0);    sprintf(buf, "\r\n");    send(client, buf, strlen(buf), 0);    sprintf(buf, "<HTML><HEAD><TITLE>Method Not Implemented\r\n");    send(client, buf, strlen(buf), 0);    sprintf(buf, "</TITLE></HEAD>\r\n");    send(client, buf, strlen(buf), 0);    sprintf(buf, "<BODY><P>HTTP request method not supported.\r\n");    send(client, buf, strlen(buf), 0);    sprintf(buf, "</BODY></HTML>\r\n");    send(client, buf, strlen(buf), 0);}

这里可以看到,输出html采用的是打印字符串、再发送tcp消息。

3. get请求处理

如果是get请求,会解析地址有没有带文件名,如果没带就设置默认 index.html 。

    //路径    sprintf(path, "htdocs%s", url);    //默认地址,解析到的路径如果为/,则自动加上index.html    if (path[strlen(path) - 1] == '/')        strcat(path, "index.html");
4. 文件处理

得到文件名,使用stat函数判断文件是否存在,如果没有找到,就返回not_found

如果文件存在,则判断文件是否有可执行权限,对于可执行程序调用 execute_cgi函数,非可执行程序则调用serve_file返回给客户端。

5.serve_file读取文件返回客户端

//如果不是CGI文件,直接读取文件返回给请求的http客户端void serve_file(int client, const char *filename) {    FILE *resource = NULL;    int numchars = 1;    char buf[1024];    //默认字符    buf[0] = 'A';    buf[1] = '\0';    while ((numchars > 0) && strcmp("\n", buf))  /* read & discard headers */        numchars = get_line(client, buf, sizeof(buf));    resource = fopen(filename, "r");    if (resource == NULL)        not_found(client);    else {        headers(client, filename);        cat(client, resource);    }    fclose(resource);}

这个函数主要读取文件,加上http头。读取发送文件的函数:

//得到文件内容,发送void cat(int client, FILE *resource) {    char buf[1024];    fgets(buf, sizeof(buf), resource);    //循环读    while (!feof(resource)) {        send(client, buf, strlen(buf), 0);        fgets(buf, sizeof(buf), resource);    }}
6.execute_cgi可执行程序
     //从output管道读到子进程处理后的信息,然后send出去        while (read(cgi_output[0], &c, 1) > 0)            send(client, &c, 1, 0);

标签: #c语言解析http