Tinyhttp学习之路

写在前面

最近在学习Linux网络编程,对客户端/服务端有了大致了解,纸上得来终觉浅,总觉得理解得不够彻底,于是在网上找了一个开源代码学习。

tinyhttpd是一个超轻量型Http Server,使用C语言开发,全部代码只有502行(包括注释),附带一个简单的client,可以通过阅读这段代码理解一个 Http Server 的本质。

下载链接:http://sourceforge.net/projects/tinyhttpd/

1、运行

Tinyhttpd是J. David Blackstone在1999年设计的,是跑在Sparc Solaris 2.6上的,如果要在Linux平台上运行,需要对源代码做一些修改。按照代码开始的注释提示,需要做如下修改:

 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.

实际编译过程中:

1.pthread_create()函数

源代码:

	if (pthread_create(&newthread , NULL, accept_request, client_sock) != 0)
   perror("pthread_create");
 }

修改为:

   if (pthread_create(&newthread , NULL, (void *)accept_request, (void *)(intptr_t)client_sock) != 0)
  perror("pthread_create");
}

相应地,accept_request函数的参数需要做微小修改。

2.编译时加lpthread参数

pthread库不是Linux系统默认库,链接时需要使用库libpthread.a,所以使用pthread_create()创建线程时,在编译中要加-lpthread:

#gcc httpd.c -o httpd -lpthread

3.namelen类型改为socklen_t

源代码用"int"数据类型存储"sockaddr_in"结构体的长度,这样在Ubuntu编译中会提示警告,但不会影响生成可执行程序,解决方式是用"socklen_t"代替"int","socklen_t"和"int"应该具有相同的长度,在头文件:
1)#include <sys/socket.h>
2)#include <unistd.h>
中都有定义

4.测试
服务器开启,动态分配的端口号为42639
可以看到,httpd服务器启动成功,动态分配的端口号为42639。
在浏览器提交地址栏输入:
在这里插入图片描述
localhost为本机地址,42639为端口号。请求成功,浏览器会获得页面:
在这里插入图片描述
此页面为源代码文件夹htdocs/index.html中。验证服务器动态页面是否能正常工作,输入“red”之后,提交查询,页面变为红色,程序运行成功。
在这里插入图片描述

2、源码分析

下图为Tinyhttp服务器的整体框架图:
在这里插入图片描述
首先tinyhttp服务器创建监听socket,当浏览器发出http请求被监听到后,服务器通过pthread_create()创建一个线程处理,新创建的线程调用accept_request()函数解析接收到的请求。如果请求错误返回错误信息,如果请求静态网页,将本地文件通过send()函数发送到浏览器,如果请求一个动态网页,创建一个子进程进行处理,子进程处理后的结果通过无名管道PIPE传递给父进程,由父进程用send()发送给浏览器。整个httpd.c里加上main()有13个函数,并不多,下面就对一些重要函数做一个简单介绍。

1.main()

首先分配一个端口创建一个监听socket,用accept()阻塞,直到收到http请求,然后创建一个线程处理请求。

2.accept_request()

accept()返回发出请求的客户端口号client_sock,将其地址作为参数传给accept_request(),它会解析HTTP请求。Tinyhttp涉及到GET和POST方法,先看看这两种方法:

GET:获取资源
GET 方法用来请求访问已被 URI 识别的资源。指定的资源进服务器端解析后返回响应内容。换言之,如果请求的资源是文本(静态页面请求),那就保持原样返回;如果是像CGI 那样的程序(动态页面请求),则返回经过执行后的输出结果。
在这里插入图片描述
POST:传输实体主体
POST 的主要目的是用来传输实体的主体,不是获取响应的主体内容
在这里插入图片描述
当使用浏览器访问服务器时,浏览器会向服务器发起一个http请求。请求类似上两张图的里的请求,以请求类型(GET或者POST)开头,紧接着的请求的资源路径。因此,请求类型和请求路径可以从请求的第一行获取。推荐GET和POST的格式以及区别
完了调用serve_file()服务静态内容,调用execute_cgi()服务动态内容。

3.execute_cgi()

建立两个管道cgi_output和cgi_input,并fork()一个子进程执行CGI程序。父子进程的通信涉及到管道的重定向,这一块是有点绕。通过画图可以一目了然。
在这里插入图片描述
在子进程中,把 STDOUT 重定向到 cgi_outputt 的写入端,把 STDIN 重定向到 cgi_input 的读取端,关闭 cgi_input 的写入端 和 cgi_output 的读取端,设置 request_method 的环境变量,GET 的话设置 query_string 的环境变量,POST 的话设置 content_length 的环境变量,这些环境变量都是为了给 cgi 脚本调用,接着用 execl 运行 cgi 程序。

在父进程中,关闭 cgi_input 的读取端 和 cgi_output 的写入端,如果 POST 的话,把 POST 数据写入 cgi_input,已被重定向到 STDIN,读取 cgi_output 的管道输出到客户端,该管道输入是 STDOUT。接着关闭所有管道,等待子进程结束.

3、源码注释

在学习的过程中,对源码做了较为详细的注释,知识点很分散,就不一一贴上来了,对着源码学习就好。注释肯定有不准确的地方,仅供参考。

下载地址:Tinyhttpd源码注释

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值