概述
写在前面
最近在学习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.测试
可以看到,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源码注释
最后
以上就是哭泣香菇为你收集整理的Tinyhttp学习之路写在前面1、运行2、源码分析3、源码注释的全部内容,希望文章能够帮你解决Tinyhttp学习之路写在前面1、运行2、源码分析3、源码注释所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复