博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Lighttpd1.4.20源代码分析 笔记 状态机之错误处理和连接关闭
阅读量:6504 次
发布时间:2019-06-24

本文共 4231 字,大约阅读时间需要 14 分钟。

这里所说的错误有两种:

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的调度中,状态机将会关闭连接,清理全部资源。

至此,连接正式关闭。

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

转载于:https://www.cnblogs.com/yutingliuyl/p/7365725.html

你可能感兴趣的文章
mysql中int、bigint、smallint 和 tinyint的区别与长度的含义
查看>>
Mina框架与Spring整合配置文件
查看>>
MongoDB 安装 Windows XP
查看>>
在Ubuntu上为Android系统编写Linux内核驱动程序(老罗学习笔记1)
查看>>
如何在Ubuntu下安装”.deb“、”.bin“、”.tar.gz“、”.tar.bz2“格式的软件包!...
查看>>
2015 UESTC 数据结构专题D题 秋实大哥与战争 SET的妙用
查看>>
在 Java SE 6 中监视和诊断性能问题
查看>>
android 深入研究ratingbar自定义
查看>>
Javascript 笔记与总结(2-5)window 对象
查看>>
OC开发_代码片段——代码编写简单的tableViewCell
查看>>
(五) 一起学 Unix 环境高级编程 (APUE) 之 进程环境
查看>>
SQL Server 2012:SQL Server体系结构——一个查询的生命周期(第3部分)(完结)...
查看>>
ActionScript3游戏中的图像编程(连载十七)
查看>>
《3D Math Primer for Graphics and Game Development》读书笔记2
查看>>
【转】Android 带checkbox的listView 实现多选,全选,反选----解决checkbox错位问题
查看>>
乾坤合一~Linux设备驱动之I2C核心、总线以及设备驱动
查看>>
Linux 下开启ssh服务(转)
查看>>
tomcat内存设置
查看>>
Winform基础
查看>>
RS特殊报表样式需求处理
查看>>