失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > 【slighttpd】基于lighttpd架构的Server项目实战(8)—状态机机制回顾

【slighttpd】基于lighttpd架构的Server项目实战(8)—状态机机制回顾

时间:2023-06-28 02:36:10

相关推荐

【slighttpd】基于lighttpd架构的Server项目实战(8)—状态机机制回顾

转载地址:/jiange_zh/article/details/50640270

有限状态机FSM(Finite State Machine)

关于状态机的一个极度确切的描述是它是一个有向图形,由一组节点和一组相应的转移函数组成。状态机通过响应一系列事件而“运行”。每个事件都在属于“当前” 节点的转移函数的控制范围内,其中函数的范围是节点的一个子集。函数返回“下一个”(也许是同一个)节点。这些节点中至少有一个必须是终态。当到达终态, 状态机停止。

传统应用程序的控制流程基本是顺序的:遵循事先设定的逻辑,从头到尾地执行。很少有事件能改变标准执行流程;而且这些事件主要涉及异常情况。“命令行实用程序”是这种传统应用程序的典型例子。

另一类应用程序由外部发生的事件来驱动——换言之,事件在应用程序之外生成,无法由应用程序或程序员来控制。具体需要执行的代码取决于接收到的事件,或者它相对于其他事件的抵达时间。所以,控制流程既不能是顺序的,也不能是事先设定好的,因为它要依赖于外部事件。

显然,必须采取不同的技术来处理这些情况。它能处理任何顺序的事件,并能提供有意义的响应——即使这些事件发生的顺序和预计的不同。有限状态机正是为了满足这方面的要求而设计的。

lighttpd的状态机机制简要回顾

状态机可以说是lighttpd最核心的部分。lighttpd将一个连接在不同的时刻分成不同的状态,状态机则根据连接当前的状态,决定要对连接进行的处理以及下一步要进入的状态。下面这幅图描述了lighttpd的状态机:

状态机机制体现了清晰的逻辑,并且其设计可以让我们更好地将服务器主体与插件结合起来,共同来完成请求的处理与响应。

下面我们将对lighttpd的状态机进行简要的回顾,具体的讨论,可以参见以下博文:

Lighttpd1.4.20源码分析 笔记 状态机与插件

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, //restart开始读取请求CON_STATE_READ, //read读取并解析请求CON_STATE_REQUEST_END, //reqend读取请求结束CON_STATE_READ_POS, //readpost读取post数据CON_STATE_HANDLE_REQUEST, //handlereq处理请求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 somethingcase CON_STATE_REQUEST_END: /* transient *///do somethingcase CON_STATE_HANDLE_REQUEST://do somethingcase CON_STATE_RESPONSE_START://do somethingcase CON_STATE_RESPONSE_END: /* transient *///do somethingcase CON_STATE_CONNECT://do somethingcase CON_STATE_CLOSE://do somethingcase CON_STATE_READ_POST://do somethingcase CON_STATE_READ://do somethingcase CON_STATE_WRITE://do somethingcase CON_STATE_ERROR: /* transient *///do somethingdefault://do somethingbreak;}//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)—状态机机制回顾》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。