咱們常常使用網頁,做爲開發人員咱們也部署過httpd服務器,好比開源的apache,也開發過httpd後臺服務,好比fastcgi程序,不過對於httpd服務器內部的運行機制,卻不是很是瞭解,前幾天看見tinyhttpd,只有短短500行左右的代碼,就實現了一個簡單的httpd服務器的基本功能,這種tiny程序,其實對於咱們瞭解基本核心有必定的幫助,能夠經過分析其代碼,瞭解構建一個簡單的httpd服務器須要的基本元素。html
(1) startup函數apache
這個函數的代碼很是簡單,其實就是簡單建立一個server端的socket,讓它綁定指定的端口,而後開始listen瀏覽器
(2) accept_request函數服務器
此函數中首先解析客戶端的請求方式,是GET,仍是POST,tinyhttpd只能處理這2種請求,若是都不是,就返回錯誤。而後解析請求的url,而後對應到服務器中tinyhttpd中htdocs目錄下的文件,檢查文件狀態,若是文件不存在,那麼返回錯誤。若是文件存在,是GET方法時,tinyhttpd直接返回此文件,一般是html。若是是POST,那麼會執行對應的.cgi文件,tinyhttpd中的CGI文件,是用PERL腳本實現的。socket
(3) execute_cgi函數函數
此函數中就是如何執行CGI腳本的地方,首先仍是將POST部分的數據解析出來,而後fork一個子進程去執行CGI腳本,這裏有一個小的技巧,就是tinyhttpd使用了管道來讓執行CGI腳本的子進程,經過標準輸出寫入和父進程的管道中,父進程在fork以後,讀取此管道中的數據,而後返回給客戶端。ui
父進程中建立管道的代碼:url
if (pipe(cgi_output) < 0) { cannot_execute(client); return; } if (pipe(cgi_input) < 0) { cannot_execute(client); return; }
這裏建立了2個管道,每一個管道都有一個寫端,一個讀端。spa
子進程是經過下面代碼執行的:code
execl(path, path, NULL);
父進程中,讀取管道中的內容:
while (read(cgi_output[0], &c, 1) > 0) send(client, &c, 1, 0);
color.cgi代碼:
#!/usr/bin/perl -Tw use strict; use CGI; my($cgi) = new CGI; print $cgi->header; my($color) = "blue"; $color = $cgi->param('color') if defined $cgi->param('color'); print $cgi->start_html(-title => uc($color), -BGCOLOR => $color); print $cgi->h1("This is $color"); print $cgi->end_html;
單獨在控制檯下運行的結果是:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="en-US" xml:lang="en-US"> <head> <title>BLUE</title> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> </head> <body bgcolor="blue"> <h1>This is blue</h1> </body> </html>
也就是說,tinyhttpd經過子進程運行PERL的CGI腳本,而後腳本輸出了一個HTML,這個HTML內容經過父子進程之間的管道進程消息傳遞,父進程讀取管道中的內容發送給瀏覽器。
不過咱們從代碼中只看到了設置環境變量putenv的部分,若是是POST方法,並無看見設置參數的部分,其實這部分代碼,是在fork以後,parent中經過for循環,將content_length長的content經過管道write給了子進程的標準輸入,這也就是爲何在fork子進程以前先建立了2個管道的緣由。
這樣,整個tinyhttpd核心的內容就結束了。