Tinyhttpd 是很早以前的一个 web 服务器程序,由 C 语言编写,整个程序十分小巧,源码只有几百行。它一般不适合用于生产环境,因为它很简单,只实现了读取 html 以及 Get / POST 两种方法,并且也只是简单支持了下,无法应对生产环境中的很多问题,生产环境还是要选拥有几十万行代码的成熟的 web服务器 :apache 和 nginx 。
不过 Tinyhttpd 因为过于小巧,所以对于初步了解服务器系统的基本运行原理很有帮助。
以下是我通过查阅相关资料后,对 tinyhttpd 的源码进行的一些注释解读。
运行环境是 mac os gcc version 13.0.0
可以到 https://github.com/kohunglee/tinyhttpd 下载以下文件
httpd.c:
代码语言:javascript复制/* J. David's webserver */
/* This is a simple webserver.
* Created November 1999 by J. David Blackstone.
* CSE 4344 (Network concepts), Prof. Zeigler
* University of Texas at Arlington
*/
/* This program compiles for Sparc Solaris 2.6.
* To compile for Linux:
* 1) Comment out the #include <pthread.h> line.
* 2) Comment out the line that defines the variable newthread.
* 3) Comment out the two lines that run pthread_create().
* 4) Uncomment the line that runs accept_request().
* 5) Remove -lsocket from the Makefile.
*/
#include <stdio.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <ctype.h>
#include <strings.h>
#include <string.h>
#include <sys/stat.h>
#include <pthread.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <stdint.h>
#define ISspace(x) isspace((int)(x))
#define SERVER_STRING "Server: jdbhttpd/0.1.0rn"
#define STDIN 0
#define STDOUT 1
#define STDERR 2
void accept_request(void *); // 处理链接,子线程
void bad_request(int); // 400 错误
void cat(int, FILE *); // 处理文件,读取文件内容,并发送到客户端
void cannot_execute(int); // 500 错误处理函数
void error_die(const char *); // 错误处理函数处理
void execute_cgi(int, const char *, const char *, const char *); // 调用 CGI
int get_line(int, char *, int); // 从缓存区读取一行
void headers(int, const char *); // 服务器成功响应,返回200
void not_found(int); // 请求的内容不存在 404
void serve_file(int, const char *); // 处理文件请求
int startup(u_short *); // 初始化服务器
void unimplemented(int); // 501 仅实现了 get post 方法,其他方法处理函数
/**********************************************************************/
/* A request has caused a call to accept() on the server port to
* return. Process the request appropriately.
* Parameters: the socket connected to the client */
/**********************************************************************/
// 处理链接,子线程
void accept_request(void *arg)
{
int client = (intptr_t)arg; // 建立 socket 描述符
char buf[1024]; // 缓冲区
size_t numchars;
char method[255];
char url[255]; // url
char path[512]; // 路径的字符数组
size_t i, j;
struct stat st; // 文件状态信息,下面检查文件是否存在时会用到
int cgi = 0; // 是否调用 cgi 程序
/* becomes true if server decides this is a CGI
* program */
char *query_string = NULL;
/* 添加 */
pthread_detach(pthread_self()); // 子线程分离,在这个线程结束后,
// 不需要其他的线程对他进行收尸
// 开始对服务器进行读 第一行
// get_line 就是解析 http 协议
// http 协议第一行,请求方法、空格符、url、空格符、协议版本,这是第一行
numchars = get_line(client, buf, sizeof(buf));
i = 0; j = 0;
// 这个循环就是在找空格符,判断第 i 个字符是不是空格
// 并且,没有超过 method 缓冲的大小
// 至于减去一,是因为要在最后面加一个 ’ ‘ ,作为标识符
while (!ISspace(buf[i]) && (i < sizeof(method) - 1))
{
method[i] = buf[i]; // 不是空格,就复制到 method 里面
i ;
}
j=i;
method[i] = '