lighttpd-1.4.39 : connection

http://www.cnblogs.com/kernel_hcy/archive/2010/03/24/1694203.html

前面讲了lighttpd是怎样使用fdevent系统的,以及监听socket的处理过程。这一篇我们来看一看lighttpd是怎样处理连接socket的。
首先,我们来看看lighttpd是怎样建立和客户端的连接的。前面在讲监听socket的处理过程中其实已经讲解了连接的建立过程。lighttpd监测监听socket的IO事件,如果有可读事件发生,那么表示有新的连接请求,然后调用network.c/network_server_handle_fdevent()来处理连接请求。network_server_handle_fdevent()函数调用connections.c/connection_accept() 接受客户端的请求,建立连接。在建立连接的同时,就得到了连接socket的fd,也就是accept函数的返回值。
建立连接之后,这个连接对应的状态机的状态被设置为CON_STATE_REQUEST_START,就是开始读取客户端发过来的request信息。在从connection_accept函数跳回network_server_handle_fdevent()函数的for循环中后,程序紧接着就调用了一次connection_state_machine()函数,这个函数是根据当前连接的状态机的状态设置状态机的下一个状态,CON_STATE_REQUEST_START的下一个状态是CON_STATE_READ,这个状态表示连接正在读取客户端发送的数据。当连接的状态机被设置成CON_STATE_READ后,在connection_state_machine()函数的最后,有这样一个switch语句:

switch (con->state)
    {
    case CON_STATE_READ_POST:
    case CON_STATE_READ:
    case CON_STATE_CLOSE:
        fdevent_event_add(srv->ev, &(con->fde_ndx), con->fd, FDEVENT_IN);
        break;
    case CON_STATE_WRITE:
        /*
         * request write-fdevent only if we really need it
         * - if we have data to write
         * - if the socket is not writable yet
         */
        if (!chunkqueue_is_empty(con->write_queue) && (con->is_writable == 0) &&(con->traffic_limit_reached == 0))
        {
            fdevent_event_add(srv->ev, &(con->fde_ndx), con->fd, FDEVENT_OUT);
        }
        else
        {
            fdevent_event_del(srv->ev, &(con->fde_ndx), con->fd);
        }
        break;
    default:
        fdevent_event_del(srv->ev, &(con->fde_ndx), con->fd);
        break;
    }

上面这个switch语句将状态处在CON_STATE_READ_POST,CON_STATE_READ和CON_STATE_CLOSE的连接对应的连接socket fd加入到fdevent系统中,并监听可读事件。将处CON_STATE_WRITE状态且有数据要写的连接对应的socket fd加入到fdevent系统中,并监听可写事件。其他状态的连接则把对应的fd从fdevent系统中删除,因为这些连接不会有IO事件发生。
这样,连接socket fd就被加入到了fdevent系统中。下面就是等待IO事件的发生。程序在前面已经提到过,如下:

if ((n = fdevent_poll(srv->ev, 1000)) > 0)
        {
            int revents;
            int fd_ndx;
            fd_ndx = -1;
            do
            {
                fdevent_handler handler;
                void *context;
                handler_t r;
                fd_ndx = fdevent_event_next_fdndx(srv->ev, fd_ndx);
                revents = fdevent_event_get_revent(srv->ev, fd_ndx);
                fd = fdevent_event_get_fd(srv->ev, fd_ndx);
                handler = fdevent_get_handler(srv->ev, fd);
                context = fdevent_get_context(srv->ev, fd);
                switch (r = (*handler) (srv, context, revents))
                {
                case HANDLER_FINISHED:
                case HANDLER_GO_ON:
                case HANDLER_WAIT_FOR_EVENT:
                case HANDLER_WAIT_FOR_FD:
                    break;
                case HANDLER_ERROR:
                    /*
                     * should never happen
                     */
                    SEGFAULT();
                    break;
                default:
                    log_error_write(srv, __FILE__, __LINE__, "d", r);
                    break;
                }
            }while (--n > 0);
        }

这段程序在前面已经讲解过。对于fdevent系统,它不关心自己处理的fd是连接fd还是监听fd,它所做的就是对于发生了这个fd所希望的IO事件以后,调用这个fd对应的处理函数处理IO事件。连接fd对应的处理函数是connections.c/connection_handle_fdevent()函数。函数的代码如下:

handler_t connection_handle_fdevent(void *s, void *context,int revents)
{
    server *srv = (server *) s;
    connection *con = context;
    //把这个连接加到作业队列中。
     joblist_append(srv, con);
    if (revents & FDEVENT_IN)
    {
        con->is_readable = 1;
    }
    if (revents & FDEVENT_OUT)
    {
        con->is_writable = 1;
        /*
         * we don't need the event twice
         */
    }
    if (revents & ~(FDEVENT_IN | FDEVENT_OUT))
    {
        /*
         * looks like an error 即可读又可写,可能是一个错误。
         */
        /*
         * FIXME: revents = 0x19 still means that we should read from the queue
         */
        if (revents & FDEVENT_HUP)
        {
            if (con->state == CON_STATE_CLOSE)
            {
                con->close_timeout_ts = 0;
            }
            else
            {
                /*
                 * sigio reports the wrong event here there was no HUP at all
                 */
                connection_set_state(srv, con, CON_STATE_ERROR);
            }
        }
        else if (revents & FDEVENT_ERR)
        {
            connection_set_state(srv, con, CON_STATE_ERROR);
        }
        else
        {
            log_error_write(srv, __FILE__, __LINE__, "sd","connection closed: poll() -> ???", revents);
        }
    }
    if (con->state == CON_STATE_READ|| con->state == CON_STATE_READ_POST)
    {
        connection_handle_read_state(srv, con);
 //继续读取数据,直到数据读取完毕
     }
 // 数据的写回并没有放给状态机去处理。
     if (con->state == CON_STATE_WRITE&& !chunkqueue_is_empty(con->write_queue) && con->is_writable)
    {
        if (-1 == connection_handle_write(srv, con))
        {
            connection_set_state(srv, con, CON_STATE_ERROR);
            log_error_write(srv, __FILE__, __LINE__, "ds", con->fd,"handle write failed.");
        }
        else if (con->state == CON_STATE_WRITE)
        {
            //写数据出错,记录当前时间,用来判断连接超时。
             con->write_request_ts = srv->cur_ts;
        }
    }
    if (con->state == CON_STATE_CLOSE)
    {
        /*
         * flush the read buffers 清空缓冲区中的数据。
         */
        int b;
        //获取缓冲区中数据的字节数
         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;
        }
    }
    return HANDLER_FINISHED;
}

可以看到,connection_handle_fdevent()函数根据当前连接fd所发生的IO事件,对connection结构体中的标记变量赋值,如is_writable,is_readable等,并做一些时间的记录。这些事件所对应的真正的IO处理则交给状态机处理。状态机根据这些标记变量进行相应的动作处理。
这样,对于fdevent系统对于一次连接fd的IO事件就处理结束了。当然,真正的处理工作是由状态机来完成。下面的图简要的描述了fdevent系统对连接fd和监听fd的处理:
这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值