why write your own?看這個問題的人證明你知道什麼是http server,世界上有很多各種規模的http server,為什麼要自己實現乙個?其實沒什麼
理由。我自己問自己,感覺就是在自己娛樂自己,或者說只是練習下網路程式設計,或者是因為某日我看到某個庫宣稱自己附帶乙個小
型的http server時,我不知道是什麼東西,於是就想自己去實現乙個。
httpd就是http daemon,這個是類unix系統上的名稱,也就是http server。httpd遵循http協議,響應http客戶端的request,
然後返回response。
那麼,什麼是http協議?最簡單的例子,就是你的瀏覽器與網頁伺服器之間使用的應用層協議。雖然官方文件說http協議可以
建立在任何可靠傳輸的協議之上,但是就我們所見到的,http還是建立在tcp之上的。
httpd最簡單的response是返回靜態的html頁面。在這裡我們的目標也只是乙個響應靜態網頁的httpd而已(也許你願意加入cgi
特性)。
在這裡有必要講解http協議的更多細節,因為我們的httpd就是要去解析這個協議。
關於http協議的詳細文件,可以參看rfc2616。但事實上對於實現乙個簡單的響應靜態網頁的httpd來說,完全沒必要讀這麼一
分冗長的文件。在這裡我推薦
,以下內容基本取自於本文件。
-http協議結構
http協議無論是請求報文(request message)還是回應報文(response message)都分為四部分:
*報文頭 (initial line )
*0個或多個header line
*空行(作為header lines的結束)
*可選body
http協議是基於行的協議,每一行以\r\n作為分隔符。報文頭通常表明報文的型別(例如請求型別),報文頭只佔一行;header line
附帶一些特殊資訊,每乙個header line佔一行,其格式為name:value,即以分號作為分隔;空行也就是乙個\r\n;可選body通常
包含資料,例如伺服器返回的某個靜態html檔案的內容。舉個例子,以下是乙個很常見的請求報文,你可以截獲瀏覽器傳送的資料
包而獲得:
7我為每一行都新增了行號,第1行就是initial line,
2-6行是header lines,7行是乙個header line的結束符,沒有顯示出來。
以下是乙個回應報文:
第6行就是可選的body,這裡是index.html這個檔案的內容。
因為我們做的事伺服器端,所以我們重點對請求報文做說明。首先看initial line,該行包含幾個字段,每個欄位用空格分開,例
如以上的get /index.html http/1.1就可以分為三部分:get、/index.html、http/1.1
。其中第乙個欄位get就是所謂的request
method。它表明請求型別,http有很多method,例如:get、post、head等。
就我們的目標而言,我們只需要實現對get和head做響應即可。
get是最普遍的method,表示請求乙個資源。什麼是資源?諸如html網頁、、聲音檔案等都是資源。順便提一句,http協議
中為每乙個資源設定乙個唯一的識別符號,就是所謂的uri(更寬泛的url)。
head與get一樣,不過它不請求資源內容,而是請求資源資訊,例如檔案長度等資訊。
-more detail
繼續說說initial line後面的內容:
對應於get和head兩個method,緊接著的字段就是資源名,其實從這裡可以看出,也就是檔名(相對於你伺服器的資源目錄),例
如這裡的/index.html;最後乙個字段表明http協議版本號。目前我們只需要支援http1.1和1.0
,沒有多大的技術差別。
然後是header line。我們並不需要關注每乙個header line。我只羅列有用的header line :
-host : 對於http1.1而言,請求報文中必須包含此header,如果沒有包含,伺服器需要返回bad request錯誤資訊。
-date : 用於回應報文,用於客戶端快取資料用。
- content-type : 用於回應報文,表示回應資源的檔案型別,以mime形式給出。什麼是mime?它們都有自己的格式,例如:
text/html, image/jpg, image/gif等。
- content-length : 用於回應報文,表示回應資源的檔案長度。
body域很簡單,你只需要將乙個檔案全部讀入記憶體,然後附加到回應報文段後傳送即可,即使是二進位制資料。
-回應報文
之前提到的乙個回應報文例子很典型,我們以其為例講解。首先是initial line,第乙個字段表明http協議版本,可以直接以請求
報文為準(即請求報文版本是多少這裡就是多少);第二個欄位是乙個status code,也就是回應狀態,相當於請求結果,請求結果
被http官方事先定義,例如200表示成功、404表示資源不存在等;最後乙個欄位為status code的可讀字串,你隨便給吧。
回應報文中最好跟上content-type、content-length等header。
具體實現
正式寫**之前我希望你能明白http協議的這種請求/回應模式,即客戶端發出乙個請求,然後伺服器端回應該請求。然後繼續
這個過程(http1.1是長連線模式,而http1.0是短連線,當伺服器端返回第乙個請求時,連線就斷開了)。
這裡,我們無論客戶端,例如瀏覽器,發出什麼樣的請求,請求什麼資源,我們都回應相同的資料:
/*阻塞地接受乙個客戶端連線
*/socket con = accept( s, 0, 0
);
/*recv request
*/char request[1024] = ;
ret = recv( con, request, sizeof( request ), 0
); printf( request );
/*whatever we recv, we send 200 response
*/ closesocket( con );
程式以最簡單的阻塞模式執行,我們可以將重點放在協議的分析上。執行程式,在瀏覽器裡輸入http:
//localhost:8080/index.html
,然後就可以看到瀏覽器正常顯示content中描述的html檔案。假設程式在8080埠監聽。
現在你基本上明白了整個工作過程,我們可以把**寫得更全面一點,例如根據get的uri來載入對應的檔案然後回應給客戶端。
其實這個很簡單,只需要從initial line裡解析出(很一般的字串解析)uri欄位,然後載入對應的檔案即可。例如以下函式:
}} 其他
要將這個簡易的httpd做完善,我們還需要注意很多細節。包括:對不支援的method返回501錯誤;對於http1.1要求有host這個
header;為了支援客戶端cache,需要新增date header;支援head請求等。
對**做簡要的說明:
evbuffer.h/buffer.c : 取自libevent的buffer,用於快取資料;
klhttp-internal.h/klhttp-internal.c :主要用於處理/解析http請求,以及建立回應報文;
klhttp-netbase.h/klhttp-netbase.c :對socket api的乙個簡要封裝,使用select模型;
klhttp.h/klhttp.c :庫的最上層,應用層主要與該層互動,這一層主要集合internal和netbase。
test_klhttp.c :乙個測試例子。
http server 搭建攻略
搭建http server具體流程 1.啟動httpd服務 systemctl restart httpd 2.關閉setenforce 0 3.關閉防火牆 iptables f 4.mv etc httpd conf.d welcome.conf etc httpd conf.d welcome....
http server單向認證
1 客戶端認證伺服器 2 伺服器不認證客戶端 3 伺服器的證書使用openssl自簽名證書 我們使用server.crt就可以當做ca證書 1 建立http server 2 啟動http server,啟動時要載入自己的證書,啟動時使用tls 編寫處理邏輯 2 啟動http server,啟動時要...
GZIP壓縮 HTTP Server優化
gzip壓縮 將網路傳輸的內容進行壓縮後再傳遞,可以有效的減輕傳輸負擔,從而提公升http請求的相應速度。修改.htaccess檔案,具體如下 在上述配置內容中,我們使用apache deflate模組開啟壓縮,通過配置檔案型別過濾器,對.html xml css js等檔案進行內容輸出前壓縮。我們...