如何實現HTTPSERVER

2022-02-18 23:28:52 字數 4491 閱讀 4333

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等檔案進行內容輸出前壓縮。我們...