要編寫一個 http 伺服器,第一步就是分析 http 協定格式,之後才能對發送過來的http資料包進行正常解析,并傳回正确的資料包;
Http協定包的格式
首先,讓我們用 netcat 捕獲浏覽器發送給伺服器的資料包,來見一見其廬山真面目。
(1)捕捉 http 協定的資料包
通過指令:
nc -l 127.0.0.1 8888 > http.data
開啟本地的 8888 号端口,在浏覽器中輸入 url 位址 http://127.0.0.1:8888 ,浏覽器将會發送給一個Get請求給nc,nc将接收到的資料寫入檔案 http.data , 接收到的内容如下:
00000000: 4745 5420 2f20 4854 5450 2f31 2e31 0d0a GET / HTTP/1.1..
00000010: 486f 7374 3a20 3132 372e 302e 302e 313a Host: 127.0.0.1:
00000020: 3838 3838 0d0a 436f 6e6e 6563 7469 6f6e 8888..Connection
00000030: 3a20 6b65 6570 2d61 6c69 7665 0d0a 5570 : keep-alive..Up
00000040: 6772 6164 652d 496e 7365 6375 7265 2d52 grade-Insecure-R
00000050: 6571 7565 7374 733a 2031 0d0a 5573 6572 equests: 1..User
00000060: 2d41 6765 6e74 3a20 4d6f 7a69 6c6c 612f -Agent: Mozilla/
00000070: 352e 3020 2858 3131 3b20 4c69 6e75 7820 5.0 (X11; Linux
00000080: 7838 365f 3634 2920 4170 706c 6557 6562 x86_64) AppleWeb
00000090: 4b69 742f 3533 372e 3336 2028 4b48 544d Kit/537.36 (KHTM
000000a0: 4c2c 206c 696b 6520 4765 636b 6f29 2043 L, like Gecko) C
000000b0: 6872 6f6d 652f 3539 2e30 2e33 3037 312e hrome/59.0.3071.
000000c0: 3131 3520 5361 6661 7269 2f35 3337 2e33 115 Safari/537.3
000000d0: 360d 0a41 6363 6570 743a 2074 6578 742f 6..Accept: text/
000000e0: 6874 6d6c 2c61 7070 6c69 6361 7469 6f6e html,application
000000f0: 2f78 6874 6d6c 2b78 6d6c 2c61 7070 6c69 /xhtml+xml,appli
00000100: 6361 7469 6f6e 2f78 6d6c 3b71 3d30 2e39 cation/xml;q=0.9
00000110: 2c69 6d61 6765 2f77 6562 702c 696d 6167 ,image/webp,imag
00000120: 652f 6170 6e67 2c2a 2f2a 3b71 3d30 2e38 e/apng,**;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.8,la;q=0.6,da;q=0.4
對于HTTP封包來說,第一行為封包的起始行,格式為
每個字段用空格分隔;
在該例子中, method 為 GET ,request-URL 為 / ,version 為 HTTP/1.1 ;
在這裡,因為我們隻是在浏覽器中輸入一個ip位址及端口号,預設的請求資源為 / ;
如果在浏覽器中輸入
(4)捕獲 http Post 請求資料包
下面我們來捕獲以下Post的請求包,看看其與Get請求包的不同;
首先,我們建立一個html檔案,檔案位址為 :
/home/hbfeng/Code/Year2017/Mon07/Day19/x.html
檔案内容為:
Document
color:
之後,開啟伺服器 :
nc -l 127.0.0.1 8888 > post.dat
在浏覽器中輸入 :
file:///home/hbfeng/Code/Year2017/Mon07/Day19/x.html
可以出現如下頁面,在文本框中填入内容,點選 送出 即可獲得一個Post資料包:
POST資料擷取
Post資料包的内容如下:
POST / HTTP/1.1
Host: 127.0.0.1:8888
Connection: keep-alive
Content-Length: 12
Cache-Control: max-age=0
Origin: null
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36
Content-Type: application/x-www-form-urlencoded
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.8,la;q=0.6,da;q=0.4
color=yellow
與Get請求的資料相比,Post資料包多出了以下我們後續編寫代碼時需要使用的内容:
Content-Length: 12 : 表示HTTP正文的大小。POST請求将資料以URL編碼的形式放在HTTP正文中,字段形式為 fieldname=value,用&分隔每個字段;
HTTP資訊頭與HTTP正文之間有一行空行;
HTTP中有表單内容 color=yellow ,正好等于 Content-Length 的長度;
伺服器的工作流程
知道了浏覽器給我們發送的資料格式以後,我們的http伺服器就可以将資料包進行解析,并動态生成頁面發送給浏覽器;
伺服器的大緻工作流程如下圖所示:
tinyhttpd的工作流程
回報給用戶端的資料格式
知道了伺服器的運作流程,我們需要知道浏覽器希望從伺服器端得到什麼格式的資料;
伺服器按照HTTP協定傳回資料給用戶端,如響應碼為400,傳回的内容為:
HTTP/1.0 400 BAD REQUEST
Content-type: text/html
... ...
每一行最後都跟 \r\n ,表示一行的結束;
第一行的3個參數用空格隔開,第一個參數說明伺服器所使用的http協定為 HTTP/1.0 ,第二個參數是一個傳回碼,第三個參數是對傳回碼的解釋;
第二行聲明的是http正文内容的類型,text/html 表示正文是一個html檔案的内容,浏覽器将其解釋并顯示,如果是 text/plain 表示正文是純文字,浏覽器直接将内容顯示,不需要解釋、渲染等操作;
之後的空行表示http資訊頭結束;
空行之後的内容即為http正文;
動态生成Web頁面技術
CGI (Common Gateway Interface,通用網關接口) :一種重要的網際網路技術,是指根據浏覽器發送過來的請求,伺服器執行一定的動作,如資料庫查詢、系統資訊查詢等,甚至可以讓伺服器删除某些檔案,之後生成對應的内容傳回給浏覽器以顯示執行結果;
可以将CGI了解為通過浏覽器就可以讓伺服器執行某些在伺服器端已經定義好的功能,實作遠端調用 ;
CGI是這種技術的定義,而其實作方式多種多樣,如 Perl 是一個廣泛被用來編寫CGI程式的語言,另外,像 Python、Ruby、C/C++、PHP 等也可以實作CGI,甚至是 Shell腳本 檔案也能勝任該任務;
CGI可以用任何一種語言編寫,隻要這種語言具有标準輸入、輸出和環境變量。
在下一篇中,我們就來根據上面的流程圖來實作一個小型的http伺服器;
參考
完!