本文主要是介绍一个支持 cgi 的简易 http 服务器,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
转自:http://blog.csdn.net/u013904227/article/details/52331438
代码 github 链接:https://github.com/SummerInSun/big-http
1. boa 移植以及使用测试
1. 下载 boa-0.94.14rc21.tar.bz2
http://www.boa.org/news.html
2. 编译安装
- 解压:tar -xjf boa-0.94.14rc21.tar.bz2
- 配置:./configure –host=arm-linux –prefix=$PWD/tmp
- 编译:make
- 去掉冗余信息:
ls src/boa -l -rwxr-xr-x 1 book book 233741 2016-08-14 15:34 src/boa arm-linux-strip src/boa -rwxr-xr-x 1 book book 72896 2016-08-14 15:37 src/boa
3. 配置
cd examples/ cp boa.conf boa.conf_ok
- 修改 boa.conf_ok
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 开发板:
mkdir -p /var/log/boa /* 日志文件都存放在这里 */ mkdir /www /* 网页文件存放在这里 */ mkdir /www/cgi-bin /* cgi 文件存放的位置 */ mkdir /etc/boa /* boa.conf 文件放在这里 */cp boa.conf_ok /etc/boa/boa.conf /* 拷贝配置文件 */ cp mime.types /etc/ cp boa /sbin/ /* 拷贝二进制文件 */
- 在 www 目录下面编辑 index.html 文件,内容如下
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 在 /www/cgi-bin 目录下面编辑 cgi-test.c 文件,内容如下
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 修改 /etc/init.d/rcS
添加 /sbin/boa 以开机启动 boa
4. 测试
- 在本机浏览器里面输入:
http://192.168.2.100/cgi-bin/cgi-test.cgi
可以看到Hellow, world.
在本机浏览器里面输入:http://192.168.2.100/
可以看到Hellow, world.
2. 自己写 http 服务器
1、tiny-httpd 注释
int main(void) {初始化一个 socket ,获得套接字描述符,绑定到端口,监听while(1){ // 循环体,接受连接阻塞等待客户端的连接,一旦有连接就获得客户端的地址以及套接字描述符等信息创建线程回应客户端的请求}关闭服务器端口返回 }/**********************************************************************/ /* 相应请求线程 */ /* 处理来自客户端的请求信息 */ /* 参数: 连接的客户端的 socket */ /**********************************************************************/ void accept_request(int client) {获取第一行发来的数据获取方式 "GET" or "POST"获取请求的资源链接如果是 "GET" 方式的话,获取资源的参数,截断资源链接缓冲区获取资源的完整路径获取资源的文件描述,fstat ,如果失败,说明不存在这个文件如果成功,判断资源文件的属性,是文件夹的话加上 index.html 如果是文件有可执行权限的话就作为 cgi 文件处理,否则的话作为 html 文件处理处理完毕,关闭客户端 }/**********************************************************************/ /* 发送文件到客户端. 如果有错误发生的话就向客户端报告错误* 参数: 客户端 socket 描述符* 文件路径名*/ /**********************************************************************/ void serve_file(int client, const char *filename) {读取剩下的客户端发来的数据流打开文件返回响应头部信息发送请求的文件关闭文件 }/**********************************************************************/ /* Execute a CGI script. Will need to set environment variables as* appropriate.* Parameters: client socket descriptor* path to the CGI script */ /**********************************************************************/ void execute_cgi(int client, const char *path,const char *method, const char *query_string) {如果是 "GET" 方式,就读取剩下的数据如果是 "POST" 方式,搜寻 "Content-Length:" 字符串获取报文实体的长度发送响应请求的头部信息建立 cgi 输入输出管道fork 本进程,主进程中返回是非0,子进程返回0子进程设置环境变量,执行 cgi 程序主进程接收剩下的 while (read(cgi_output[0], &c, 1) > 0)发送标准输出结果到客户端 }
2. 一个错误
502 Bad Gateway
The CGI was not CGI/1.1 compliant
-
错误可能有如下情况:
- 文件没有可执行权限
- 头部与报文体没有用一个空行隔开
- 程序内部某个地方有执行错误
-
一个 html 文件的请求与回复过程
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 一个 cgi 文件的请求与回复过程
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
3. 程序流程:
- 主程序
int main(int argc, char *argv[]) {格式初始化准备环境:设置用户,当前目录创建服务器 socket fork 主进程,主进程退出,留下子进程往下运行while(1){等待客户端连接若成功,执行响应函数 HandleRequest失败,进入下一轮等待} }
- 响应
void HandleRequest(int iClientSocketFd) {获取客户端的请求头如成功,返回响应报文关闭 iClientSocketFd }
4. 开发过程中遇到的重要的知识点
-
post 与 get 的区别
- http 协议中
post:长度没有规定上限。用于修改服务器数据
get: 长度没有规定上限。用于获取服务器数据
method 与 data 没有关系,两者是相互独立的概念 - html 协议中
post:约定请求参数放在报文体中,长度随浏览器以及服务器的不同而不同
get: 约定请求参数放在 url 或者 cookie 中,长度随浏览器以及服务器的不同而不同。有的服务器接受 get 方式含有 body,但是浏览器不会这样请求
两种方式的安全性没有本质的区别,都是可以通过一定方式加密,但是也都可以人为进行截获解密,至于 get 的请求参数放在 url 中可能就比 post 更容易获取吧,后者还要开调试界面
- http 协议中
-
send recv 函数
- send
send 与 write 唯一的区别就是 send 的第四个参数:flag
检查 socket 发送缓冲区,如果 buf 大于整个发送缓冲区,返回错误
如果 buf 小于发送缓冲区剩余空间长度,就把 buf 内容拷贝到发送缓冲区中
如果发送缓冲区剩余空间一直小于 buf 长度,函数会阻塞等待
在 send 过程中网络断开会导致进程终止 - recv
如果 socket 发送缓冲区里面有数据,等待发送缓冲区空
如果接收缓冲区空或者正在接收,阻塞等待数据到来
在接收过程中网络断开会导致进程终止
- send
-
服务器 cgi 支持
服务器设置的环境变量:
SERVER_SOFTWARE SERVER_NAME GATEWAY_INTERFACE SERVER_PROTOCOL SERVER_PORT REQUEST_METHOD PATH_INFO PATH_TRANSLATED SCRIPT_NAME QUERY_STRING /* 重要,对 get 方式来说不可缺少 */ REMOTE_HOST REMOTE_ADDR AUTH_TYPE REMOTE_USER REMOTE_IDENT HTTP_COOKIE /* 浏览器 cookie */ CONTENT_TYPE /* 重要,对 cgi post 方式不可缺少 */ CONTENT_LENGTH /* 重要,对 cgi post 方式不可缺少 */
- putenv 与 setenv 的区别
putenv 可以使用已有的变量,不给此变量所指向的内容重新分配空间,例:
char strTmp[256];
putenv(strTmp); /* 这里使用的是 strTmp,并没有重新分配空间,如果多次使用 strTmp 会以第一个为准 */
也可以为常量字符串分配空间,例:
putenv(“QUERY_STRING=action=add?name=1”); /* 这里为常量重新分配了空间 */
s**etenv 则是每一个环境变量都重新分配一次空间,即使参数不是一个字符串常量也会重新为其分配空间**
- 在设计编写程序的时候遇到的问题:
- url 请求过长,服务器是否做了限制以及处理
- 报文体过长,服务器是否做了限制以及处理
- 连接成功建立了,但是浏览器数据发生了延迟或者丢失,服务器是否会永远等待,造成程序死掉
- send 数据但是浏览器没有接收到,或者是协议并没有成功发送数据导致缓冲区满,那么下一次 send 就会阻塞等待,而这种等待永远持续下去怎么处理
- 在数据发送接收过程中出现断网现象,程序会不会出现卡死或者崩溃
这篇关于一个支持 cgi 的简易 http 服务器的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!