系列文章:
自己動手寫http服務(wù)器(一) -- UNIX C 網(wǎng)絡(luò)編程
自己動手寫http服務(wù)器(二) -- http協(xié)議分析
自己動手寫http服務(wù)器(三) -- 代碼實(shí)現(xiàn)
要編寫一個(gè) http 服務(wù)器,第一步就是分析 http 協(xié)議格式,之后才能對發(fā)送過來的http數(shù)據(jù)包進(jìn)行正常解析规哪,并返回正確的數(shù)據(jù)包瀑构;
Http協(xié)議包的格式
首先,讓我們用 netcat 捕獲瀏覽器發(fā)送給服務(wù)器的數(shù)據(jù)包,來見一見其廬山真面目。
(1)捕捉 http 協(xié)議的數(shù)據(jù)包
通過命令:
nc -l 127.0.0.1 8888 > http.data
開啟本地的 8888 號端口,在瀏覽器中輸入 url 地址 http://127.0.0.1:8888 冯键,瀏覽器將會發(fā)送給一個(gè)Get請求給nc,nc將接收到的數(shù)據(jù)寫入文件 http.data , 接收到的內(nèi)容如下:
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
00000130: 0d0a 4163 6365 7074 2d45 6e63 6f64 696e ..Accept-Encodin
00000140: 673a 2067 7a69 702c 2064 6566 6c61 7465 g: gzip, deflate
00000150: 2c20 6272 0d0a 4163 6365 7074 2d4c 616e , br..Accept-Lan
00000160: 6775 6167 653a 207a 682d 434e 2c7a 683b guage: zh-CN,zh;
00000170: 713d 302e 382c 6c61 3b71 3d30 2e36 2c64 q=0.8,la;q=0.6,d
00000180: 613b 713d 302e 340d 0a0d 0a a;q=0.4....
左側(cè)是接收的數(shù)據(jù)原始二進(jìn)制流庸汗,右側(cè)是對應(yīng)的ASCII碼惫确;
可見瀏覽器默認(rèn)使用的http協(xié)議是 HTTP/1.1,其頭信息肯定是文本(ASCII編碼)夫晌;
(2)捕捉 https 協(xié)議的數(shù)據(jù)包
通過命令:
nc -l 127.0.0.1 8888 > https.data
在瀏覽器中輸入 url 地址 https://127.0.0.1:8888 雕薪,即可獲得瀏覽器發(fā)送給服務(wù)器的數(shù)據(jù),內(nèi)容如下:
00000000: 1603 0100 c2ae 0100 00c2 aa03 0328 6fc2 .............(o.
00000010: a227 0f31 c392 c388 c392 42c2 8dc2 9dc2 .'.1......B.....
00000020: 8275 c297 2324 c38f 484d 75c2 8b23 5bc2 .u..#$..HMu..#[.
00000030: aac3 98c3 a17c 0d70 6cc2 be00 001c 2a2a .....|.pl.....**
00000040: c380 2bc3 802f c380 2cc3 8030 c38c c2a9 ..+../..,..0....
00000050: c38c c2a8 c380 13c3 8014 00c2 9c00 c29d ................
00000060: 002f 0035 000a 0100 0065 c2aa c2aa 0000 ./.5.....e......
00000070: c3bf 0100 0100 0017 0000 0023 0000 000d ...........#....
00000080: 0014 0012 0403 0804 0401 0503 0805 0501 ................
00000090: 0806 0601 0201 0005 0005 0100 0000 0000 ................
000000a0: 1200 0000 1000 0e00 0c02 6832 0868 7474 ..........h2.htt
000000b0: 702f 312e 3175 5000 0000 0b00 0201 0000 p/1.1uP.........
000000c0: 0a00 0a00 086a 6a00 1d00 1700 182a 2a00 .....jj......**.
000000d0: 0100 0a ...
可見晓淀,https協(xié)議的頭信息是二進(jìn)制數(shù)據(jù)流而非文本所袁;
本文只對 http1.1 協(xié)議進(jìn)行分析;
(3)分析 http Get請求數(shù)據(jù)包
瀏覽器發(fā)送到服務(wù)器端的請求數(shù)據(jù)為:
GET / HTTP/1.1
Host: 127.0.0.1:8888
Connection: keep-alive
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
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
對于HTTP報(bào)文來說凶掰,第一行為報(bào)文的起始行燥爷,格式為
<method> <request-URL> <version>
每個(gè)字段用空格分隔;
在該例子中懦窘, method 為 GET 前翎,request-URL 為 / ,version 為 HTTP/1.1 畅涂;
在這里港华,因?yàn)槲覀冎皇窃跒g覽器中輸入一個(gè)ip地址及端口號,默認(rèn)的請求資源為 / 午衰;
如果在瀏覽器中輸入 http://127.0.0.1:8888/xxx/yy?name=abc&age=23 則 request-URL 的值將是 * /xxx/yy?name=abc&age=23 * 立宜;
(4)捕獲 http Post 請求數(shù)據(jù)包
下面我們來捕獲以下Post的請求包冒萄,看看其與Get請求包的不同;
首先橙数,我們創(chuàng)建一個(gè)html文件尊流,文件地址為 :
/home/hbfeng/Code/Year2017/Mon07/Day19/x.html
文件內(nèi)容為:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<form action="http://127.0.0.1:8888" method="POST">
color:<input type="text" name="color">
<input type="submit" value="提交" />
</form>
</body>
</html>
之后,開啟服務(wù)器 :
nc -l 127.0.0.1 8888 > post.dat
在瀏覽器中輸入 :
file:///home/hbfeng/Code/Year2017/Mon07/Day19/x.html
可以出現(xiàn)如下頁面灯帮,在文本框中填入內(nèi)容崖技,點(diǎn)擊 提交 即可獲得一個(gè)Post數(shù)據(jù)包:
Post數(shù)據(jù)包的內(nèi)容如下:
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請求的數(shù)據(jù)相比,Post數(shù)據(jù)包多出了以下我們后續(xù)編寫代碼時(shí)需要使用的內(nèi)容:
Content-Length: 12
: 表示HTTP正文的大小钟哥。POST請求將數(shù)據(jù)以URL編碼的形式放在HTTP正文中迎献,字段形式為 fieldname=value,用&分隔每個(gè)字段瞪醋;HTTP信息頭與HTTP正文之間有一行空行忿晕;
HTTP中有表單內(nèi)容
color=yellow
装诡,正好等于Content-Length
的長度银受;
服務(wù)器的工作流程
知道了瀏覽器給我們發(fā)送的數(shù)據(jù)格式以后,我們的http服務(wù)器就可以將數(shù)據(jù)包進(jìn)行解析鸦采,并動態(tài)生成頁面發(fā)送給瀏覽器宾巍;
服務(wù)器的大致工作流程如下圖所示:
反饋給客戶端的數(shù)據(jù)格式
知道了服務(wù)器的運(yùn)行流程,我們需要知道瀏覽器希望從服務(wù)器端得到什么格式的數(shù)據(jù)渔伯;
服務(wù)器按照HTTP協(xié)議返回?cái)?shù)據(jù)給客戶端顶霞,如響應(yīng)碼為400,返回的內(nèi)容為:
HTTP/1.0 400 BAD REQUEST
Content-type: text/html
<!DOCTYPE>
<html>
<!-- html內(nèi)容 -->
... ...
</html>
每一行最后都跟 \r\n
锣吼,表示一行的結(jié)束选浑;
第一行的3個(gè)參數(shù)用空格隔開,第一個(gè)參數(shù)說明服務(wù)器所使用的http協(xié)議為 HTTP/1.0 玄叠,第二個(gè)參數(shù)是一個(gè)返回碼古徒,第三個(gè)參數(shù)是對返回碼的解釋;
第二行聲明的是http正文內(nèi)容的類型读恃,text/html 表示正文是一個(gè)html文件的內(nèi)容隧膘,瀏覽器將其解釋并顯示,如果是 text/plain 表示正文是純文本寺惫,瀏覽器直接將內(nèi)容顯示疹吃,不需要解釋、渲染等操作西雀;
之后的空行表示http信息頭結(jié)束萨驶;
空行之后的內(nèi)容即為http正文;
動態(tài)生成Web頁面技術(shù)
CGI (Common Gateway Interface艇肴,通用網(wǎng)關(guān)接口) :一種重要的互聯(lián)網(wǎng)技術(shù)腔呜,是指根據(jù)瀏覽器發(fā)送過來的請求判莉,服務(wù)器執(zhí)行一定的動作,如數(shù)據(jù)庫查詢育谬、系統(tǒng)信息查詢等券盅,甚至可以讓服務(wù)器刪除某些文件,之后生成對應(yīng)的內(nèi)容返回給瀏覽器以顯示執(zhí)行結(jié)果膛檀;
可以將CGI理解為通過瀏覽器就可以讓服務(wù)器執(zhí)行某些在服務(wù)器端已經(jīng)定義好的功能锰镀,實(shí)現(xiàn)遠(yuǎn)程調(diào)用 ;
CGI是這種技術(shù)的定義咖刃,而其實(shí)現(xiàn)方式多種多樣泳炉,如 Perl 是一個(gè)廣泛被用來編寫CGI程序的語言,另外嚎杨,像 Python花鹅、Ruby、C/C++枫浙、PHP 等也可以實(shí)現(xiàn)CGI刨肃,甚至是 Shell腳本 文件也能勝任該任務(wù);
CGI可以用任何一種語言編寫箩帚,只要這種語言具有標(biāo)準(zhǔn)輸入真友、輸出和環(huán)境變量。
在下一篇中紧帕,我們就來根據(jù)上面的流程圖來實(shí)現(xiàn)一個(gè)小型的http服務(wù)器盔然;
參考
HTTP/1.1 Header Field Definitions
完是嗜!