对于主函数而言,归纳来讲主要作了三点内容,也就是初始化系统,进行系统大循环,退出系统。下面主要简单阐述下在这三个部分,又作了哪些工做呢。html
拿出程序的名字(argv[0])用来做为参数打开那个log(syslog)web
解析命令行的参数(parse_args),初始化内部的参数变量api
检查当前主机名(addr) 没有的话利用gethostbyname从hostname中获取数组
检查当前要使用的主机端口(port)缓存
读取Throttle file(门限文件,这里省略)安全
检查logfile的值,有的话就建立一个logfp咯服务器
得到系统用户的相关信息(getpwnam),使用的系统用户为nobody(安全),记录下uid,gid值cookie
切换程序的工做空间为参数中的dir值数据结构
得到当前工做目录(保证以'/'结尾)app
调用daemon函数进入后台工做
查看pidfile,若是pidfile不为空则打开该文件,写入pid值
根据参数选择是否chroot,(chroot的缘由见这个连接)
设置信号处理函数signal(处理SIGTERM SIGINT SIGPIPE SIGHUP SIGUSR1)
初始化http处理模块(调用该模块init函数)
设置一个occasional timer用于时不时的清除定时器模块和mmc模块的无用内存,若是有须要的话,设置一个status timer,用于记录状态
为了安全,放弃root权限,变成nobody(使用setgroups和setgid,setuid函数族)
利用fdwatch包装的api,得到最多能够复用的fd数
建立一个链接池(数组)每一个链接的数据结构以下,并完成初始化操做。
typedef struct {
int conn_state; //链接状态
httpd_conn* hc; //用户信息
int tnums[MAXTHROTTLENUMS]; /* throttle indexes /
int numtnums; //
long limit; //
time_t started_at; //起始时间
Timer idle_read_timer; //空闲读取定时器
Timer* idle_send_timer; //空闲发送定时器
Timer* wakeup_timer; //苏醒定时器
Timer* linger_timer; //
long wouldblock_delay; //
off_t bytes; //****
off_t bytes_sent; //发送的数据
off_t bytes_to_send; //还须要发送的数据
} connecttab;
得到当前的时间,开始大循环
循环的条件时 terminate != 0 || numconnects > 0
循环的内容是:
新来的链接处理
首先,保证链接数不大于最大的链接数
接着,找到链接池中的最靠前的free链接,新建一个用户数据结构,代表为未初始化;
调用http模块的httpd_get_conn函数,初始化该用户信息,而后填充些链接信息到数据结构中,开启read定时器;
设置该链接为非阻塞的链接;
fd可读时的处理
首先,看是否有更多的空间来存取用户的请求数据,若是没有的话,给read_buf增长空间,每次1000字节,5000封顶;
而后,从链接中读取数据;
判断当前读入的数据是否能构成一个合理的http request;
若是能够的话,进行http解析请求;
设置须要给用户放回的数据;
设置链接的状态为SENDING,中止该链接的读定时,开启该用户的写定时;
fd可写时的处理
fd须要linger时的处理
若是有数据直接读取数据扔掉
在httpd模块中,定义了两个核心数据结构,服务器数据(http_server)和用户链接数据(httpd_conn)。
服务器的数据结构的定义分别以下:
/* A server. */ typedef struct { char* hostname; //主机名(ex:localhost) struct in_addr host_addr; //主机地址 int port; //端口号 char* cgi_pattern; //cgi样式 char* cwd; //当前工做路径 int listen_fd; //监听套接字 FILE* logfp; //log文件描述符 int no_symlinks; //有无符号链接标志 int vhost; //虚拟主机标志 } httpd_server;
下面是链接的数据结构:
/* A connection. */ typedef struct { int initialized; //初始化标志 httpd_server* hs; //服务器结构地址 struct in_addr client_addr; //客户端地址 char* read_buf; //读缓存 int read_size, read_idx, checked_idx; //缓存标志位 int checked_state; //检测状态标志 int method; //请求方法标志 int status; //当前链接状态 off_t bytes; char* encodedurl; //encode后的url char* decodedurl; //decode后的url char* protocol; //http协议类型 char* origfilename; //原来的文件名 char* expnfilename; //扩展后的文件名 char* encodings; char* pathinfo; char* query; char* referer; char* useragent; char* accept; char* accepte; char* cookie; char* contenttype; char* reqhost; char* hdrhost; char* authorization; char* remoteuser; char* response; //发送缓存 int maxdecodedurl, maxorigfilename, maxexpnfilename, maxencodings,maxpathinfo, maxquery, maxaccept, maxaccepte, maxreqhost,maxremoteuser, maxresponse; #ifdef TILDE_MAP_2 char* altdir; int maxaltdir; #endif int responselen; time_t if_modified_since, range_if; off_t contentlength; char* type; /* not malloc()ed */ char* hostname; /* not malloc()ed */ int mime_flag; int one_one; /* HTTP/1.1 or better */ int got_range; int tildemapped; /* this connection got tilde-mapped */ off_t init_byte_loc, end_byte_loc; int keep_alive; int should_linger; struct stat sb; int conn_fd; char* file_address; } httpd_conn;
该模块提供的函数接口有:
//初始化http server数据结构 extern httpd_server* httpd_initialize( char* hostname, u_int addr, int port, char* cgi_pattern, char* cwd, FILE* logfp, int no_symlinks, int vhost ); //改变http server结构中的logfp extern void httpd_set_logfp( httpd_server* hs, FILE* logfp ); //清除http server结构 extern void httpd_terminate( httpd_server* hs ); //当有一个新链接来临时,接收这个链接,并将该链接http client初始化 extern int httpd_get_conn( httpd_server* hs, httpd_conn* hc ); //根据链接hc的read_buf中的内容,判断当前接收的数据是不是一个完成的http请求,并返回对应结果 extern int httpd_got_request( httpd_conn* hc ); //解析上述的http请求,并把解析后的值放入hc对应的数据单元中 extern int httpd_parse_request( httpd_conn* hc ); //准备须要向客户端发送的数据 extern int httpd_start_request( httpd_conn* hc ); //把hc中response中的内容写给用户 extern void httpd_write_response( httpd_conn* hc ); //关闭一个链接并释放链接的空间 extern void httpd_close_conn( httpd_conn* hc, struct timeval* nowP ); //释放hc中全部的空间 extern void httpd_destroy_conn( httpd_conn* hc ); //向客户端发送一个错误信息 extern void httpd_send_err( httpd_conn* hc, int status, char* title, char* form, char* arg ); //根据method号找到method内容 extern char* httpd_method_str( int method ); //从新分配一段string extern void httpd_realloc_str( char** strP, int* maxsizeP, int size );
其中,系统操做这个httpd模块则能够分为以下几部进行理解。
使用对应的接口进行httpd模块的初始化,对应http server的初始化采用httpd_initialize接口,初始化好了以后就在对应的端口上进行监听套接字;
当监听的套接字可读以后,就可使用httpd_get_conn函数,accept该用户,并开辟一个用户的httpd_conn结构,并该结构利用已有的信息进行初始化;
接着当该用户的套接字可读时,又将会去调用httpd_got_request接口,该接口将会去将套接字上的数据读到hc结构中的read_buf中去,而后对于read_buf中的数据进行检测,查看收到的数据是否能构成一个完整的http请求;
若是接收到的确实是一个完整的http请求,就会去调httpd_parse_request接口,对read_buf中的数据进行解析,并将http头中解析到的字段(如method,url等)放入hc结构体中。
当数据都解析完成后,系统将会调用httpd_start_request接口来准备须要回复给用户的数据,这个数据的准备是根据解析到的具体状况来进行处理的,有可能就是一个index.html文件,而有可能就是在hc的response中放了一些错误信息。
而准备好要发送的数据以后,就能够设置链接的状态为SENDING,这样下次select后就会对于该链接调用handle_send函数,将数据发送出去,并关闭链接。
再具体的说的话,能够看到thttpd预先开辟了大约1024个conn_tab结构,这里的conn_tab指的是链接的具体信息,其数据结构核心数据以下:
typedef struct { int conn_state; httpd_conn* hc; //has to define long limit; time_t started_at; int numtnums; Timer* idle_read_timer; Timer* idle_send_timer; Timer* wakeup_timer; Timer* linger_timer; off_t bytes; off_t bytes_sent; off_t bytes_to_send; } conn_tab;
其中包含了链接的状态conn_state,内嵌了具体的用户信息hc,发送限制limit,开始时间started_at,四个定时器(读定时,写定时,链接苏醒定时,链接保持定时),和链接当前要发送的字节数bytes_to_send,已经发送的字节bytes_sent,这里的bytes好像没有啥重要意义;
对于接收到的一个用户而言,则按照上述的httpd_conn结构的定义,则初始化的时候须要考虑以下内容,init初始化标志,hs服务器结构地址,本身的client地址,而后就是读取信息须要的read_buf和用于其标记的idx和checked状态位,而后就是解析所须要的method, encodedurl, decodeurl, protocol, origfilename, expnfilename, encodings, pathinfo, query, referer, useragent, accept, accepete, cookie, contenttype, reqhost, hdrhost, quthorization, remoteuser, 及其最大字符长度,最后就是返回须要的response_buf,file_address, contentlength,还有些标志位信息如mimeflag,http1.1标志one_one,keep_alive标志,should_linger标志以及got_range标志。
这里以一个普通的GET请求为例,讲一下http_conn中各字段的值分别是什么。
HTTP REQUEST: GET /index.html?a=1 HTTP/1.1
解析完成后:
method 1(GET) protocol HTTP/1.1 reqhost "" encodedurl /index.html?a=1(可能有16进制数) decodefurl /index.html?a=1(没有16进制) origfilename index.html (url是"/"时设为“.”) expnfilename index.html (没有符号连接,且证实了文件存在性) pathinfo "" query a=1 accept text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8 accepte gzip, deflate, sdch remoteuser "" response 存放着http头部信息p referer "" useragent Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.101 Safari/537.36 cookie "" contenttype "" hdrhost 127.0.0.1 authorization "" id_modified_since -1 range_if -1 contentlength -1 got_range 0 init_byte_loc 0 end_byte_loc -1 keep_alive 1 should_linger 1 hostname NULL mime_flag 1 bytes 111(html文件的大小) file_address 文件内存地址 其中最后反馈的数据就是由response和file_address这里那个部分组成的。