久久国产成人av_抖音国产毛片_a片网站免费观看_A片无码播放手机在线观看,色五月在线观看,亚洲精品m在线观看,女人自慰的免费网址,悠悠在线观看精品视频,一级日本片免费的,亚洲精品久,国产精品成人久久久久久久

分享

深度解密HTTP通信細節(jié)

 liang1234_ 2019-01-29

來源:碼農(nóng)桃花源




上一篇文章三次握手,,四次揮手”你真的懂嗎,?中,我們學(xué)會了用wireshark和tcpdump來分析TCP的“三次握手,,四次揮手”,,非常好用。這哥倆就是傳說中的 錘子,,拿著 錘子,,看什么都像 釘子!在這篇文章中,我對準了 HTTP這顆釘子砸下去,,咳咳,。

為了對網(wǎng)絡(luò)數(shù)據(jù)包的“流轉(zhuǎn)”有更加深刻的理解,我在docker(遠程)上部署一個服務(wù),,支持http方式調(diào)用,。從客戶端(本地)用http方式請求其中的一個接口,并得到響應(yīng)數(shù)據(jù),。同時本地通過wireshark抓包,,遠程用tcpdump抓包,然后分析過程中的所有通信細節(jié),。悲劇是把美好的東西撕碎給人看,,而我則是把復(fù)雜的東西撕碎了給人看。

文章稍長,,請在看本文時保持耐心,。我先通過工具獲取HTTP通信的數(shù)據(jù)包,再來抽絲剝繭,,深入二進制的天地里,,解密HTTP所有的通信細節(jié)。分析過程中,,由點到面,,將相關(guān)知識串接起來。保證全篇讀完之后,,你對HTTP的理解會上升一個臺階,!

為了更好的閱讀體驗,我手動貼上本文的目錄:

HTTP報文截獲

背景介紹

我手頭現(xiàn)在有一個地理幾何相關(guān)的服務(wù),,它提供一組接口對外使用,。其中有一個接口是 Fence2Area. 使用方傳入一個圍欄(由點的列表組成,點由<經(jīng)度,,緯度>表示),、點的坐標系類型(谷歌地圖用的是wgs84, 國內(nèi)騰訊、高德用的是soso, 而百度用的是另一套自己的坐標系),,接口輸出的則是圍欄的面積,。

我請求服務(wù)的“Fence2Area”接口,輸入圍欄(fence)頂點(lng, lat)坐標,、坐標系類型(coordtype),,輸出的則是多邊形的面積(area).

一次正常的請求示例url, 這個大家都不陌生(我用docker_ip代替真實的ip):

  1. http://docker_ip:7080/data?cmd=Fence2Area&meta={'caller':'test','TraceId':'test'}&request={'fence':[{'lng':10.2,'lat':10.2}, {'lng':10.2,'lat':8.2}, {'lng':8.2,'lat':8.2}, {'lng':8.2,'lat':10.2}],'coordtype':2}

請求發(fā)出后,服務(wù)器進行處理,,之后,,客戶端收到返回的數(shù)據(jù)如下:

  1. {

  2.    'data': {

  3.        'area': 48764135597.842606

  4.    },

  5.    'errstr': ''

  6. }

area字段表示面積, errstr表示出錯信息,空說明沒有出錯,。

抓包

在真正發(fā)送請求之前,需要進行抓包前的設(shè)置,。在本地mac,,我用wireshark; 而在遠程docker上,我用tcpdump工具,。

mac本地

設(shè)置wireshark包過濾器,,監(jiān)控本地主機和遠程docker之間的通信。

  1. ip.addr eq docker_ip

點擊開始捕獲,。

遠程docker

該服務(wù)通過7080端口對外提供,,使用如下命令捕獲網(wǎng)絡(luò)包:

  1. tcpdump -w /tmp/testHttp.cap port 7080 -s0

請求 && 分析

準備工作做完,我選了一個神圣的時刻,,在本地通過瀏覽器訪問如下url:

  1. http://docker_ip:7080/data?cmd=Fence2Area&meta={'caller':'test','TraceId':'test'}&request={'fence':[{'lng':10.2,'lat':10.2}, {'lng':10.2,'lat':8.2}, {'lng':8.2,'lat':8.2}, {'lng':8.2,'lat':10.2}],'coordtype':2}

這樣本地的wireshark和遠程的tcpdump都能抓取到HTTP網(wǎng)絡(luò)數(shù)據(jù)包,。

關(guān)閉服務(wù)進程

正式請求之前,我們先看一下幾種特殊的情形,。

首先,,關(guān)閉gcs服務(wù)進程,請求直接返回RST報文,。

如上圖,,我在請求的時候,訪問服務(wù)端的另一個端口 5010, 這個端口沒有服務(wù)監(jiān)聽,,和關(guān)閉gcs服務(wù)進程是同樣的效果,。可以看到,,客戶端發(fā)送SYN報文,,但直接被遠程docker RST掉了。因為服務(wù)端操作系統(tǒng)找不到監(jiān)聽此端口的進程,。

關(guān)閉docker

關(guān)閉docker, 由于發(fā)送的SYN報文段得不到響應(yīng),,因此會進行重試,mac下重試的次數(shù)為10次,。

先每隔1秒重試了5次,,再用“指數(shù)退避”的時間間隔重試,2s, 4s, 8s, 16s, 32s. 最后結(jié)束,。

重啟docker

先進行一次正常的訪問,,隨后重啟docker。并再次在本地訪問以上url, 瀏覽器這時還是用的上一次的端口,,訪問到服務(wù)端后,,因為它已經(jīng)重啟了,所以服務(wù)端已經(jīng)沒有這個連接的消息了。因此會返回一個RST報文,。

正常請求

服務(wù)正常啟動,,正常發(fā)送請求,這次請求成功,,那是當然的,,嘿嘿!

這是在mac上用wireshark捕獲的數(shù)據(jù)包,,共7個包,,前三個包為3次握手的包,第四個包為 HTTP層發(fā)送的請求數(shù)據(jù),,第五個包為服務(wù)端的TCP 確認報文,,第六個包為服務(wù)端在 HTTP層發(fā)送的響應(yīng)數(shù)據(jù),第七個包為mac對第六個包的確認報文,。

重點來關(guān)注后面幾個包,,先看第四個包,

  1. 0x0000:  4500 0295 0000 4000 3606 623b ac17 ccdc

  2. 0x0010:  0a60 5cd4 db9b 1ba8 a59a 46ce 6d03 e87d

  3. 0x0020:  8018 1015 0ee7 0000 0101 080a 2e4c b2ef

  4. 0x0030:  0f20 3acf 4745 5420 2f64 6174 613f 636d

  5. 0x0040:  643d 4665 6e63 6532 4172 6561 266d 6574

  6. 0x0050:  613d 7b25 3232 6361 6c6c 6572 2532 323a

  7. 0x0060:  2532 3274 6573 7425 3232 2c25 3232 5472

  8. 0x0070:  6163 6549 6425 3232 3a25 3232 7465 7374

  9. 0x0080:  2532 327d 2672 6571 7565 7374 3d7b 2532

  10. 0x0090:  3266 656e 6365 2532 323a 5b7b 2532 326c

  11. 0x00a0:  6e67 2532 323a 3130 2e32 2c25 3232 6c61

  12. 0x00b0:  7425 3232 3a31 302e 327d 2c25 3230 7b25

  13. 0x00c0:  3232 6c6e 6725 3232 3a31 302e 322c 2532

  14. 0x00d0:  326c 6174 2532 323a 382e 327d 2c25 3230

  15. 0x00e0:  7b25 3232 6c6e 6725 3232 3a38 2e32 2c25

  16. 0x00f0:  3232 6c61 7425 3232 3a38 2e32 7d2c 2532

  17. 0x0100:  307b 2532 326c 6e67 2532 323a 382e 322c

  18. 0x0110:  2532 326c 6174 2532 323a 3130 2e32 7d5d

  19. 0x0120:  2c25 3232 636f 6f72 6474 7970 6525 3232

  20. 0x0130:  3a32 7d20 4854 5450 2f31 2e31 0d0a 486f

  21. 0x0140:  7374 3a20 3130 2e39 362e 3932 2e32 3132

  22. 0x0150:  3a37 3038 300d 0a55 7067 7261 6465 2d49

  23. 0x0160:  6e73 6563 7572 652d 5265 7175 6573 7473

  24. 0x0170:  3a20 310d 0a41 6363 6570 743a 2074 6578

  25. 0x0180:  742f 6874 6d6c 2c61 7070 6c69 6361 7469

  26. 0x0190:  6f6e 2f78 6874 6d6c 2b78 6d6c 2c61 7070

  27. 0x01a0:  6c69 6361 7469 6f6e 2f78 6d6c 3b71 3d30

  28. 0x01b0:  2e39 2c2a 2f2a 3b71 3d30 2e38 0d0a 5573

  29. 0x01c0:  6572 2d41 6765 6e74 3a20 4d6f 7a69 6c6c

  30. 0x01d0:  612f 352e 3020 284d 6163 696e 746f 7368

  31. 0x01e0:  3b20 496e 7465 6c20 4d61 6320 4f53 2058

  32. 0x01f0:  2031 305f 3133 5f36 2920 4170 706c 6557

  33. 0x0200:  6562 4b69 742f 3630 352e 312e 3135 2028

  34. 0x0210:  4b48 544d 4c2c 206c 696b 6520 4765 636b

  35. 0x0220:  6f29 2056 6572 7369 6f6e 2f31 322e 302e

  36. 0x0230:  3220 5361 6661 7269 2f36 3035 2e31 2e31

  37. 0x0240:  350d 0a41 6363 6570 742d 4c61 6e67 7561

  38. 0x0250:  6765 3a20 7a68 2d63 6e0d 0a41 6363 6570

  39. 0x0260:  742d 456e 636f 6469 6e67 3a20 677a 6970

  40. 0x0270:  2c20 6465 666c 6174 650d 0a43 6f6e 6e65

  41. 0x0280:  6374 696f 6e3a 206b 6565 702d 616c 6976

  42. 0x0290:  650d 0a0d 0a

我們來逐字節(jié)分析,。

剩余的數(shù)據(jù)部分即為TCP協(xié)議相關(guān)的,。TCP也是20B固定長度 可變長度部分。

可變長度部分,,協(xié)議如下:

剩下來的就是數(shù)據(jù)部分了,。我們一行一行地看。因為http是字符流,,所以我們先看一下ascii字符集,,執(zhí)行命令:

  1. man ascii

可以得到ascii碼,我們直接看十六進制的結(jié)果:

把上表的最后一列連起來,,就是:

  1. GET /data?cmd=Fence2Area&meta={%22caller%22:%22test%22,%22TraceId%22:%22test%22}&request={%22fence%22:[{%22lng%22:10.2,%22lat%22:10.2},%20{%22lng%22:10.2,%22lat%22:8.2},%20{%22lng%22:8.2,%22lat%22:8.2},%20{%22lng%22:8.2,%22lat%22:10.2}],%22coordtype%22:2} HTTP/1.1

  2. Host: 10.96.92.212:7080

  3. Upgrade-Insecure-Requests: 1

  4. Accept: text/html,application/xhtml xml,application/xml;q=0.9,*/*;q=0.8

  5. User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0.2 Safari/605.1.15

  6. Accept-Language: zh-cn

  7. Accept-Encoding: gzip, deflate

  8. Connection: keep-alive

其中,,cr nl表示回車,換行,。

docker收到數(shù)據(jù)后,,會回復(fù)一個ack包。第四個包的總長度為661字節(jié),,去掉IP頭部20字節(jié),,TCP頭部固定部分20字節(jié),TCP頭部可選長度為12字節(jié),,共52字節(jié),,因此TCP數(shù)據(jù)部分總長度為661-52=609字節(jié)。另外,,序列號為2778351310.

再來看第5個包,,字節(jié)流如下:

  1. 0x0000:  4500 0034 d28b 4000 4006 8810 0a60 5cd4

  2. 0x0010:  ac17 ccdc 1ba8 db9b 6d03 e87d a59a 492f

  3. 0x0020:  8010 00ec e04e 0000 0101 080a 0f20 3af7

  4. 0x0030:  2e4c b2ef

剩余的數(shù)據(jù)部分即為TCP協(xié)議相關(guān)的,。TCP也是20B固定長度 可變長度部分。

可變長度部分,,協(xié)議如下:

數(shù)據(jù)部分為空,,這個包僅為確認包。

再來看第六個包,,字節(jié)流如下:

  1. 0x0000:  4500 00f9 d28c 4000 4006 874a 0a60 5cd4

  2. 0x0010:  ac17 ccdc 1ba8 db9b 6d03 e87d a59a 492f

  3. 0x0020:  8018 00ec e113 0000 0101 080a 0f20 3af8

  4. 0x0030:  2e4c b2ef 4854 5450 2f31 2e31 2032 3030

  5. 0x0040:  204f 4b0d 0a41 6363 6573 732d 436f 6e74

  6. 0x0050:  726f 6c2d 416c 6c6f 772d 4f72 6967 696e

  7. 0x0060:  3a20 2a0d 0a44 6174 653a 2054 6875 2c20

  8. 0x0070:  3033 204a 616e 2032 3031 3920 3132 3a32

  9. 0x0080:  333a 3437 2047 4d54 0d0a 436f 6e74 656e

  10. 0x0090:  742d 4c65 6e67 7468 3a20 3438 0d0a 436f

  11. 0x00a0:  6e74 656e 742d 5479 7065 3a20 7465 7874

  12. 0x00b0:  2f70 6c61 696e 3b20 6368 6172 7365 743d

  13. 0x00c0:  7574 662d 380d 0a0d 0a7b 2264 6174 6122

  14. 0x00d0:  3a7b 2261 7265 6122 3a34 3837 3634 3133

  15. 0x00e0:  3535 3937 2e38 3432 3630 367d 2c22 6572

  16. 0x00f0:  7273 7472 223a 2222 7d

剩余的數(shù)據(jù)部分即為TCP協(xié)議相關(guān)的,。TCP也是20B固定長度 可變長度部分。

可變長度部分,,協(xié)議如下:

剩下來的就是數(shù)據(jù)部分了。我們一行一行地看,。

把上表的最后一列連起來,,就是:

  1. HTTP/1.1 200 OK

  2. Access-Control-Allow-Origin: *

  3. Date: Thu, 03 Jan 2019 12:23:47 GMT

  4. Content-Length: 48

  5. Content-Type: text/plain; charset=utf-8

  6. {'data':{'area':48764135597.842606},'errstr':''}

Content-Length: 48,最后一行的長度即為48個字節(jié),。

最后,,第七個包,字節(jié)流如下:

  1. 0x0000:  4500 0034 0000 4000 3606 649c ac17 ccdc

  2. 0x0010:  0a60 5cd4 db9b 1ba8 a59a 492f 6d03 e942

  3. 0x0020:  8010 100f 1eb9 0000 0101 080a 2e4c b314

  4. 0x0030:  0f20 3af8

剩余的數(shù)據(jù)部分即為TCP協(xié)議相關(guān)的,。TCP也是20B固定長度 可變長度部分,。

可變長度部分,協(xié)議如下:

至此,,一次完整的http請求的報文就解析完了,。感覺如何,是不是很親切,?

HTTP協(xié)議分析

上面我們把HTTP協(xié)議相關(guān)的數(shù)據(jù)給解構(gòu)了,,下面我將對照上面的數(shù)據(jù)拆解結(jié)果,一步步帶你深入理解HTTP協(xié)議,。

整體介紹

HTTP(Hypertext Transfer Protocol)超文本傳輸協(xié)議,,是在互聯(lián)網(wǎng)上進行通信時使用的一種協(xié)議。說得更形象一點: HTTP是現(xiàn)代互聯(lián)網(wǎng)中使用的公共語言,。它最著名的應(yīng)用是用在瀏覽器的服務(wù)器間的通信,。

HTTP屬于應(yīng)用層協(xié)議,底層是靠TCP進行可靠地信息傳輸,。

HTTP在傳輸一段報文時,,會以 的形式將報文數(shù)據(jù)的內(nèi)容通過 一條打開的TCP連接按序傳輸。TCP接到上層應(yīng)用交給它的數(shù)據(jù)流之后,,會按序?qū)?shù)據(jù)流打散成一個個的分段,。再交到IP層,通過網(wǎng)絡(luò)進行傳輸,。另一端的接收方則相反,,它們將接收到的分段按序組裝好,,交給上層HTTP協(xié)議進行處理。

編碼

我們再來回顧一下:

原始的url值:

  1. /data?cmd=Fence2Area&meta={'caller':'test','TraceId':'test'}&request={'fence':[{'lng':10.2,'lat':10.2}, {'lng':10.2,'lat':8.2}, {'lng':8.2,'lat':8.2}, {'lng':8.2,'lat':10.2}],'coordtype':2}

編碼后的url值:

  1. /data?cmd=Fence2Area&meta={%22caller%22:%22test%22,%22TraceId%22:%22test%22}&request={%22fence%22:[{%22lng%22:10.2,%22lat%22:10.2},%20{%22lng%22:10.2,%22lat%22:8.2},%20{%22lng%22:8.2,%22lat%22:8.2},%20{%22lng%22:8.2,%22lat%22:10.2}],%22coordtype%22:2}

在之前的報文拆解過程中,,我們看到多了很多 %22,,其實, 0x22是單引號 '的ascii值,,

一方面,,URL描述的資源為了能通過其他各種協(xié)議傳送,但是有些協(xié)議在傳輸過程中會剝?nèi)ヒ恍┨囟ǖ淖址?;另一方面,,URL還是可讀的,所以那些不可打印的字符就不能在URL中使用了,,比如空格,;最后,URL還得是完整的,,它需要支持所有語言的字符,。

總之,基于很多原因,,URL設(shè)計者將US-ASCII碼和其轉(zhuǎn)義序列集成到URL中,,通過轉(zhuǎn)義序列,就可以用US-ASCII字符集的有限子集對任意字符或數(shù)據(jù)進行編碼了,。

轉(zhuǎn)義的方法:百分號( %)后跟著兩個表示ASCII碼的十六進制數(shù),。比如:

所以上面在瀏覽器發(fā)送給服務(wù)器的URL進行了非“安全字符”編碼,也就不奇怪了吧,?

在URL中,,當上面的保留字符用在保留用途之外的場合時,需要對URL進行編碼,。

MIME類型

響應(yīng)數(shù)據(jù)中,,我們注意到有一個首部:

  1. Content-Type: text/plain; charset=utf-8

互聯(lián)網(wǎng)上有數(shù)千種不同的數(shù)據(jù)類型,HTTP給每種對象都打上了MIME(Multipurpose Internet Media Extension, 多用途因特網(wǎng)郵件擴展)標簽,,也就是響應(yīng)數(shù)據(jù)中的 Content-Type. MIME本來是用在郵件協(xié)議中的,,后來被移植到了HTTP中。瀏覽器從服務(wù)器上取回了一個對象時,,會去查看MIME類型,,從而得知如何處理這種對象,是該展示圖片,,還是調(diào)用聲卡播放聲音,。MIME通過斜杠來標識對象的主類型和其中的特定的子類型,下表展示了一些常見的類型,,其中的實體主體是指body部分:

URI/URL/URN

URI(Uniform Resource Identifier, 統(tǒng)一資源標識符)表示服務(wù)器資源,,URL(Uniform Resource Locator, 統(tǒng)一資源定位符)和URN(Uniform Resource Name, 統(tǒng)一資源名)是URI的具體實現(xiàn),。URI是一個通用的概念,由兩個主要的子集URL和URN構(gòu)成,,URL通過位置,、URN通過名字來標識資源。

URL定義了資源的位置,,表示資源的實際地址,,在使用URL的過程中,如果URL背后的資源發(fā)生了位置移動,,訪問者就找不到它了,。這個時候就要用到URN了,它給定資源一個名字,,無論它移動到哪里,,都可以通過這個名字來訪問到它,簡直完美,!

URL通常的格式是:

  1. 協(xié)議方案 服務(wù)器地址 具體的資源路徑

協(xié)議方案(scheme),如 http, ftp,,告知web客戶端怎樣訪問資源),;服務(wù)器地址,如 www.oreilly.com; 具體的資源路徑,,如 index.html.

HTTP方法

HTTP支持幾種不同的請求方法,,每種方法對服務(wù)器要求的動作不同,如下圖是幾種常見的方法:

HEAD方法只獲取頭部,,不獲取數(shù)據(jù)部分,。通過頭部可以獲取比如資源的類型(Content-Type)、資源的長度(Content-Length)這些信息,。這樣,,客戶端可以獲取即將請求資源的一些情況,可以做到心中有數(shù),。

POST用于向服務(wù)器發(fā)送數(shù)據(jù),,常見的是提交表單;PUT用于向服務(wù)器上的資源存儲數(shù)據(jù),。

狀態(tài)碼

每條HTTP的響應(yīng)報文都會帶上一個三位數(shù)字的狀態(tài)碼和一條解釋性的“原因短語”,,通知客戶端本次請求的狀態(tài),幫助客戶端快速理解事務(wù)處理結(jié)果,,最常見的是:

  1. 200 OK

  2. 404 Not Found

  3. 500 Internal Server Error

我們平時使用瀏覽器的時候,,很多的錯誤碼其實是由瀏覽器處理的,我們感知不到,。但是 404NotFound會穿透重重迷霧,,來到我們面前,,為何?那是因為他對我們愛的深沉??!

客戶端可以據(jù)此狀態(tài)碼,決定下一步的行動(如重定向等),。

三位數(shù)字的第一位表示分類:

報文格式

HTTP報文實際上是由一行行的字符串組成的,,每行字符串的末尾用 \r\n分隔,人類可以很方便的閱讀,。順便說一句,,不是所有的協(xié)議都對人類這么友好的,像thrift協(xié)議,,直接甩一堆字節(jié)給你,,告訴你說 0x0001表示調(diào)用方法,諸如此類的,,你只能對著一個十六進制的數(shù)據(jù)塊一個個地去“解碼”,。不可能像HTTP協(xié)議這樣,直接將字符編碼,,人類可以直接讀懂,。

舉個簡單的請求報文和響應(yīng)報文的格式的例子:

實際上,請求報文也是可以有body(主體)部分的,。請求報文是由 請求行(request line),、請求頭部(header)、空行,、請求數(shù)據(jù)四個部分組成,。唯一要注意的一點就是,請求報文即使body部分是空的,,請求頭部后的 回車換行符也是必須要有的,。

響應(yīng)報文的格式和請求報文的格式類似:

請求報文、響應(yīng)報文的起始行和響應(yīng)頭部里的字段都是文本化,、結(jié)構(gòu)化的,。而請求body卻可以包含任意二進制數(shù)據(jù)(如圖片、視頻,、軟件等),,當然也可以包含文本。

有些首部是通用的,,有些則是請求或者響應(yīng)報文才會有的,。

順便提一下, 用telnet直連服務(wù)器的http端口,,telnet命令會建立一條TCP通道,,然后就可以通過這個通道直接發(fā)送HTTP請求數(shù)據(jù),,獲取響應(yīng)數(shù)據(jù)了。

HTTP協(xié)議進階

代理

HTTP的代理服務(wù)器既是Web服務(wù)器,,又是Web客戶端,。

使用代理可以“接觸”到所有流過的HTTP流量,代理可以對其進行監(jiān)視和修改,。常見的就是對兒童過濾一些“成人”內(nèi)容,;網(wǎng)絡(luò)工程師會利用代理服務(wù)器來提高安全性,它可以限制哪些應(yīng)用層的協(xié)議數(shù)據(jù)可以通過,,過濾“病毒”等數(shù)據(jù),;代理可以存儲緩存的文件,直接返回給訪問者,,無需請求原始的服務(wù)器資源,;對于訪問慢速網(wǎng)絡(luò)上的公共內(nèi)容時,可以假扮服務(wù)器提供服務(wù),,從而提高訪問速度,;這被稱為 反向代理;可以作為內(nèi)容路由器,,如對付費用戶,,則將請求導(dǎo)到緩存服務(wù)器,提高訪問速度,;可以將頁面的語言轉(zhuǎn)換到與客戶端相匹配,這稱為 內(nèi)容轉(zhuǎn)碼器; 匿名代理會主動從HTTP報文中刪除身份相關(guān)的信息,,如 User-Agent, Cookie等字段,。

現(xiàn)實中,請求通過以下幾種方式打到代理服務(wù)器上去:

報文每經(jīng)過一個中間點(代理或網(wǎng)關(guān)),,都需要在首部via字段的末尾插入一個可以代表本節(jié)點的獨特的字符串,,包含實現(xiàn)的協(xié)議版本和主機地址。注意圖中的via字段,。

請求和響應(yīng)的報文傳輸路徑通常都是一致的,,只不過方向是相反的。因此,,響應(yīng)報文上的via字段表示的中間節(jié)點的順序是剛好相反的,。

緩存

當有很多請求訪問同一個頁面時,服務(wù)器會多次傳輸同一份數(shù)據(jù),,這些數(shù)據(jù)重復(fù)地在網(wǎng)絡(luò)中傳輸著,,消耗著大量帶寬。如果將這些數(shù)據(jù)緩存下來,,就可以提高響應(yīng)速度,,節(jié)省網(wǎng)絡(luò)帶寬了,。

大部分緩存只有在客戶端發(fā)起請求,并且副本已經(jīng)比較舊的情況下才會對副本的新鮮度進行檢測,。最常用的請求首部是 If-Modified-Since, 如果在xx時間(此時間即為If-Modified-Since的值)之后內(nèi)容沒有變化,,服務(wù)器會回應(yīng)一個 304NotModified. 否則,服務(wù)器會正常響應(yīng),,并返回原始的文件數(shù)據(jù),,而這個過程中被稱為 再驗證命中

再驗證可能出現(xiàn)命中或未命中的情況,。未命中時,,服務(wù)器回復(fù) 200OK,并且返回完整的數(shù)據(jù),;命中時,,服務(wù)器回復(fù) 304NotModified; 還有一種情況,緩存被刪除了,,那么根據(jù)響應(yīng)狀態(tài)碼,,緩存服務(wù)器也會刪除自己緩存的副本。

順帶提一句,,若要在項目中使用緩存,,就一定要關(guān)注緩存命中比例。若命中比例不高,,就要重新考慮設(shè)置緩存的必要性了,。

緩存服務(wù)器返回響應(yīng)的時候,是基于已緩存的服務(wù)器響應(yīng)的首部,,再對一些首部字段做一些微調(diào),。比如向其中插入新鮮度信息(如 Age, Expires首部等),而且通常會包含一個 via首部來說明緩存是由一個緩存代理提供的,。注意,,這時不要修改 Date字段,它表示原始服務(wù)器最初構(gòu)建這條響應(yīng)的日期,。

HTTP通過 文檔過期機制服務(wù)器再驗證機制保持已緩存數(shù)據(jù)和服務(wù)器間的數(shù)據(jù)充分一致,。

文檔過期通過如下首部字段來表示緩存的有效期:

當上面兩個字段暗示的過期時間已到,需要向服務(wù)器再次驗證文檔的新鮮度,。如果這時緩存仍和服務(wù)器上的原始文檔一致,,緩存只需要更新頭部的相關(guān)字段。如上表中提到的 Expires字段等,。

為了更好的節(jié)省網(wǎng)絡(luò)流量,,緩存服務(wù)器可以通過相關(guān)首部向原始服務(wù)器發(fā)送一個 條件GET請求, 這樣只有在緩存真正過期的情況下,才會返回原始的文檔,否則只會返回相關(guān)的首部,。 條件GET請求會用到如下的字段:

cookie

cookie是服務(wù)器“貼在”客戶端身上的標簽,,由客戶端維護的狀態(tài)片段,并且只會回送給合適的站點,。

有兩類cookie: 會話cookie,、持久cookie. 會話cookie在退出瀏覽器后就被刪除了;而持久cookie則保存在硬盤中,,計算機重啟后仍然存在,。

服務(wù)器在給客戶端的響應(yīng)字段首部加上 Set-cookieSet-cookie2, 值為 名字=值的列表,即可以包含多個字段,。當下次瀏覽器再次訪問到相同的網(wǎng)站時,,會將這些字段通過 Cookie帶上。cookie中保留的內(nèi)容是服務(wù)器給此客戶端打的標簽,,方便服務(wù)進行追蹤的識別碼,。瀏覽器會將cookie以特定的格式存儲在特定的文件中。

瀏覽器只會向產(chǎn)生這條cookie的站點發(fā)生cookie. Set-cookie字段的值會包含 domain這個字段,,告知瀏覽器可以把這條cookie發(fā)送給給相關(guān)的匹配的站點,。 path字段也是相似的功能。如i瀏覽器收到如下的cookie:

  1. Set-cookie: user='mary'; domain='stefno.com'

那么瀏覽器在訪問任意以 stefno.com結(jié)尾的站點都會發(fā)送:

  1. Cookie: user='mary'

實體和編碼

響應(yīng)報文中的body部分傳輸?shù)臄?shù)據(jù)本質(zhì)上都是二進制,。我們從上面的報文數(shù)據(jù)也可以看出來,,都是用十六進制數(shù)來表示,關(guān)鍵是怎么解釋這塊內(nèi)容,。如果 Content-Type定義是 text/plain, 那說明body內(nèi)容就是文本,,我們直接按文本編碼來解釋;如果 Content-Type定義是 image/png, 說明body部分是一幅圖片,,那我們就按圖片的格式去解釋數(shù)據(jù),。

Content-Length標示報文主體部分的數(shù)據(jù)長度大小,如果內(nèi)容是壓縮的,,那它表示的就是壓縮后的大小。另外,, Content-Length在長連接的情況下,,可以對多個報文進行正確地分段。所以,,如果沒有采用分塊編碼,,響應(yīng)數(shù)據(jù)中必須帶上 Content-Length字段。分塊編碼的情形中,,數(shù)據(jù)被拆分成很多小塊,,每塊都有大小說明。因此,任何帶有主體部分的報文(請求或是響應(yīng))都應(yīng)帶上正確的 Content-Length首部,。

HTTP的早期版本采用關(guān)閉連接的方式來劃定報文的結(jié)束,。這帶來的問題是顯而易見的:客戶端并不能分清是因為服務(wù)器正常結(jié)束還是中途崩潰了。這里,,如果是客戶端用關(guān)閉來表示請求報文主體部分的結(jié)束,,是不可取的,因為關(guān)閉之后,,就無法獲取服務(wù)器的響應(yīng)了,。當然,客戶端可以采用半關(guān)閉的方式,,只關(guān)閉數(shù)據(jù)發(fā)送方向,,但是很多服務(wù)器是不識別的,會把半關(guān)閉當成客戶端要成服務(wù)器斷開來處理,。

HTTP報文在傳輸?shù)倪^程中可能會遭到代理或是其他通信實體的無意修改,,為了讓接收方知道這種情況,服務(wù)器會對body部分作一個md5, 并把值放到 Content-MD5這個字段中,。但是,,如果中間的代理即修改了報文主體,又修改了md5, 就不好檢測了,。因此規(guī)定代理是不能修改 Content-MD5首部的,。這樣,客戶端在收到數(shù)據(jù)后,,先進行解碼,,再算出md5, 并與 Content-MD5首部進行比較。這主要是防止代理對報文進行了無意的改動,。

HTTP在發(fā)送內(nèi)容之前需要對其進行編碼,,它是對報文主體進行的可逆變換。比如將報文用gzip格式進行壓縮,,減少傳輸時間,。常見的編碼類型如下:

當然,客戶端為了避免服務(wù)器返回自己不能解碼的數(shù)據(jù),,請求的時候,,會在 Accept-Encoding首部里帶上自己支持的編碼方式。如果不傳輸?shù)脑?,默認可以接受任何編碼方式,。

上面提到的編碼是內(nèi)容編碼,它只是在響應(yīng)報文的主體報文將原始數(shù)據(jù)進行編碼,,改變的是內(nèi)容的格式,。還有另一種編碼: 傳輸編碼。它與內(nèi)容無關(guān),它是為了改變報文數(shù)據(jù)在網(wǎng)絡(luò)上傳輸?shù)姆绞?。傳輸編碼是在HTTP 1.1中引入的一個新特性,。

通常,服務(wù)器需要先生成數(shù)據(jù),,再進行傳輸,,這時,可以計算數(shù)據(jù)的長度,,并將其編碼到 Content-Length中,。但是,有時,,內(nèi)容是動態(tài)生成的,,服務(wù)器希望在數(shù)據(jù)生成之前就開始傳輸,這時,,是沒有辦法知道數(shù)據(jù)大小的,。這種情況下,就要用到 傳輸編碼來標注數(shù)據(jù)的結(jié)束的,。

HTTP協(xié)議中通過如下兩個首部來描述和控制傳輸編碼:

分塊編碼的報文形式是這樣的:

每個分塊包含一個長度值(十六進制,,字節(jié)數(shù))和該分塊的數(shù)據(jù)。 <CR><LF>用于區(qū)隔長度值和數(shù)據(jù),。長度值不包含分塊中的任何 <CR><LF>序列,。最后一個分塊,用長度值0來表示結(jié)束,。注意報文首部包含一個 Trailer:Content-MD5, 所以在緊跟著最后一個報文結(jié)束之后,,就是一個拖掛。其他如,, Content-Length, Trailer, Transfer-Encoding也可以作為拖掛,。

內(nèi)容編碼和傳輸編碼是可以結(jié)合起來使用的。

國際化支持

HTTP為了支持國際化的內(nèi)容,,客戶端要告知服務(wù)器自己能理解的何種語言,,以及瀏覽器上安裝了何種字母表編碼算法。這通過 Accept-CharsetAccept-Language首部實現(xiàn),。

比如:

  1. Accept-Language: fr, en;q=0.8

  2. Accept-Charset: iso-8859-1, utf-8

表示:客戶端接受法語(fr, 優(yōu)先級默認為1.0),、英語(en, 優(yōu)先級為0.8),支持iso-8859-1, utf-8兩種字符集編碼,。服務(wù)器則會在 Content-Type首部里放上 charset.

本質(zhì)上,HTTP報文的body部分存放的就是一串二進制碼,,我們先把二進制碼轉(zhuǎn)換成字符代碼(如ascii是一個字節(jié)表示一個字符,,而utf-8則表示一個字符的字節(jié)數(shù)不定,每個字符1~6個字節(jié)),之后,,用字符代碼去字符集中找到對應(yīng)的元素,。

比較常見的字符集是 US-ASCII: 這個字符集是所有字符集的始祖,早在1968年就發(fā)布了標準,。ASCII碼的代碼值從0到127, 只需要7個bit位就可以覆蓋代碼空間,。HTTP報文的首部、URL使用的字符集就是ASCII碼,??梢栽倏聪律衔膱笪姆治霾糠值腶csii碼集。

US-ASCII是把每個字符編碼成固定的7位二進制值,。 UTF-8則是無固定的編碼方案,。第一個字節(jié)的高位用來表示編碼后的字符所用的字節(jié)數(shù)(如果所用的字節(jié)數(shù)是5,則第一個字節(jié)前5bit都是1,,第6bit是0),,所需的后續(xù)的字節(jié)都含有6位的代碼值,前兩個bit位是用 10標識,。

舉個例子,,漢字“嚴”的Unicode編碼為 4E25( 100111000100101), 共有15位,落在上表中的第三行,,因此“嚴”的編碼就需要三個字節(jié),。將 100111000100101填入上表中的 c位即可。因此,,嚴的 UTF-8編碼是11100100 10111000 10100101,,轉(zhuǎn)換成十六進制就是E4B8A5. 比如我在谷歌搜索框里搜索“嚴”字,google發(fā)出的請求如下:

  1. https://www./search?q=%E4%B8%A5&oq=%E4%B8%A5&aqs=chrome..69i57j0l5.3802j0j4&sourceid=chrome&ie=UTF-8&gws_rd=cr

q=%E4%B8%A5這個就是搜索的詞了,。

重定向與負載均衡

Web內(nèi)容通常分散地分布在很多地方,,這可以防止“單點故障”,萬一某個地方發(fā)生地震了,,機房被毀了,,那還有其他地方的機房可以提供服務(wù)。一般都會有所謂的“雙活”,,“多活”,,所謂 狡兔三窟嘛。

這樣,,用戶的請求會根據(jù) 負載均衡的原則,,被 重定向到它應(yīng)該去的地方。

HTTP重定向

服務(wù)器收到客戶端請求后,,向客戶端返回一條帶有狀態(tài)碼 302重定向的報文,,告訴他們應(yīng)該去其他的地方試試,。web站點將重定向看成一種簡單的負載均衡策略來使用, 重定向服務(wù)器找到可用的負載最小的機器,,由于服務(wù)器知道客戶端的地址,,理論上來說,可以做到最優(yōu)的重定向選擇,。

當然,,缺點也是顯而易見的,由于客戶端要發(fā)送兩次請求,,因此會增加耗時,。

DNS重定向

DNS將幾個IP地址關(guān)聯(lián)到一個域上,采用算法決定返回的IP地址,??梢允呛唵蔚?輪轉(zhuǎn);也可以是更高級的算法,,如返回負載最輕的服務(wù)器的IP地址,,稱為 負載均衡算法;如果考慮地理位置,,返回給客戶端最近位置的地址,,稱為 鄰接路由算法;還有一種是繞過出現(xiàn)故障的地址,,稱為 故障屏蔽算法,。

DNS服務(wù)器總是會返回所有的IP地址,但是DNS客戶端一般只會使用第一個IP地址,,而且會緩存下來,,之后會一直用這個地址。所以,,DNS輪轉(zhuǎn)通常不會平衡單個客戶端的負載,。但是,由于DNS服務(wù)器對于不同的請求,,總是會返回輪轉(zhuǎn)后的IP地址列表,,因此,會把負載分散到多個客戶端,。

HTTP連接

HTTP連接是HTTP報文傳輸?shù)年P(guān)鍵通道,。

并行連接

對于一個頁面上同時出現(xiàn)多個對象的時候,如果瀏覽器并行地打開多個連接,,同時去獲取這些對象,,多個連接的TCP握手時延可以進行重疊,速度會快起來,。

如一個包含3張圖片的頁面,,瀏覽器要發(fā)送4次HTTP請求來獲取頁面,。1個用于頂層的HTML頁面,3個用于圖片,。如果采用串行方式,那么連接時延會進行疊加,。

采用并行連接之后:

但是并行連接也不絕對提升速度,,如果一個頁面有數(shù)百個內(nèi)嵌對象,那要啟動數(shù)百個連接,,對服務(wù)器的性能也是非常大的挑戰(zhàn),。所以,通常瀏覽器會限制并行連接的總數(shù)據(jù)在一個較小的值,,通常是4個,,而且服務(wù)端可以隨意關(guān)閉客戶端超量的連接。

另一方面,,如果客戶端網(wǎng)絡(luò)帶寬較小,,每個連接都會去爭搶有限的帶寬,每個連接都會獲取較小的速度,,即每個對象都會以較小的速度去加載,。這樣,并行連接帶來的速度提升就會比較小,,甚至沒有提升,。

持久連接

HTTP keep-alive機制

我們知道HTTP請求是“請求-應(yīng)答”模式,每次請求-應(yīng)答都要新建一個連接,,完成之后要斷開連接,。HTTP是無狀態(tài)的,連接之間沒有任何關(guān)系,。

HTTP是應(yīng)用層協(xié)議,,TCP是傳輸層協(xié)議。HTTP底層仍然采用TCP進行傳輸數(shù)據(jù),。TCP為HTTP提供了一層可靠的比特傳輸通道,。HTTP一般交換的數(shù)據(jù)都不大,而每次連接都要進行TCP三次握手,,很大一部分時間都消耗在這上面,,有時候甚至能達到50%。如果能復(fù)用連接,,就可以減少由于TCP三次握手所帶來的時延,。

HTTP 1.1默認開啟keep-alive機制,從上面抓到的包也可以看到,。這樣,,數(shù)據(jù)傳輸完成之后保持TCP連接不斷開,,之后同域名下復(fù)用連接,繼續(xù)用這個通道傳輸數(shù)據(jù),。服務(wù)器在響應(yīng)一個請求后,,可以保持這個連接keep-alive timeout的時間,在這個時間內(nèi)沒有請求,,則關(guān)閉此連接,;否則,重新開始倒計時keep-alive timeout時間,。

HTTP有keep-alive機制,,目的是可以在一個TCP 連接上傳輸多個HTTP事務(wù),以此提高通信效率,。底層的TCP其實也有keep-alive機制,,它是為了探測TCP連接的活躍性。TCP層的keepalive可以在任何一方設(shè)置,,可以是一端設(shè)置,、兩端同時設(shè)置或者兩端都沒有設(shè)置。新建socket的時候需要設(shè)置,,從而使得協(xié)議棧調(diào)用相關(guān)函數(shù)tcpsetkeepalive,,來激活連接的keep-alive屬性。

當網(wǎng)絡(luò)兩端建立了TCP連接之后,,閑置(雙方?jīng)]有任何數(shù)據(jù)流發(fā)送往來)時間超過 tcp_keepalive_time后,,服務(wù)器內(nèi)核就會嘗試向客戶端發(fā)送偵測包,來判斷TCP連接狀況(有可能客戶端崩潰,、強制關(guān)閉了應(yīng)用,、主機不可達等等)。如果沒有收到對方的回答(ack包),,則會在 tcp_keepalive_intvl后再次嘗試發(fā)送偵測包,,直到收到對方的ack,如果一直沒有收到對方的ack,一共會嘗試 tcpkeepaliveprobes次,每次的間隔時間在這里分別是15s, 30s, 45s, 60s, 75s,。如果嘗試 tcp_keepalive_probes次后,依然沒有收到對方的ack包,,則會丟棄該TCP連接。TCP連接默認閑置時間是2小時,,一般設(shè)置為30分鐘足夠了,。

管道化連接

在keep-alive的基礎(chǔ)上,我們可以做地更進一步,,在響應(yīng)到達之前,,我們將多條請求按序放入請求隊列,服務(wù)端在收到請求后,,必須按照順序?qū)?yīng)請求的響應(yīng),。但由于網(wǎng)絡(luò)環(huán)境非常復(fù)雜,,因此即使請求是按順序發(fā)送的,也不一定是按順序到達服務(wù)端的,。而且就算是服務(wù)端按序處理的,,也不一定是按序返回給客戶端,所以最好是在響應(yīng)中附帶一些可以標識請求的參數(shù),。

為了安全起見,,管道化的連接只適合“冪等”的請求,一般我們認為:GET/HEAD/PUT/DELETE/TRACE/OPTIONS等方法都是冪等的,。

小結(jié)

以上,就是所有HTTP的通信細節(jié)了,,足夠在日常開發(fā) 作中使用了,。更多沒有涉及的細節(jié)可以在用到的時候再去仔細研究。

文章看完了,,不知道你對HTTP的理解有沒有更上一層樓,?歡迎一起交流探討。

參考資料

【http長連接】https://www.cnblogs.com/cswuyg/p/3653263.html 【http/tcp keep alive】https://segmentfault.com/a/1190000012894416 【http/tcp keep alive】http://www./academy/detail/23350305 【http/tcp keep alive】https:///articles/8020/on-the-keep-alive-and-tcp-keep-alive-in-the-http-protocol 【tcp keep alive】http://blog.51cto.com/zxtong/1788252 【http權(quán)威指南】https://book.douban.com/subject/10746113/ 【HTTP狀態(tài)碼】https://www.cnblogs.com/starof/p/5035119.html 【HTTP協(xié)議】https://www.cnblogs.com/ranyonsue/p/5984001.html 【HTTP狀態(tài)分類】http://www.runoob.com/http/http-status-codes.html 【url編碼】http://www./blog/2010/02/url_encoding.html

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,,所有內(nèi)容均由用戶發(fā)布,,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式,、誘導(dǎo)購買等信息,,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,,請點擊一鍵舉報,。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多