Lighttpd1.4.20源代码分析 笔记 状态机之错误处理和连接关闭

这里所说的错误有两种:

1.http协议规定的错误,如404错误。

2.server执行过程中的错误。如write错误。

对于http协议规定的错误,这里的“错误”是针对client的。

lighttpd返回相应的错误提示文件之后,相当于顺利的完毕了一次请求,仅仅是结果和client想要的不一样而已。

对于server执行中的错误,状态机进入CON_STATE_ERROR状态。常见的错误原因:client提前断开连接。

比方你不停的刷新页面。在你刷新的时候,前一次的连接没有完毕,但被浏览器强行断开。对于server而言,刷新前后的两个连接是不相干的,server在接收后一个连接的时候仍然会继续处理前一次的连接。而前一次的连接已断开,这就产生了连接错误。

进入CON_STATE_ERROR状态后。假设前面的请求处理已经得到了结果。也就是http_status不为空。那么调用plugins_call_handle_request_done告诉插件请求处理结束:

            /* even if the connection was drop we still have to write it to the access log */
            if (con->http_status) {
                plugins_call_handle_request_done(srv, con);
            }

假设使用了ssl,关闭ssl连接:

#ifdef USE_OPENSSL
    if (srv_sock->is_ssl) {
    /* 关闭ssl连接 */
    }
    ERR_clear_error();
#endif

接着:

            switch(con->mode) {
            case DIRECT:
#if 0
                log_error_write(srv, __FILE__, __LINE__, "sd",
                        "emergency exit: direct",
                        con->fd);
#endif
                break;
            default:
                switch(r = plugins_call_handle_connection_close(srv, con)) {
                case HANDLER_GO_ON:
                case HANDLER_FINISHED:
                    break;
                default:
                    log_error_write(srv, __FILE__, __LINE__, "");
                    break;
                }
                break;
            }
            connection_reset(srv, con);

假设连接模式不是DIRECT,调用plugins_call_handle_connection_close告诉插件连接已经关闭。

假设设置了keep_alive。此时可能是server首先关闭连接的。调用shutdown关闭连接的读和写。假设关闭没有出错,状态机进入CON_STATE_CLOSE状态。

假设没有设置keep_alive或者shutdown调用失败,那么直接关闭连接。结束状态机的执行。

            /* close the connection */
            if ((con->keep_alive == 1) &&
                (0 == shutdown(con->fd, SHUT_WR))) {
                con->close_timeout_ts = srv->cur_ts;
                connection_set_state(srv, con, CON_STATE_CLOSE);

                if (srv->srvconf.log_state_handling) {
                    log_error_write(srv, __FILE__, __LINE__, "sd",
                            "shutdown for fd", con->fd);
                }
            } else {
                connection_close(srv, con);
            }

            con->keep_alive = 0;

            srv->con_closed++;

注意到。这里server主动关闭连接的时候用的是shutdown而不是close:

1.close使用引用计数,在计数为0时才关闭套接字;shutdown无论引用计数,直接激发TCP的正常连接终止序列。

2.close终止读和写两个方向的数据传送。shutdown能够指定仅仅关闭连接的读,或仅仅关闭连接的写。或两者均关闭。

以上lighttpd是关闭了连接的写这一半,对于TCP套接字来说。这叫做半关闭:当前留在套接字发送缓冲区的数据仍然能够发送。可是进程不能再对其调用写函数(因为读端没有关闭,所以server仍然能够读数据),当数据发送完毕之后,TCP连接终止。

另外。注意一下:con->close_timeout_ts = srv->cur_ts;将close_timeout_ts的值设置为当前时间,在以下会用到。

在CON_STATE_CLOSE阶段:

        case CON_STATE_CLOSE:
            if (srv->srvconf.log_state_handling) {
                log_error_write(srv, __FILE__, __LINE__, "sds",
                        "state for fd", con->fd, connection_get_state(con->state));
            }

            if (con->keep_alive) {
                if (ioctl(con->fd, FIONREAD, &b)) {
                    log_error_write(srv, __FILE__, __LINE__, "ss",
                            "ioctl() failed", strerror(errno));
                }
                if (b > 0) {
                    char buf[1024];
                    log_error_write(srv, __FILE__, __LINE__, "sdd",
                            "CLOSE-read()", con->fd, b);

                    /* */
                    read(con->fd, buf, sizeof(buf));
                } else {
                    /* nothing to read */

                    con->close_timeout_ts = 0;
                }
            } else {
                con->close_timeout_ts = 0;
            }

            if (srv->cur_ts - con->close_timeout_ts > 1) {
                connection_close(srv, con);

                if (srv->srvconf.log_state_handling) {
                    log_error_write(srv, __FILE__, __LINE__, "sd",
                            "connection closed for fd", con->fd);
                }
            }

            break;

假设缓冲区中还有数据,server会把数据读出来(然后丢弃),以腾出内存空间。

假设没有数据可读,那么设置close_timeout_ts=0,关闭连接。

假设有数据可读,读取数据之后,连接依旧处在CON_STATE_CLOSE状态中(在出了CON_STATE_ERROR后。进入CON_STATE_CLOSE,这段时间cur_ts是没有改变的。假设有数据可读,此时const_time_ts是等于cur_ts的,因此连接并未被关闭),连接相应的fd被增加到fdevent系统中监听读事件。

假设缓冲区中还有数据,那么在connection_handle_fdevent 函数中,也有上面这段代码,再次执行之,直到数据读完。

随着close_timeout_ts被设置为0,在下次joblist的调度中,状态机将会关闭连接,清理全部资源。

至此,连接正式关闭。

关于状态机的简单解析就到此为止~

时间: 2024-10-13 08:01:28

Lighttpd1.4.20源代码分析 笔记 状态机之错误处理和连接关闭的相关文章

Lighttpd1.4.20源码分析 笔记 状态机之错误处理和连接关闭

这里所说的错误有两种: 1.http协议规定的错误,如404错误: 2.服务器运行过程中的错误,如write错误. 对于http协议规定的错误,这里的"错误"是针对客户端的.lighttpd返回相应的错误提示文件之后,相当于顺利的完成了一次请求,只是结果和客户端想要的不一样而已. 对于服务器运行中的错误,状态机进入CON_STATE_ERROR状态.常见的错误原因:客户端提前断开连接.比如你不停的刷新页面,在你刷新的时候,前一次的连接没有完成,但被浏览器强行断开.对于服务器而言,刷新前

Lighttpd1.4.20源码分析 笔记 状态机之response

在CON_STATE_RESPONSE_START状态中,服务器开始准备给客户端的response: case CON_STATE_RESPONSE_START: /* * the decision is done * - create the HTTP-Response-Header * */ if (srv->srvconf.log_state_handling) { log_error_write(srv, __FILE__, __LINE__, "sds", "

kbengine0.4.20源代码分析(一)

基于kbengine 0.4.2 MMOPG服务端是一种高品质的工程项目,品读开源的kbe是一种乐趣.本文档我带童鞋们一起领略一下.囿于我知识面和经验方面所限,文中所述之处难免有错误存在,还请读童鞋们睁大慧眼,如果你发现错误,可以 电邮至[email protected].(因为我个人懒散或者时间仓促的关系,这个文档的排版有点小乱...) 其他牛逼哄哄的前言就不说了. 从理论上来讲,我们阅读一份源代码,首先应该基于现有的文档从整体上把握项目的架构之后再庖丁解牛一般地细分阅读,不过在我写这个文档的

wifidog认证自带http服务器Lighttpd1.4.20源码分析之状态机返回response

前一篇介绍完了请求的处理,先面lighttpd将会把处理的结果返回给客户端.状态机进入CON_STATE_RESPONST_START.在这个状态中,服务器主要的工作在函数connection_handle_write_prepare.这个函数不算复杂,主要是根据客户端请求的method来设置response的headers,其实就是设置“Content-Length”的值.下面是函数代码,做了一些删减: static int connection_handle_write_prepare(se

转:RTMPDump源代码分析

0: 主要函数调用分析 rtmpdump 是一个用来处理 RTMP 流媒体的开源工具包,支持 rtmp://, rtmpt://, rtmpe://, rtmpte://, and rtmps://.也提供 Android 版本. 最近研究了一下它内部函数调用的关系. 下面列出几个主要的函数的调用关系. RTMPDump用于下载RTMP流媒体的函数Download: 用于建立网络连接(NetConnect)的函数Connect: 用于建立网络流(NetStream)的函数 rtmpdump源代码

竖屏小游戏--喵星战争源代码分析【完整】

 转载请注明出处:http://blog.csdn.net/oyangyufu/article/details/26942311 源代码地址:http://download.csdn.net/detail/oyangyufu/7409593 Ccp文件介绍: GameMenuScene.cpp游戏主菜单 GameMainScene.cpp游戏主页面 GameObjHero.cpp主角 GameHeroBullet.cpp主角的子弹 GameObjEnemy.cpp敌人 GameEnemyBull

Raid1源代码分析--读流程

我阅读的代码的linux内核版本是2.6.32.61.刚进实验室什么都不懂,处于摸索阶段,近期的任务就是阅读raid1的源码.第一次接触raid相关的东西,网上分析源码的资料又比较少,不详细.逐行阅读代码,做了笔记.如果要对raid1的读流程有个整体上的把握,需要将笔记中的主线提炼出来,这里不写了.理解不足或者有误之处,希望批评指正. 读流程主要涉及以下函数: 请求函数make_request 读均衡read_balance 回调函数raid1_end_read_request 读出错处理rai

STL源代码分析——STL算法sort排序算法

前言 因为在前文的<STL算法剖析>中,源代码剖析许多,不方便学习,也不方便以后复习.这里把这些算法进行归类,对他们单独的源代码剖析进行解说.本文介绍的STL算法中的sort排序算法,SGI STL中的排序算法不是简单的高速排序,而是交叉利用各种排序:堆排序.插入排序和高速排序:这样做的目的是提高效率.针对数据量比較大的採用高速排序,数据量比較小的能够採用堆排序或插入排序. 本文介绍了有关排序的算法random_shuffle.partition.stable_partition.sort.s

HBase源代码分析之HRegion上MemStore的flsuh流程(二)

继上篇<HBase源代码分析之HRegion上MemStore的flsuh流程(一)>之后.我们继续分析下HRegion上MemStore flush的核心方法internalFlushcache().它的主要流程如图所看到的: 当中.internalFlushcache()方法的代码例如以下: /** * Flush the memstore. Flushing the memstore is a little tricky. We have a lot of updates in the