【slighttpd】基于lighttpd架构的Server项目实战(9)—状态机
2016-02-09 21:46
603 查看
上一节中,我们简单回顾了lighttpd的状态机机制,在本节中,我们将把状态机机制引入到我们的项目当中~
我们的状态机大体上效仿lighttpd的状态机,不过有一些改动:
在这里,我们暂对post不做特殊处理,因此所有request数据都将在CON_STATE_READ阶段读进来。
另外,我们的close阶段隐含在了Connection的析构函数当中,因此也不拿出来作为一个状态,所以我们的状态如上所述。
我们整体看看状态机部分的代码:
下面我们分别谈一谈各个阶段:
【CON_STATE_CONNECT】
重置连接,这一个状态将在引入连接池后发挥作用。
【CON_STATE_REQUEST_START】
过渡状态,为读取请求做准备工作;
con_req_cnt表示当前连接处理的请求次数,后面可能用于限制一次长连接所处理的请求数;
之后开始监听读事件,进入CON_STATE_READ。
【CON_STATE_READ】
这个阶段开始读数据,其中调用GetParsedRequest(),该函数从请求队列中获取一个已经解析完的请求并存放到数据成员http_req_parsed中,如果请求队列为空,则对读取的inbuf进行解析,如果是一个完整的请求,则获取该请求,否则返回REQ_NOT_COMPLETE,继续读取;
如果获得一个完整的请求,进入CON_STATE_REQUEST_END状态;
【CON_STATE_REQUEST_END】
结束读取请求,注销读事件。
【CON_STATE_HANDLE_REQUEST】
请求已经解析完了,在这里需要决定如何处理请求;
我们调用PrepareResponse(),它根据解析的结果,决定给用户返回怎样的回复,并填充http_response结构体;
【CON_STATE_RESPONSE_START】
过渡阶段,开始写事件的监听。
【CON_STATE_WRITE】
开始写,调用http_response.GetResponse(),将http_response结构体的数据合成响应字符串,追加到outbuf中用于发送。
【CON_STATE_RESPONSE_END】
调用NotWantWrite()表示不想写了,在这里,我们并没有真正注销写事件,而是只设置一个标识,以后不再触发新的写事件,但是留在输出缓冲区的数据仍继续发送,当缓冲区没数据之后,才真正注销写事件;
下面是写事件回调函数中的部分代码:
只有缓冲区为空且服务器不想写时才真正注销写事件;
之后做一些清理工作,进入CON_STATE_REQUEST_START状态(keep-alive)。
【CON_STATE_ERROR】
出错时进入这个状态,设置错误码响应(500),之后需要将错误码发出去,因此在outbuf为空时才返回false,否则保持write事件。
加入状态机后,我们的整个逻辑就清晰了许多!
接下来我们将开始添加插件模块,之后将插件与状态机结合起来,可以很明显的看到状态机带来的便利~
我们的状态机大体上效仿lighttpd的状态机,不过有一些改动:
[code]typedef enum { CON_STATE_CONNECT, CON_STATE_REQUEST_START, CON_STATE_READ, CON_STATE_REQUEST_END, CON_STATE_HANDLE_REQUEST, CON_STATE_RESPONSE_START, CON_STATE_WRITE, CON_STATE_RESPONSE_END, CON_STATE_ERROR } connection_state_t;
在这里,我们暂对post不做特殊处理,因此所有request数据都将在CON_STATE_READ阶段读进来。
另外,我们的close阶段隐含在了Connection的析构函数当中,因此也不拿出来作为一个状态,所以我们的状态如上所述。
我们整体看看状态机部分的代码:
[code]bool Connection::StateMachine() { request_state_t req_state; while (true) { switch (con_state) { case CON_STATE_CONNECT: ResetConnection(); break; case CON_STATE_REQUEST_START: ++con_req_cnt; WantRead(); SetState(CON_STATE_READ); break; case CON_STATE_READ: req_state = GetParsedRequest(); if (req_state == REQ_ERROR) { return false; } else if (req_state == REQ_IS_COMPLETE) { SetState(CON_STATE_REQUEST_END); break; } else { return true; } break; case CON_STATE_REQUEST_END: NotWantRead(); SetState(CON_STATE_HANDLE_REQUEST); break; case CON_STATE_HANDLE_REQUEST: PrepareResponse(); SetState(CON_STATE_RESPONSE_START); break; case CON_STATE_RESPONSE_START: WantWrite(); SetState(CON_STATE_WRITE); break; case CON_STATE_WRITE: con_outbuf += http_response.GetResponse(); SetState(CON_STATE_RESPONSE_END); break; case CON_STATE_RESPONSE_END: NotWantWrite(); //设置flag表示不想读,但是如果缓冲区还有数据,仍发送,发送完毕再注销写事件 delete http_req_parsed; http_req_parsed = NULL; http_response.ResetResponse(); SetState(CON_STATE_REQUEST_START); break; case CON_STATE_ERROR: http_response.ResetResponse(); SetErrorResponse(); con_outbuf += http_response.GetResponse(); if (con_outbuf.empty()) //错误信息写出去之后才关闭连接 return false; return true; default: return false; } } return true; }
下面我们分别谈一谈各个阶段:
【CON_STATE_CONNECT】
重置连接,这一个状态将在引入连接池后发挥作用。
[code]case CON_STATE_CONNECT: ResetConnection(); break;
【CON_STATE_REQUEST_START】
过渡状态,为读取请求做准备工作;
con_req_cnt表示当前连接处理的请求次数,后面可能用于限制一次长连接所处理的请求数;
之后开始监听读事件,进入CON_STATE_READ。
[code]case CON_STATE_REQUEST_START: ++con_req_cnt; WantRead(); SetState(CON_STATE_READ); break;
【CON_STATE_READ】
这个阶段开始读数据,其中调用GetParsedRequest(),该函数从请求队列中获取一个已经解析完的请求并存放到数据成员http_req_parsed中,如果请求队列为空,则对读取的inbuf进行解析,如果是一个完整的请求,则获取该请求,否则返回REQ_NOT_COMPLETE,继续读取;
如果获得一个完整的请求,进入CON_STATE_REQUEST_END状态;
[code]case CON_STATE_READ: req_state = GetParsedRequest(); if (req_state == REQ_ERROR) { return false; } else if (req_state == REQ_IS_COMPLETE) { SetState(CON_STATE_REQUEST_END); break; } else { return true; } break;
【CON_STATE_REQUEST_END】
结束读取请求,注销读事件。
[code]case CON_STATE_REQUEST_END: NotWantRead(); SetState(CON_STATE_HANDLE_REQUEST); break;
【CON_STATE_HANDLE_REQUEST】
请求已经解析完了,在这里需要决定如何处理请求;
我们调用PrepareResponse(),它根据解析的结果,决定给用户返回怎样的回复,并填充http_response结构体;
[code]case CON_STATE_HANDLE_REQUEST: PrepareResponse(); SetState(CON_STATE_RESPONSE_START); break;
【CON_STATE_RESPONSE_START】
过渡阶段,开始写事件的监听。
[code]case CON_STATE_RESPONSE_START: WantWrite(); SetState(CON_STATE_WRITE); break;
【CON_STATE_WRITE】
开始写,调用http_response.GetResponse(),将http_response结构体的数据合成响应字符串,追加到outbuf中用于发送。
[code]case CON_STATE_WRITE: con_outbuf += http_response.GetResponse(); SetState(CON_STATE_RESPONSE_END); break;
【CON_STATE_RESPONSE_END】
调用NotWantWrite()表示不想写了,在这里,我们并没有真正注销写事件,而是只设置一个标识,以后不再触发新的写事件,但是留在输出缓冲区的数据仍继续发送,当缓冲区没数据之后,才真正注销写事件;
下面是写事件回调函数中的部分代码:
[code]con->con_outbuf.erase(con->con_outbuf.begin(), con->con_outbuf.begin() + ret); if (con->con_outbuf.size() == 0 && !con->con_want_write) { con->UnsetWriteEvent(); }
只有缓冲区为空且服务器不想写时才真正注销写事件;
之后做一些清理工作,进入CON_STATE_REQUEST_START状态(keep-alive)。
[code]case CON_STATE_RESPONSE_END: NotWantWrite(); //设置flag表示不想读,但是如果缓冲区还有数据,仍发送,发送完毕再注销写事件 delete http_req_parsed; http_req_parsed = NULL; http_response.ResetResponse(); SetState(CON_STATE_REQUEST_START); break;
【CON_STATE_ERROR】
出错时进入这个状态,设置错误码响应(500),之后需要将错误码发出去,因此在outbuf为空时才返回false,否则保持write事件。
[code]case CON_STATE_ERROR: http_response.ResetResponse(); SetErrorResponse(); con_outbuf += http_response.GetResponse(); if (con_outbuf.empty()) //错误信息写出去之后才关闭连接 return false; return true;
加入状态机后,我们的整个逻辑就清晰了许多!
接下来我们将开始添加插件模块,之后将插件与状态机结合起来,可以很明显的看到状态机带来的便利~
相关文章推荐
- Description Resource Path Location Type The superclass "javax.servlet.http.HttpServlet" was not foun
- bzoj1391 最大权闭合子图(also最小割、网络流)
- 网络编程(2)—— 基于linux系统的socket客户端编程步骤
- Android开发笔记(六十)网络的检测与连接
- 基于TCP的网络聊天程序
- TCP协议与UDP协议的区别
- 详解AngularJS中的http拦截
- Linux C TCP Programming example
- Unix网络编程:I/O复用
- 线程同步、网络部分
- linux运行级别[转自网络]
- http://browniefed.com/blog/2015/09/10/the-shapes-of-react-native/
- 详解AngularJS中的http拦截
- IOS-网络(文件上传)
- http://www.google.com/ncr 谷歌无限制搜索方法,用过都说好
- AngularJS中的http拦截
- restlet 2.3.5 org.restlet包导入eclipse出现的com.sun.net.httpserver类包找不到问题
- lighttpd-1.4.39 : watcher and worker
- [置顶] 利用django和python构建网络运维平
- JAVA网络编程TCP案例