J.David的tinyhttpd
500行的服务器程序,是不是已经迫不及待了!
脱掉外套
服务器的执行流程:
- 首先使用startup()在端口port上监听网络连接
- 然后进入循环,每次使用accept()接受一个请求
- 对于每个请求,创建一个线程pthread_create()为其服务
脱掉毛线
对于具体的服务过程(accept_request()),首先它会解析请求的method,tinyhttpd只实现了POST和GET两个请求,如果请求其它method,它会给客户端程序返回一个服务尚未实现的消息(unimplemented());然后解析请求uri,如果是GET的method,取得它的query_string,如果是静态请求,使用serve_file()来为其服务,如果是动态请求,使用execute_cgi()来为其服务。
脱掉内衣
静态请求:打开文件,将文件中的数据传输给客户端程序。
void serve_file(int client, const char *filename){
FILE *resource = fopen(filename, "r");
headers(client, filename);
cat(client, resource);
fclose(resource);
}
void cat(int client, FILE *resource) {
char buf[1024];
fgets(buf, sizeof(buf), resource);
while (!feof(resouce)) {
send(client, buf, strlen(buf), 0);
fgets(buf, sizeof(buf), resource);
}
}
动态请求:使用了fork()和execl()组合,子进程去执行cgi程序,得到的输出通过管道cgi_output传送给父进程;如果method为POST,父进程会将读取到的数据通过管道cgi_input传送给子进程,粗看子进程好像没有任何调用读入的函数,其实读入过程发生在execl()之后,该进程会根据环境变量中设置的CONTENT_LENGTH知道需要读入的字节数。
void execuete_cgi(int client, const char *path,
const char *method, const char *query_string)
{
// POST
while ((numchars > 0) && strcmp("\n", buf)){
buf[15] = '\0';
if (strcasecmp(buf, "Content-Length:") == 0)
content_length = atoi(&(buf[16]));
numchars = get_line(client, buf, sizeof(buf));
}
if (!(pid = fork())) {
dup2(cgi_output[1], 1); // 子进程的输出重定向到cgi_output[1]
dup2(cgi_input[0], 0); // 输入重定向到cgi_input[0]
close(cgi_output[0]);
close(cgi_input[1]);
snprintf(meth_env, "REQUEST_METHOD=%s", method);
putenv(meth_env);
if (!strcasecmp(method, "GET")) {
sprintf(query_env, "QUERY_STRING=%s", query_string);
putenv(query_env);
} else {
snprintf(length_env, "CONTENT_LENGTH=%d", content_length);
putenv(length_env);
}
execl(path, path, NULL);
exit(0);
} else {
close(cgi_output[1]);
close(cgi_input[0]);
if (!strcasecmp(method, "POST"))
for (i = 0; i < content_length; i++) {
recv(client, &c, 1, 0);
write(cgi_input[1], &c, 1);
}
while (read(cgi_output[0], &c, 1) > 0) // 子进程执行execl()的输出
send(client, &c, 1, 0);
close(cgi_output[0]);
close(cgi_input[1]);
waitpid(pid, &status, 0);
}
}
这大概是整个程序中最精妙的部分了,到此一个美女,不,一段代码一丝不挂的站在面前。看代码的过程很舒畅,只是要吐槽下代码中的缩进,缩进只用一个空格简直是不能忍受。
过两天我也要写一个tiny httpd服务器!