Liso源码阅读笔记
本文介绍了CMU 15-441的课程项目Liso的一个实现。主要介绍了请求流程、Client状态机模型、Dynamic Buffer数据结构等等。SSL相关的部分没有涉及。 实现 我看的是这位同学的实现。 一个HTTP静态文件请求的流程 首先我们拿一个简单的静态文件请求来梳理一下Liso的流程。在liso.c的main函数中,有这个服务器的主循环,这个循环每次做的事情就是用select来查询I/O事件。select之后,我们首先做的是查看listenfd是不是有read事件,如果有就accept这个连接。然后把连接加入到连接池里。一个连接的初始状态是READY_FOR_READ。 listenfd是连接池中的第一个item,也是一直存在于连接池中的一个描述符。 在循环的最后,我们会调用handle_clients对连接池中可读和可写的描述符依次进行处理。具体的方式就是遍历连接池,如果一个连接是可读的,并且状态是READY_FOR_READ,我们就读取这个连接中的数据。 每次从每个连接读取的数据是固定的长度,比如4KB。这是为了控制I/O的粒度,不让server在一个连接上花太多时间,使得新的连接请求被阻塞。 我们把收到的数据,放到client_buffer里面。然后看一下HTTP的Header是不是已经读取完全了(调用handle_recv_header判断读取到的数据中是否有"\r\n\r\n")。如果HTTP的header还没有读取完全,那就继续留在READY_FOR_READ状态,等待下一次循环。 如果Header已经读取完全了,我们就调用handle_http_request处理请求,在处理请求时,我们把要返回的数据写在client_buffer里,然后调整这个client的state到READY_FOR_WRITE。 在handle_clients的循环中,如果我们发现一个client的状态是READY_FOR_WRITE,我们就会把client_buffer里相关的数据写到客户端,然后根据这个连接的ConnectionHeader选择关闭或者保留这个连接。 这就是一个简单的HTTP静态文件请求的流程。 连接池的数据结构 typedef struct { /* Client pool global data */ fd_set master; /* all descritors */ fd_set read_fds; /* all ready-to-read descriptors */ fd_set write_fds; /* all ready-to_write descriptors */ int maxfd; /* maximum value of all descriptors */ int nready; /* number of ready descriptors */ int maxi; /* maximum index of available slot */ /* Client specific data */ int client_fd[FD_SETSIZE]; /* client slots */ dynamic_buffer * client_buffer[FD_SETSIZE]; /* client's dynamic-size buffer */ dynamic_buffer * back_up_buffer[FD_SETSIZE]; /* store historical pending request */ size_t received_header[FD_SETSIZE]; /* store header ending's offset */ char should_be_close[FD_SETSIZE]; /* whether client should be closed when checked */ client_state state[FD_SETSIZE]; /* client's state */ char *remote_addr[FD_SETSIZE]; /* client's remote address */ /* SSL related */ client_type type[FD_SETSIZE]; /* client's type: HTTP or HTTPS */ SSL * context[FD_SETSIZE]; /* set if client's type is HTTPS */ /* CGI related */ int cgi_client[FD_SETSIZE]; } client_pool; 每个socket连接在建立之后都会被放到client_pool里面。连接池在概念上就是一个大的数组。实现的时候,我们把每个连接相关的属性各自设置为一个相同大小的数组。每个item的属性就是属性数组中相应index的值。比如我们可以通过client_pool->client_fd[i]拿到第i个请求的fd。其他属性也是类似的。...