天天看點

tinyhttpd源碼詳解

tinyhttpd是一輕量級的web 伺服器,最近幾天終于抽出時間來研究研究了。其源碼百度即可下載下傳,500多行,确實是學習linux程式設計的好材料。很多網友都寫了關于tinyhttpd的博文,但是我還是覺得不夠深入,嚴格說是寫得不夠深入,往往就是把500多行代碼一扔,分析下主要過程,畫個流程圖就完事了。我怎麼覺得還有很多東西可以挖一挖呢,也許還可再調整一下代碼,雖然目前也不清楚可調整多少,待我細細道來。

我分析的過程就按主要路線走,也就是這樣一個主幹道流程:伺服器建立socket并監聽某一端口->浏覽器輸入url送出請求->伺服器收到請求,建立線程處理請求,主線程繼續等待->新線程讀取http請求,并解析相關字段,讀取檔案内容或者執行cgi程式并傳回給浏覽器->關閉用戶端套接字,新線程退出

咱們先來看看main函數

這段代碼,隻要是稍微了解linux的網絡程式設計就很好懂,建立服務端socket,綁定、監聽、等待用戶端連接配接。隻不過作者把這些步驟都放在了一個叫startup的函數裡。那來看startup

很常見的步驟,就不多說了。

此後,服務端就accept等待連接配接,作者其實沒有關心用戶端來自哪裡,那accept的第二、第三參數完全可以為null。接着就建立線程把用戶端套接字作為參數傳過去了,由新線程處理請求,這是伺服器程式設計的常用手段,提高并發性。注意這裡的線程函數并不完全合法,至少在linux上就不符合線程函數的原型定義,編譯時編譯器也隻是警告而未報錯。

接下來重點就線上程函數accept_request上了

首先很關鍵一點要了解get_line的意思。我們要知道當在浏覽器中輸入url後enter之後,它發給伺服器是文本型的字元串,遵循http請求格式,類似下面的:

get / http/1.1

host:www.abc.com

content-type:text/html

...

get_line幹的事就是讀取一行,并且不管原來是以\n還是\r\n結束,均轉化為以\n再加\0字元結束。其實作如下:

get_line完後,就是開始解析第一行,判斷是get方法還是post方法,目前隻支援這兩種。如果是post,還是把cgi置1,表明要運作cgi程式;如果是get方法且附帶以?開頭的參數時,也認為是執行cgi程式

還是擷取要通路的url,可以是很常見的/,/index.html等等。該程式預設為根目錄是在htdocs下的,且預設檔案是index.html。另外還判斷了給定檔案是否有可執權限,如果有,則認為是cgi程式。最後根據變量cgi的值來進行相應選擇:讀取靜态檔案或者執行cgi程式傳回結果。

我們首先看看最簡單的靜态檔案情況,調用函數serve_file

将檔案名作為參數,首先讀完用戶端的頭部,然後打開建立檔案流。為了模拟http響應,首先向用戶端發送頭部,頭部資訊至少包含以下幾點:

http/1.0 200 ok

server:

content-type:

\r\n(一個空白行,辨別頭部結束)

最後發送資料體部分,即檔案内容,在cat方法中,fgets每讀入一行,就send,直到末尾。headers和cat函數就不在這裡列出了。下面,我們來看看一個具體測試例子,緊接着在gdb中調試

我在根目錄下的htdocs下建立一個新檔案index2.html,内容如下:

<a href="http://10.108.222.96:54205/test.sh">display date</a>

我在這裡放了一個連結,href部分是關于cgi的,先不管,就隻看文本部分能否顯示在浏覽器中。

首先編譯之後直接運作./httpd,程式列印"httpd running on port 53079"

我們在浏覽器中通路index2.html檔案,如下圖所示:

tinyhttpd源碼詳解

文本能正确顯示了。那如何在gdb中調試觀察呢?

前面已說過,tinyhttpd目前就支援兩種請求形式,純get請求或者帶?的get和直接post請求。了解到源碼htdocs目錄下的cgi都是perl寫的,不知讀者你懂不懂,反正部落客我不懂,是以就改一改,改成自己的需求,用shell寫。正如index2.html所示:

test.sh腳本如下:

#!/bin/sh 

#echo "content-type:text/html"

echo

echo "<html><head><meta charset="utf-8"><title>mytitle</title></head><body>"

time=`date`

echo "<p>server time:$time"

echo "</body></html>"

即包括伺服器響應給客戶的字元資料,順便把伺服器時間傳過去。注意要加test.sh添加執行權限,才會被視為執行cgi程式,且href中的端口号要改為你具體的端口号,這裡隻是個示例。來看當在浏覽器中點選“display date”時,伺服器作出的響應:

結果顯示:

tinyhttpd源碼詳解

當然我在這裡隻是示範了其中的一種情況,至于情況如get請求帶?查詢的,post請求帶資料體的,隻有靠讀者自己去嘗試了,部落客暫時抛磚引玉于此。

呃,感覺講解至此結束了呢。貌似還有一點點細節部落客還得繼續研究下,總之通過這個例子确實對linux程式設計了解了更多了,感謝開源,哈哈!

參考連結

1 http://blog.csdn.net/jcjc918/article/details/42129311

2 http://blog.sina.com.cn/s/blog_a5191b5c0102v9yr.html

3 cgi介紹:http://www.jdon.com/idea/cgi.htm

4 http://www.scholat.com/vpost.html?pid=7337

繼續閱讀