标签:
关于状态机的一个极度确切的描述是它是一个有向图形,由一组节点和一组相应的转移函数组成。状态机通过响应一系列事件而“运行”。每个事件都在属于“当前” 节点的转移函数的控制范围内,其中函数的范围是节点的一个子集。函数返回“下一个”(也许是同一个)节点。这些节点中至少有一个必须是终态。当到达终态, 状态机停止。
传统应用程序的控制流程基本是顺序的:遵循事先设定的逻辑,从头到尾地执行。很少有事件能改变标准执行流程;而且这些事件主要涉及异常情况。“命令行实用程序”是这种传统应用程序的典型例子。
另一类应用程序由外部发生的事件来驱动——换言之,事件在应用程序之外生成,无法由应用程序或程序员来控制。具体需要执行的代码取决于接收到的事件,或者它相对于其他事件的抵达时间。所以,控制流程既不能是顺序的,也不能是事先设定好的,因为它要依赖于外部事件。
显然,必须采取不同的技术来处理这些情况。它能处理任何顺序的事件,并能提供有意义的响应——即使这些事件发生的顺序和预计的不同。有限状态机正是为了满足这方面的要求而设计的。
状态机可以说是lighttpd最核心的部分。lighttpd将一个连接在不同的时刻分成不同的状态,状态机则根据连接当前的状态,决定要对连接进行的处理以及下一步要进入的状态。下面这幅图描述了lighttpd的状态机:
状态机机制体现了清晰的逻辑,并且其设计可以让我们更好地将服务器主体与插件结合起来,共同来完成请求的处理与响应。
下面我们将对lighttpd的状态机进行简要的回顾,具体的讨论,可以参见以下博文:
Lighttpd1.4.20源码分析 笔记 状态机之请求处理
Lighttpd1.4.20源码分析 笔记 状态机之response
Lighttpd1.4.20源码分析 笔记 状态机之错误处理和连接关闭
Lighttpd启动时完成了一系列初始化操作后,就进入了一个包含11个状态的有限状态机中。
每个连接都是一个connection实例(con),状态的切换取决于con->state。
lighttpd经过初步处理后将con的基本信息初始化,而插件对事件的处理就是针对con进行的,它拿到con后按照业务需要进行相应处理,然后再交还给lighttpd,lighttpd根据con中的信息完成响应。
状态定义如下:
typedef enum
{
CON_STATE_CONNECT, //connect 连接开始
CON_STATE_REQUEST_START, //reqstart 开始读取请求
CON_STATE_READ, //read 读取并解析请求
CON_STATE_REQUEST_END, //reqend 读取请求结束
CON_STATE_READ_POST, //readpost 读取post数据
CON_STATE_HANDLE_REQUEST, //handelreq 处理请求
CON_STATE_RESPONSE_START, //respstart 开始回复
CON_STATE_WRITE, //write 回复写数据
CON_STATE_RESPONSE_END, //respend 回复结束
CON_STATE_ERROR, //error 出错
CON_STATE_CLOSE //close 连接关闭
} connection_state_t;
整个状态机的核心函数是connections.c/ connection_state_machine()函数。
函数的主体部分删减之后如下:
int connection_state_machine(server * srv, connection * con)
{
int done = 0, r;
while (done == 0)
{
size_t ostate = con -> state;
int b;
//根据当前状态机的状态进行相应的处理和状态转换。
switch (con->state)
{
case CON_STATE_REQUEST_START: /* transient */
//do something
case CON_STATE_REQUEST_END: /* transient */
//do something
case CON_STATE_HANDLE_REQUEST:
//do something
case CON_STATE_RESPONSE_START:
//do something
case CON_STATE_RESPONSE_END: /* transient */
//do something
case CON_STATE_CONNECT:
//do something
case CON_STATE_CLOSE:
//do something
case CON_STATE_READ_POST:
//do something
case CON_STATE_READ:
//do something
case CON_STATE_WRITE:
//do something
case CON_STATE_ERROR: /* transient */
//do something
default:
//do something
break;
}//end of switch(con -> state) ...
if (done == -1)
{
done = 0;
}
else if (ostate == con->state)
{
done = 1;
}
}
/* something else */
return 0;
}
可以看到,事实上,状态机的主体就是一个switch语句,它根据不同的state进入相应的分支,进行事件的处理,在一个状态处理结束时,会通过调用connection_set_state()函数来设置新的状态,从而推动状态机的运转。
在lighttpd中,各个状态所做的工作总结如下:
【CON_STATE_CONNECT】
清除待读取队列中的数据-chunkqueue_reset(con->read_queue);
置con->request_count = 0。
【CON_STATE_REQUEST_START】 /*transient */
过渡状态;
记录事件起始时间;
con->request_count++(一次长连接最多可以处理的请求数量是有限制的);
转移到CON_STATE_READ状态。
【CON_STATE_READ】和【CON_STATE_READ_POST】
调用connection_handle_read_state(srv,con);
服务器从连接读取HTTP头并存放在con->requeset.request中。
两者的区别:POST的数据量比较大,可能需要临时文件来存储。
【CON_STATE_REQUEST_END】 /*transient */
调用http_request_parse(srv, con)解析请求;
函数首先解析Request line,解析出来的结果存放在
con->request.http_method,
con->request.http_version和
con->request.uri中;
解析完request line后,开始分析header lines。
将field name和value保存到con->request.headers中。
解析完后判断是否有POST数据,有则进入CON_STATE_READ_POST状态,
否则转移到CON_STATE_HANDLE_REQUEST状态。
【CON_STATE_HANDLE_REQUEST】
本状态需要决定如何处理请求;
该状态调用http_response_prepare函数,根据返回值进行相应的处理。
如果函数返回HANDLER_FINISHED,且con->mode!=DIRECT(事件已被插件接管),
则直接进入CON_STATE_RESPONSE_START。
否则lighttpd会做一些处理后再进入CON_STATE_RESPONSE_START状态。
如果函数返回了HANDLER_WAIT_FOR_FD或
HANDLER_WAIT_FOR_EVENT,
状态依旧会停留在CON_STATE_HANDLE_REQUEST,等待事件或数据。
如果函数返回了HANDLER_ERROR,进入到CON_STATE_ERROR状态。
【CON_STATE_RESPONSE_START】
调用connection_handle_write_prepare(srv,con);
根据客户端请求的method来设置response的headers;
状态机进入CON_STATE_WRITE状态。
【CON_STATE_WRITE】
调用connection_handle_write(srv,con);
将响应写回给客户端,注意,数据可能一次发送不完。
如果数据发送完毕,状态机进入CON_STATE_RESPONSE_END状态。
【CON_STATE_RESPONSE_END】
通知所有插件连接处理完毕;
如果是长连接,重新回到CON_STATE_REQUEST_START;
否则通知所有插件连接关闭;
执行connection_close(srv, con);
和connection_reset(srv, con);
连接关闭。
【CON_STATE_ERROR】 /* transient */
调用插件handle_request_done;
调用插件handle_connection_close;
执行connection_close将连接关闭。
【CON_STATE_CLOSE】
调用connection_close(srv, con);
将连接关闭,
注意,这里服务器主动关闭连接,使用shutdown而不是close。
好了,回顾完lighttpd的状态机机制之后,下一节中,我们将把状态机机制引入到我们的项目当中!~
【slighttpd】基于lighttpd架构的Server项目实战(8)—状态机机制回顾
标签:
原文地址:http://blog.csdn.net/jiange_zh/article/details/50640270