自己動(dòng)手寫http服務(wù)器(二) -- http協(xié)議分析

系列文章:
自己動(dòng)手寫http服務(wù)器(一) -- UNIX C 網(wǎng)絡(luò)編程
自己動(dòng)手寫http服務(wù)器(二) -- http協(xié)議分析
自己動(dòng)手寫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 ,瀏覽器將會(huì)發(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ù)獲取

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)行解析,并動(dòng)態(tài)生成頁面發(fā)送給瀏覽器;

服務(wù)器的大致工作流程如下圖所示:

tinyhttpd的工作流程

反饋給客戶端的數(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正文;

動(dòng)態(tài)生成Web頁面技術(shù)

CGI (Common Gateway Interface,通用網(wǎng)關(guān)接口) :一種重要的互聯(lián)網(wǎng)技術(shù),是指根據(jù)瀏覽器發(fā)送過來的請求,服務(wù)器執(zhí)行一定的動(dòng)作,如數(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、RubyC/C++、PHP 等也可以實(shí)現(xiàn)CGI,甚至是 Shell腳本 文件也能勝任該任務(wù);

CGI可以用任何一種語言編寫,只要這種語言具有標(biāo)準(zhǔn)輸入、輸出和環(huán)境變量。

在下一篇中,我們就來根據(jù)上面的流程圖來實(shí)現(xiàn)一個(gè)小型的http服務(wù)器;

參考

HTTP協(xié)議入門

關(guān)于HTTP協(xié)議,一篇就夠了

HTTP/1.1 Header Field Definitions

通用網(wǎng)關(guān)接口

完!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 一、概念(載錄于:http://www.cnblogs.com/EricaMIN1987_IT/p/3837436...
    yuantao123434閱讀 8,741評論 6 152
  • Http協(xié)議詳解 標(biāo)簽(空格分隔): Linux 聲明:本片文章非原創(chuàng),內(nèi)容來源于博客園作者M(jìn)IN飛翔的HTTP協(xié)...
    Sivin閱讀 5,344評論 3 82
  • 前言:最近發(fā)現(xiàn)自己在網(wǎng)絡(luò)相關(guān)這一塊基礎(chǔ)很是欠缺,所以準(zhǔn)備花時(shí)間了解一下,本文主要是講http協(xié)議的一些基礎(chǔ),和一些...
    justCode_閱讀 2,148評論 0 23
  • 深入淺出HTTP協(xié)議(WEB開發(fā)和面試必備) 1.基礎(chǔ)概念篇 a.簡介 HTTP是Hyper Text Trans...
    半世韶華憶闌珊閱讀 1,341評論 0 7
  • 本文整理自MIN飛翔博客 [1] 1. 概念 協(xié)議是指計(jì)算機(jī)通信網(wǎng)絡(luò)中兩臺計(jì)算機(jī)之間進(jìn)行通信所必須共同遵守的規(guī)定或...
    HoyaWhite閱讀 2,795評論 2 20

友情鏈接更多精彩內(nèi)容