琐碎的学习——tinyhttpd简易WEB服务器

tinyhttpd是一个超轻量型Http Server,使用C语言开发,全部代码只有502行。

我从sourceforge上下载后编译不通过,提示找不到libsocket,发现去掉对libsocket的依赖也没问题

httpd: httpd.c
	gcc -g -W -Wall -lsocket -lpthread -o httpd httpd.c
#改为
httpd: httpd.c
	gcc -g -W -Wall -lpthread -o httpd httpd.c

 执行cgi的函数中也有错误

#273行
   execl(path, path, NULL);
#改为
   execl(path, query_string, NULL);

 编译运行./httpd就可以把web服务运行起来了。浏览器打开http://127.0.0.1:8080/访问

 下面学习几段源代码

1,main函数初始化套接口监听并接收连接,针对每个连接开启线程去处理

int main(void)
{
 int server_sock = -1;
 u_short port = 8080;
 int client_sock = -1;
 struct sockaddr_in client_name;
 int client_name_len = sizeof(client_name);
 pthread_t newthread;

 server_sock = startup(&port);
 printf("httpd running on port %d\n", port);

 while (1)
 {
  client_sock = accept(server_sock,
                       (struct sockaddr *)&client_name,
                       &client_name_len);
  if (client_sock == -1)
   error_die("accept");
 /* accept_request(client_sock); */
 if (pthread_create(&newthread , NULL, accept_request, client_sock) != 0)
   perror("pthread_create");
 }

 close(server_sock);

 return(0);
}

2,accept_request函数解析客户端请求,判断是请求静态文件还是cgi代码(通过请求类型以及参数来判定),如果是静态文件则将文件输出给前端,如果是cgi则进入cgi处理函数

3,execute_cgi函数负责将请求传递给cgi程序处理,服务器与cgi之间通过管道pip通信,首先初始化两个管道,并创建子进程去执行cgi函数

 if (pipe(cgi_output) < 0) {
  cannot_execute(client);
  return;
 }
 if (pipe(cgi_input) < 0) {
  cannot_execute(client);
  return;
 }

 if ( (pid = fork()) < 0 ) {
  cannot_execute(client);
  return;
 }

 子进程中,用刚才初始化的管道替换掉标准输入标准输出,将请求参数加到环境变量中,调用execl执行cgi程序获得输出。

if (pid == 0)  /* child: CGI script */
 {
  char meth_env[255];
  char query_env[255];
  char length_env[255];

  dup2(cgi_output[1], 1);
  dup2(cgi_input[0], 0);
  close(cgi_output[0]);
  close(cgi_input[1]);
  sprintf(meth_env, "REQUEST_METHOD=%s", method);
  putenv(meth_env);
  if (strcasecmp(method, "GET") == 0) {
   sprintf(query_env, "QUERY_STRING=%s", query_string);
   putenv(query_env);
  }
  else {   /* POST */
   sprintf(length_env, "CONTENT_LENGTH=%d", content_length);
   putenv(length_env);
  }
  execl(path, query_string, NULL);
  exit(0);
 }

 服务器"线程"继续将post的请求头发送给子进程(cgi程序),然后获取cgi程序的标准输出作为响应内容发送给客户端。

else {    /* parent */
  close(cgi_output[1]);
  close(cgi_input[0]);
  if (strcasecmp(method, "POST") == 0)
   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)
   send(client, &c, 1, 0);

  close(cgi_output[0]);
  close(cgi_input[1]);
  waitpid(pid, &status, 0);
 }

 502行代码都比较精炼,服务器在完成一些web功能外还处理了一些异常情况,500状态码cgi执行出错,404找不到文件错误之类的,开始以为putenv函数会设置个全局变量,当多用户访问的时候会互相影响,后来测试了一下发现设置的变量是互相隔离的,这些代码值得学习。

你可能感兴趣的:(web服务器)