1. 程式人生 > >基於HTTP協議實現的小型web伺服器

基於HTTP協議實現的小型web伺服器

我們先了解一下這個專案最終能達到的一個目標,然後以這個來進行專案的分析:
1、實現最基本的HTTP/1.0版本的web伺服器,客戶端能夠使用GET、POST方法請求資源
2、伺服器將客戶請求的資源以html頁面的形似呈現,並能夠進行差錯處理(如:客戶請求的資源不存在時,伺服器能夠返回一個404的頁面)
3、伺服器能進行簡單的cgi執行。比如當客戶在表單中輸入資料後,伺服器能夠將執行結果返回個客戶
4、能夠通過頁面對資料庫進行操作,如增刪查改等操作

一、http伺服器實現的基本框架

  • 關於HTTP協議
    即超文字傳輸協議,是網際網路上應用最廣泛的網路協議。它是應用層的協議,底層是基於TCP通訊的。HTTP協議的工作過程:客戶通過瀏覽器向伺服器傳送文件請求,瀏覽器將請求的資源迴應給瀏覽器,然後關閉連線。即:連線->請求->響應->關閉連線。
  • 關於URL
    即統一資源定位符,每個網頁都對應一個URL地址(俗稱網址),具有全球唯一性。它包含的資訊指出檔案的位置以及瀏覽器應該怎麼處理它。 一個完整的URL包括協議型別、主機型別、路徑和檔名。
    http協議的URL格式: http: //host[:port][abs_path] ,http表示使用http協議來進行資源定位;host是主機域名;port是埠號,一般有預設的;abs_path代表資源的路徑。
    這裡我主要介紹專案中涉及的URL的兩種格式—URL帶引數和不帶引數的。
    這裡寫圖片描述
    GET方法使用的是帶引數的URL,即傳遞的引數會使用?連線在資源路徑後邊;POST方法使用的是不帶引數的URL,它的引數是通過http請求報頭中的請求訊息體傳遞給伺服器的。
  • 關於HTTP的請求與響應格式
    這裡寫圖片描述
    響應報頭中的狀態碼和狀態碼描述,例如:當請求的資源不存在時,會收到“404 NotFound”的頁面,404就是狀態碼,“NotFound”就是狀態碼描述,即請求的檔案不存在。

二、伺服器實現的基本思路

1、http協議是基於TCP通訊的協議,因此,實現web伺服器的第一步至少要能實現兩個主機不同程序之間的TCP通訊。
2、接下來的部分就是比較主要的處理邏輯了,當伺服器收到請求後,首先應該分析請求方法(因為web伺服器是要支援cgi的,但請求方法不同處理cgi也不同,這裡我們只處理GET和POST方法)。
3、當方法確定後,應該拿到請求的URL,這一步是為了我們後邊能處理GET和POST方法的cgi(GET和POST的引數位置不同,GET的引數在URL中,POST的引數在請求正文中)
4、判斷資源是否存在,如果存在,判斷這個資源是一個目錄、普通檔案還是一個可執行程式。之前幾步我們已經提取到URL以及引數。GET方法:如果沒有引數,就直接將請求的資源返回(即進入非cgi模式執行);否則,進入cgi模式內部執行;只要是POST方法就需要支援cgi:直接進入cgi函式內部執行。

非cgi模式:
進入非cgi模式時一定是GET方法且沒有引數,此時進入echo_www()函式內部即可,該函式會將所請求的資源以html的格式返回給瀏覽器。

cgi模式:
這裡寫圖片描述
上述這張圖描述了執行cgi時的過程,首先伺服器要從瀏覽器上讀取引數,然後需要fork出一個子程序進行cgi部分的處理,父程序通過環境變數的方式將引數轉交給子程序,子程序執行完成後,將結果交給父程序,父程序再將資料輸出給瀏覽器。在這個過程中可以將父程序看作一個所謂的中間量,只進行了引數的轉交,因此可以將子程序的輸入輸出檔案描述符進行重定向,即子程序直接與瀏覽器“聯絡”。

下面總結出父子程序內部各自需要乾的事情:
這裡寫圖片描述

三、錯誤處理

錯誤處理這部分的實現可以參考echo_www()函式,但需要改變響應的訊息報頭的格式,即改變狀態碼,狀態碼描述,以及返回的頁面。例如當請求的資源不存在時,伺服器需要返回給瀏覽器一個預設的404頁面,告訴客戶請求的資源不存在。效果如圖:
這裡寫圖片描述

四、專案檔案

這裡寫圖片描述
目錄:
cgi:執行cgi部分的實現程式碼
conf:配置檔案,存放需要繫結的伺服器的ip和port
log:shell的日誌檔案以及http錯誤處理的日誌檔案
lib:mysql需要的lib庫
sql_client:mysql部分的API及CGI實現
wwwroot:web伺服器工作的根目錄,包含各種資源頁面(例如預設的index.html頁面,差錯處理的404頁面),以及執行cgi的可執行程式

檔案:
configure.sh:sheel指令碼,執行該shell指令碼後需要自動生成Makefile檔案
http_ctl.sh:伺服器控制指令碼,需要實現伺服器的啟動、暫停以及重新啟動
httpd.pid:與http_ctl.sh配合使用。如果把伺服器變成守護程序在後臺執行,重新啟動時就需要檢測伺服器是否啟動,該檔案存放伺服器啟動以後的程序id
httpd.h:伺服器的方法宣告
httpd.c:方法實現
main.c:伺服器的主邏輯

五、實現結果

請求資源存在:
這裡寫圖片描述

執行cgi後:
這裡寫圖片描述

六、原始碼:

附:
這裡是我遇到的一些問題,粘出來,也可能是你遇到的問題:
1、本地環回測試ok,Linux下的瀏覽器測試也可以,但不能接外部的瀏覽器訪問(沒有設定橋接模式)嗯~要是在外部瀏覽器測試的話千萬別忘記關閉防火牆
2、伺服器應答時,沒有將html格式的頁面傳送,而是將底層的實現程式碼展示在瀏覽器,並且在除錯時將本來要列印的除錯資訊會列印到網頁上(在迴應空行時將send期望傳送的數值寫的太大,本來只需要傳送兩個位元組的內容)
解決:先檢查程式碼,思路正確,在容易出現問題的地方加入除錯資訊,最後將問題定位在echo_www()函式內
3、不能顯示圖片(這個問題是沒有將所有傳送的情況考慮完全,只考慮到目錄、可執行程式,但沒有考慮到如果請求的是一個路徑明確的普通檔案)
解決:測試請求一個路徑明確的test.html檔案,加入除錯資訊 ,將問題定位在:如果請求的資源存在,應該如何處理。對於普通檔案,找到後並回顯給瀏覽器;如果是目錄,應答的是預設頁面;如果是可執行程式,執行後返回結果
4、能顯示圖片後,但顯示的不完整(原因:echo_www中,期望讀取一行資訊的line值太小,不能存下一張圖片)
5、執行cgi模式時,每次提交資料並進行submit後都會自動出現提醒下載的頁面
原因:在響應報頭中,將Content-Type中的”text”寫成”test”。而瀏覽器對於不能識別或解析的實體,都會提醒使用者下載。