转载地址:https://blog.csdn.net/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)—状态机机制回顾相关推荐

  1. 【slighttpd】基于lighttpd架构的Server项目实战(3)—MasterWorker模式

    转载地址:https://blog.csdn.net/jiange_zh/article/details/50636180 现在,我们开始一步步构建我们的项目了- Master-Worker模式 本次 ...

  2. 【slighttpd】基于lighttpd架构的Server项目实战(2)—预备知识之libevent

    转载地址:https://blog.csdn.net/jiange_zh/article/details/50631393 简介 由于本项目是纯异步的,而对于大量 socket 连接,使用 selec ...

  3. 【slighttpd】基于lighttpd架构的Server项目实战(7)—http-parser

    转载地址:https://blog.csdn.net/jiange_zh/article/details/50639178 对于http服务器,http request的解析是比较麻烦的,由于我们的重 ...

  4. 【slighttpd】基于lighttpd架构的Server项目实战(5)—TCP的TIME_WAIT状态

    转载地址:https://blog.csdn.net/jiange_zh/article/details/50637549 上一节我们已经开发了一个简单的echo服务器,在这里我们先不急着继续下去,先 ...

  5. 【slighttpd】基于lighttpd架构的Server项目实战(4)—简单的echo服务器

    转载地址:https://blog.csdn.net/jiange_zh/article/details/50636536 在这一部分,我们将对上一篇中的master-worker进行拓展,成为一个简 ...

  6. 【项目】基于SaaS的餐掌柜项目实战 阶段一 基于SaaS的餐掌柜项目实战 第1章 基础架构搭建 1 餐掌柜需求分析

    [项目]基于SaaS的餐掌柜项目实战 文章目录 [项目]基于SaaS的餐掌柜项目实战 阶段一 基于SaaS的餐掌柜项目实战 第1章 基础架构搭建 1 餐掌柜需求分析 1.1 餐饮行业分析 1.2 餐饮 ...

  7. 【项目】bxg基于SaaS的餐掌柜项目实战(2023)

    基于SaaS的餐掌柜项目实战 餐掌柜是一款基于SaaS思想打造的餐饮系统,采用分布式系统架构进行多服务研发,共包含4个子系统,分别为平台运营端.管家端(门店).收银端.小程序端,为餐饮商家打造一站式餐 ...

  8. 【PCIE】基于Riffa架构的PCIE项目

    基于Riffa架构的PCIE项目 Pcie分为四层: ① 物理层:完成信号的转换以及编码 包含 PMA 和 PCS PMA: Physical Media Attachment 物理媒介层,完成并转串 ...

  9. 简单谈一谈基于SaaS的餐掌柜项目实战

    基于SaaS的餐掌柜项目实战 由于基于SaaS的餐掌柜项目本身是一个较为复杂的系统,代码量也相对较大,因此这里只提供一段简单的代码作为示例: // 定义菜品列表 const menuList = [ ...

最新文章

  1. 2016/3/10 PHP (超文本预处理器) 是什么?
  2. Linux find命令批量替换字符串find roles/hadoop_ha/vars/ -name “*.yml“ |xargs perl -pi -e ‘s|node135|node108|g‘
  3. Java基础(39)Arrays.binarySearch方法
  4. hdu 4502 一维dp
  5. api.php phpcms,phpcms程序api怎么写接口
  6. C++ Primer 5th 第15章 面向对象程序设计
  7. 在VMware虚拟机中安装Ubuntu教程
  8. es搜索同义词近义词技术方案
  9. 微信php页面你画我猜,微信小程序你画我猜
  10. NotebookApp] 302 GET /?token=be0e8107dd84eab831a957b640602e5157b5336b15e7fa61 (127.0.0.1) 1.000000ms
  11. 如何梳理陌生的代码模块
  12. gsap_使用GSAP的动画库为Bootstrap传送带制作动画
  13. Web安全—敏感信息泄露
  14. 如何调出IPGUARD控制台维护里的命令行
  15. Consistency = Consensus?
  16. mac上传文件到七牛云,使用qshell上传文件到七牛云
  17. 实习的“黄金圈法则”,你了解多少?
  18. 使用php生成二维码
  19. 数字电子钟的设计与实现(纯数字电路,Proteus仿真)
  20. 2022 需求工程选择填空题【太原理工大学】

热门文章

  1. java中实现同步的两种方式:syschronized和lock的区别和联系
  2. Linux 环境下安装 Golang
  3. python是什么语言汇编_编程语言及python介绍
  4. 通用无线设备对码软件_电动车上的无线电池管理系统wBMS
  5. linux c语言 信号,linux下基于C语言的信号编程实例
  6. 深入了解计算机网络参考模型
  7. 多维列表索引_10分钟带你学会Pandas多层级索引
  8. linux内核irq,linux-kernel – do_IRQ中的中断向量和irq映射
  9. jsp java servlet_jsp+java ,servlet如何实现用户登录和注册页面
  10. 小学计算机打字基础知识,浅谈小学计算机教学技巧5篇