曲径通幽论坛

 找回密码
 立即注册
搜索
查看: 3141|回复: 0
打印 上一主题 下一主题

[系统应用] uhttpd的实现框架

[复制链接]

716

主题

734

帖子

2946

积分

超级版主

Rank: 9Rank: 9Rank: 9

积分
2946
跳转到指定楼层
楼主
发表于 2014-12-16 10:28:57 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
  uhttpd 是一个简单的 web 服务器程序,以前没怎么接触过,所以这里主要是对 web 服务器设计的一些学习总结。Openwrt 系统中,真正用到的(需要了解的),其实不多,主要就是 cgi 的处理,包括与 cgi 程序的信息交互等,最后一节详细描述一下。

1.HTTP协议概述
HTTP 协议是目前互联网使用最广泛的应用层协议。其协议框架很简单,在一个 TCP 连接中,以一问一答的方式进行信息交互。具体讲,就是客户端(如常见的浏览器)connect 服务端的知名端口(通常是80),建立一个TCP连接,然后发送一个 request;服务器端对该 request 解析后,发回相应的 response 应答,并关闭 TCP 连接。这就是一次交互,之后客户端再有请求,则重复上面的过程。

交互报文格式如下图所示:


Request 报文首行为 request-line,其中 type 有GET、POST、HEAD 三种方式,然后最重要的是 url,它告诉服务器所请求的资源。Response 报文首行为 response-line,其中最重要的是 code,它告知客户端响应情况(found、redirect、error等),然后跟一个简单的可读的短语。

两种报文后面具体的内容格式差不多,都是一些 headers(其中冒号前的 str 指明 header 类型),然后以一个空行标识 header 结束,后面是数据。对于request,只有 POST 类型的请求需要提交数据,其它类型的是没有数据的。Response 报文的数据就是 url 所指定的资源文件(html、doc、gif等)。

2.服务器架构

Uhttpd作为一个简单的 web 服务器,其代码量并不多,而且组织结构比较清楚。和其它网络服务器差不多,其 main 函数进行一些初始化(首先 parse config-file,然后parse argv),然后进入一个循环,不断地监听,每当有一个客户请求到达时,则对它进行处理。

  对于 web 服务器,所要做的处理主要就是分析 url,判断出是 file-request、cgi-request 或l ua-request,这主要是根据 url 的最前面的字符串(称为前缀 prefix)得出的;然后就用相应的形式进行处理。如下图所示:


3.cig-response 流程

前面已提到,openwrt系统中使用的uhttpd服务,主要是用cgi方式来回应客户请求的,下面就对这种方式详细阐述。

3.1 url 解析

由上图红色字所示,uh_cgi_request 需要两个二外的参数 pathinfo 和 interpreter,其中 pin 是一个 struct,包含了路径中各种有用信息;ipr 指明所用的 cgi 程序,因为一个服务器中可以有多个 cgi 程序。


3.2 cgi处理框架

要运行 cgi 程序,首先意味着需 fork 出一个子进程,并通过 execl 函数替换进程空间为 cgi 程序;其次,数据传递,子进程替换了进程空间后,怎么获得原信息,有怎么把回馈数据传输给父进程(即uhttpd),父进程又怎么接收这些数据。



      首先创建了两个 pipe,这实际上是利用 AF_UNIX 协议域,创建两个相连的 socket_unix,那么它们映射的文件描述符(即这里的fd[0]、fd[1])就构成了一个 pipe,且这种关系即使 fork 后也仍然存在,因为 fork 仅是增加文件的引用次数,而 os 维护的 file 结构和 socket 结构都没变,这就是父子进程间传递数据的方式。然后 fork 出一个子进程。

    子进程中首先把两个管道的一端 close,注意这仅是使得文件引用次数变为1。由于子进程待会要 excel 替换,替换后 rfd、wfd就不存在了,因此先把它们 dup2 给知名的stdin、stdout,这样即使 execl 替换后,ipt->extu 程序可以以此来和父进程传递数据。另外,execl 替换后,cgi 程序仍需要之前的一些参数信息,如 PATH_INFO 等,这种情况下,最简单的办法就是 setenv,把需要的参数设为环境变量。

    为什么要两个pipe,因为子进程向父进程传递回馈数据需要一个 out-pipe,而若有 post 数据,子进程还需要一个 in-pipe,从父进程读取post数据。

    父进程中首先也是 close,同上所述。若有 post 数据,先从 httprequest-header 中得到 content-length,为后面传递给子进程做准备。然后进入一个循环(为什么要循环,什么时候退出,后面讲),通过select轮询 io,超时、中断的情况就不看了,轮询的 io 一个是 reader,即从子进程读取回馈数据,而若有 post 数据的话,还要另一个 io,writer,向子进程写 post 数据。主要的处理就是上图中红色字所示,具体如下:


转载自:http://www.cnblogs.com/zmkeil/archive/2013/05/14/3078766.html

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|曲径通幽 ( 琼ICP备11001422号-1|公安备案:46900502000207 )

GMT+8, 2025-5-3 13:41 , Processed in 0.084636 second(s), 23 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表