對於主函數而言,歸納來講主要作了三點內容,也就是初始化系統,進行系統大循環,退出系統。下面主要簡單闡述下在這三個部分,又作了哪些工做呢。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這裏那個部分組成的。