上一篇文章中,我們學會了用wireshark和tcpdump來分析TCP的“三次握手,四次揮手”,非常好用。這哥倆就是傳說中的錘子
,拿著錘子
,看什麼都像釘子
!在這篇文章中,我對準了HTTP
這顆釘子砸下去,咳咳。
為了對網路資料包的“流轉”有更加深刻的理解,我在docker(遠端)上部署一個服務,支援http方式呼叫。從客戶端(本地)用http方式請求其中的一個介面,並得到響應資料。同時本地通過wireshark抓包,遠端用tcpdump抓包,然後分析過程中的所有通訊細節。悲劇是把美好的東西撕碎給人看,而我則是把複雜的東西撕碎了給人看。
文章稍長,請在看本文時保持耐心。我先通過工具獲取HTTP通訊的資料包,再來抽絲剝繭,深入二進位制的天地裡,解密HTTP所有的通訊細節。分析過程中,由點到面,將相關知識串接起來。保證全篇讀完之後,你對HTTP的理解會上升一個臺階!
為了更好的閱讀體驗,我手動貼上本文的目錄:
HTTP報文截獲
背景介紹
我手頭現在有一個地理幾何相關的服務,它提供一組介面對外使用。其中有一個介面是Fence2Area
. 使用方傳入一個圍欄(由點的列表組成,點由<經度,緯度>表示)、點的座標系型別(谷歌地圖用的是wgs84, 國內騰訊、高德用的是soso, 而百度用的是另一套自己的座標系),介面輸出的則是圍欄的面積。
我請求服務的“Fence2Area”介面,輸入圍欄(fence)頂點(lng, lat)座標、座標系型別(coordtype),輸出的則是多邊形的面積(area).
一次正常的請求示例url, 這個大家都不陌生(我用docker_ip代替真實的ip):
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}
複製程式碼
請求發出後,伺服器進行處理,之後,客戶端收到返回的資料如下:
{
"data": {
"area": 48764135597.842606
},
"errstr": ""
}
複製程式碼
area
欄位表示面積,errstr
表示出錯資訊,空說明沒有出錯。
抓包
在真正傳送請求之前,需要進行抓包前的設定。在本地mac,我用wireshark; 而在遠端docker上,我用tcpdump工具。
mac本地
設定wireshark包過濾器,監控本地主機和遠端docker之間的通訊。
ip.addr eq docker_ip
複製程式碼
點選開始捕獲。
遠端docker
該服務通過7080埠對外提供,使用如下命令捕獲網路包:
tcpdump -w /tmp/testHttp.cap port 7080 -s0
複製程式碼
請求 && 分析
準備工作做完,我選了一個神聖的時刻,在本地通過瀏覽器訪問如下url:
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網路資料包。
關閉服務程式
正式請求之前,我們先看一下幾種特殊的情形。
首先,關閉gcs服務程式,請求直接返回RST報文。
如上圖,我在請求的時候,訪問服務端的另一個埠5010
, 這個埠沒有服務監聽,和關閉gcs服務程式是同樣的效果。可以看到,客戶端傳送SYN報文,但直接被遠端docker RST掉了。因為服務端作業系統找不到監聽此埠的程式。
關閉docker
關閉docker, 由於傳送的SYN報文段得不到響應,因此會進行重試,mac下重試的次數為10次。
先每隔1秒重試了5次,再用“指數退避”的時間間隔重試,2s, 4s, 8s, 16s, 32s. 最後結束。
重啟docker
先進行一次正常的訪問,隨後重啟docker。並再次在本地訪問以上url, 瀏覽器這時還是用的上一次的埠,訪問到服務端後,因為它已經重啟了,所以服務端已經沒有這個連線的訊息了。因此會返回一個RST報文。
正常請求
服務正常啟動,正常傳送請求,這次請求成功,那是當然的,嘿嘿!
這是在mac上用wireshark捕獲的資料包,共7個包,前三個包為3次握手的包,第四個包為HTTP
層傳送的請求資料,第五個包為服務端的TCP 確認報文,第六個包為服務端在HTTP
層傳送的響應資料,第七個包為mac對第六個包的確認報文。
重點來關注後面幾個包,先看第四個包,
0x0000: 4500 0295 0000 4000 3606 623b ac17 ccdc
0x0010: 0a60 5cd4 db9b 1ba8 a59a 46ce 6d03 e87d
0x0020: 8018 1015 0ee7 0000 0101 080a 2e4c b2ef
0x0030: 0f20 3acf 4745 5420 2f64 6174 613f 636d
0x0040: 643d 4665 6e63 6532 4172 6561 266d 6574
0x0050: 613d 7b25 3232 6361 6c6c 6572 2532 323a
0x0060: 2532 3274 6573 7425 3232 2c25 3232 5472
0x0070: 6163 6549 6425 3232 3a25 3232 7465 7374
0x0080: 2532 327d 2672 6571 7565 7374 3d7b 2532
0x0090: 3266 656e 6365 2532 323a 5b7b 2532 326c
0x00a0: 6e67 2532 323a 3130 2e32 2c25 3232 6c61
0x00b0: 7425 3232 3a31 302e 327d 2c25 3230 7b25
0x00c0: 3232 6c6e 6725 3232 3a31 302e 322c 2532
0x00d0: 326c 6174 2532 323a 382e 327d 2c25 3230
0x00e0: 7b25 3232 6c6e 6725 3232 3a38 2e32 2c25
0x00f0: 3232 6c61 7425 3232 3a38 2e32 7d2c 2532
0x0100: 307b 2532 326c 6e67 2532 323a 382e 322c
0x0110: 2532 326c 6174 2532 323a 3130 2e32 7d5d
0x0120: 2c25 3232 636f 6f72 6474 7970 6525 3232
0x0130: 3a32 7d20 4854 5450 2f31 2e31 0d0a 486f
0x0140: 7374 3a20 3130 2e39 362e 3932 2e32 3132
0x0150: 3a37 3038 300d 0a55 7067 7261 6465 2d49
0x0160: 6e73 6563 7572 652d 5265 7175 6573 7473
0x0170: 3a20 310d 0a41 6363 6570 743a 2074 6578
0x0180: 742f 6874 6d6c 2c61 7070 6c69 6361 7469
0x0190: 6f6e 2f78 6874 6d6c 2b78 6d6c 2c61 7070
0x01a0: 6c69 6361 7469 6f6e 2f78 6d6c 3b71 3d30
0x01b0: 2e39 2c2a 2f2a 3b71 3d30 2e38 0d0a 5573
0x01c0: 6572 2d41 6765 6e74 3a20 4d6f 7a69 6c6c
0x01d0: 612f 352e 3020 284d 6163 696e 746f 7368
0x01e0: 3b20 496e 7465 6c20 4d61 6320 4f53 2058
0x01f0: 2031 305f 3133 5f36 2920 4170 706c 6557
0x0200: 6562 4b69 742f 3630 352e 312e 3135 2028
0x0210: 4b48 544d 4c2c 206c 696b 6520 4765 636b
0x0220: 6f29 2056 6572 7369 6f6e 2f31 322e 302e
0x0230: 3220 5361 6661 7269 2f36 3035 2e31 2e31
0x0240: 350d 0a41 6363 6570 742d 4c61 6e67 7561
0x0250: 6765 3a20 7a68 2d63 6e0d 0a41 6363 6570
0x0260: 742d 456e 636f 6469 6e67 3a20 677a 6970
0x0270: 2c20 6465 666c 6174 650d 0a43 6f6e 6e65
0x0280: 6374 696f 6e3a 206b 6565 702d 616c 6976
0x0290: 650d 0a0d 0a
複製程式碼
我們來逐位元組分析。
位元組值 | 位元組含義 |
---|---|
0x4 | IP版本為ipv4 |
0x5 | 首部長度為5 * 4位元組=20B |
0x00 | 服務型別,現在基本都置為0 |
0x0295 | 總長度為661位元組,即整個包的長度是661位元組 |
0x0000 | 標識。同一個資料包的唯一標識。當IP資料包被拆分時,會複製到每一個資料中。 |
0x4000 | 3bit 標誌 + 13bit 片偏移 。3bit 標誌對應 R、DF、MF。目前只有後兩位有效,DF位:為1表示不分片,為0表示分片。MF:為1表示“更多的片”,為0表示這是最後一片。13bit 片位移:本分片在原先資料包文中相對首位的偏移位。(需要再乘以8 ) |
0x36 | 生存時間TTL。IP報文所允許通過的路由器的最大數量。每經過一個路由器,TTL減1,當為 0 時,路由器將該資料包丟棄。TTL 欄位是由傳送端初始設定一個 8 bit欄位.推薦的初始值由分配數字 RFC 指定。傳送 ICMP 回顯應答時經常把 TTL 設為最大值 255。TTL可以防止資料包陷入路由迴圈。 此處為54. |
0x06 | 協議型別。指出IP報文攜帶的資料使用的是哪種協議,以便目的主機的IP層能知道要將資料包上交到哪個程式。TCP 的協議號為6,UDP 的協議號為17。ICMP 的協議號為1,IGMP 的協議號為2。該 IP 報文攜帶的資料使用 TCP 協議,得到了驗證。 |
0x623b | 16bitIP首部校驗和。 |
0xac17 ccdc | 32bit源ip地址。 |
0x0a60 5cd4 | 32bit目的ip地址。 |
剩餘的資料部分即為TCP協議相關的。TCP也是20B固定長度+可變長度部分。
位元組值 | 位元組含義 |
---|---|
0xdb9b | 16bit源埠。56219 |
0x1ba8 | 16bit目的埠7080 |
0xa59a 46ce | 32bit序列號。2778351310 |
0x6d03 e87d | 32bit確認號。1828972669 |
0x8 | 4bit首部長度,以4byte為單位。共8*4=32位元組。因此TCP報文的可選長度為32-20=12位元組 |
0b000000 | 6bit保留位。目前置為0. |
0b011000 | 6bitTCP標誌位。從左到右依次是緊急 URG、確認 ACK、推送 PSH、復位 RST、同步 SYN 、終止 FIN。ack有效,同時psh有效 |
0x1015 | 滑動視窗大小,滑動視窗即tcp接收緩衝區的大小,用於tcp擁塞控制。4117 |
0x0ee7 | 16bit校驗和。 |
0x0000 | 緊急指標。僅在 URG = 1時才有意義,它指出本報文段中的緊急資料的位元組數。當 URG = 1 時,傳送方 TCP 就把緊急資料插入到本報文段資料的最前面,而在緊急資料後面的資料仍是普通資料。 |
可變長度部分,協議如下:
位元組值 | 位元組含義 |
---|---|
0x01 | 無操作 |
0x01 | 無操作 |
0x0402 | 表示支援SACK |
0x080a 2e4c b2ef 0f20 3acf | 時間戳。Ts val=0x2e4c b2ef=776778479, ecr=0x0f20 3acf=253770447 |
剩下來的就是資料部分了。我們一行一行地看。因為http是字元流,所以我們先看一下ascii字符集,執行命令:
man ascii
複製程式碼
可以得到ascii碼,我們直接看十六進位制的結果:
行首地址 | 位元組流 | 字元 |
---|---|---|
0x0030 | 4745 5420 2f64 6174 613f 636d | GE T /d at a? cm |
0x0040 | 643d 4665 6e63 6532 4172 6561 266d 6574 | d= Fe nc e2 Ar ea &m et |
0x0050 | 613d 7b25 3232 6361 6c6c 6572 2532 323a | a= {% 22 ca ll er %2 2: |
0x0060 | 2532 3274 6573 7425 3232 2c25 3232 5472 | %2 2t es t% 22 ,% 22 Tr |
0x0070 | 6163 6549 6425 3232 3a25 3232 7465 7374 | ac eI d% 22 :% 22 te st |
0x0080 | 2532 327d 2672 6571 7565 7374 3d7b 2532 | %2 2} &r eq ue st ={ %2 |
0x0090 | 3266 656e 6365 2532 323a 5b7b 2532 326c | 2f en ce %2 2: [{ %2 2l |
0x00a0 | 6e67 2532 323a 3130 2e32 2c25 3232 6c61 | ng %2 2: 10 .2 ,% 22 la |
0x00b0 | 7425 3232 3a31 302e 327d 2c25 3230 7b25 | t% 22 :1 0. 2} ,% 20 {% |
0x00c0 | 3232 6c6e 6725 3232 3a31 302e 322c 2532 | 22 ln g% 22 :1 0. 2, %2 |
0x00d0 | 326c 6174 2532 323a 382e 327d 2c25 3230 | 2l at %2 2: 8. 2} ,% 20 |
0x00e0 | 7b25 3232 6c6e 6725 3232 3a38 2e32 2c25 | {% 22 ln g% 22 :8 .2 ,% |
0x00f0 | 3232 6c61 7425 3232 3a38 2e32 7d2c 2532 | 22 la t% 22 :8 .2 }, %2 |
0x0100 | 307b 2532 326c 6e67 2532 323a 382e 322c | 0{ %2 2l ng %2 2: 8. 2, |
0x0110 | 2532 326c 6174 2532 323a 3130 2e32 7d5d | %2 2l at %2 2: 10 .2 } ] |
0x0120 | 2c25 3232 636f 6f72 6474 7970 6525 3232 | ,% 22 co or dt yp e% 22 |
0x0130 | 3a32 7d20 4854 5450 2f31 2e31 0d0a 486f | :2 } HT TP /1 .1 crnl Ho |
0x0140 | 7374 3a20 3130 2e39 362e 3932 2e32 3132 | st : 10 .9 6. 92 .2 12 |
0x0150 | 3a37 3038 300d 0a55 7067 7261 6465 2d49 | :7 08 0cr nlU pg ra de -I |
0x0160 | 6e73 6563 7572 652d 5265 7175 6573 7473 | ns ec ur e- Re qu es ts |
0x0170 | 3a20 310d 0a41 6363 6570 743a 2074 6578 | : 1cr nlA cc ep t: t ex |
0x0180 | 742f 6874 6d6c 2c61 7070 6c69 6361 7469 | t/ ht ml ,a pp li ca ti |
0x0190 | 6f6e 2f78 6874 6d6c 2b78 6d6c 2c61 7070 | on /x ht ml +x ml ,a pp |
0x01a0 | 6c69 6361 7469 6f6e 2f78 6d6c 3b71 3d30 | li ca ti on /x ml ;q =0 |
0x01b0 | 2e39 2c2a 2f2a 3b71 3d30 2e38 0d0a 5573 | .9 ,* /* ;q =0 .8 crnl Us |
0x01c0 | 6572 2d41 6765 6e74 3a20 4d6f 7a69 6c6c | er -A ge nt : Mo zi ll |
0x01d0 | 612f 352e 3020 284d 6163 696e 746f 7368 | a/ 5. 0 (M ac in to sh |
0x01e0 | 3b20 496e 7465 6c20 4d61 6320 4f53 2058 | ; In te l Ma c OS X |
0x01f0 | 2031 305f 3133 5f36 2920 4170 706c 6557 | 1 0_ 13 _6 ) Ap pl eW |
0x0200 | 6562 4b69 742f 3630 352e 312e 3135 2028 | eb Ki t/ 60 5. 1. 15 ( |
0x0210 | 4b48 544d 4c2c 206c 696b 6520 4765 636b | KH TM L, l i k e Ge ck |
0x0220 | 6f29 2056 6572 7369 6f6e 2f31 322e 302e | o) V er si o n /1 2. 0. |
0x0230 | 3220 5361 6661 7269 2f36 3035 2e31 2e31 | 2 Sa fa ri /6 05 .1 .1 |
0x0240 | 350d 0a41 6363 6570 742d 4c61 6e67 7561 | 5cr nlA cc ep t- La ng ua |
0x0250 | 6765 3a20 7a68 2d63 6e0d 0a41 6363 6570 | ge : zh -c ncr nlA cc ep |
0x0260 | 742d 456e 636f 6469 6e67 3a20 677a 6970 | t- En co di ng : gz ip |
0x0270 | 2c20 6465 666c 6174 650d 0a43 6f6e 6e65 | , de fl at ecr nlC on ne |
0x0280 | 6374 696f 6e3a 206b 6565 702d 616c 6976 | ct io n: k ee p- al iv |
0x0290 | 650d 0a0d 0a | ecr nl cr nl |
把上表的最後一列連起來,就是:
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
Host: 10.96.92.212:7080
Upgrade-Insecure-Requests: 1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
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
Accept-Language: zh-cn
Accept-Encoding: gzip, deflate
Connection: keep-alive
複製程式碼
其中,cr nl表示回車,換行。
docker收到資料後,會回覆一個ack包。第四個包的總長度為661位元組,去掉IP頭部20位元組,TCP頭部固定部分20位元組,TCP頭部可選長度為12位元組,共52位元組,因此TCP資料部分總長度為661-52=609位元組。另外,序列號為2778351310.
再來看第5個包,位元組流如下:
0x0000: 4500 0034 d28b 4000 4006 8810 0a60 5cd4
0x0010: ac17 ccdc 1ba8 db9b 6d03 e87d a59a 492f
0x0020: 8010 00ec e04e 0000 0101 080a 0f20 3af7
0x0030: 2e4c b2ef
複製程式碼
位元組值 | 位元組含義 |
---|---|
0x4 | IP版本為ipv4 |
0x5 | 首部長度為5 * 4位元組=20B |
0x00 | 服務型別,現在基本都置為0 |
0x0034 | 總長度為52位元組,即整個包的長度是52位元組 |
0xd28b | 標識。同一個資料包的唯一標識。當IP資料包被拆分時,會複製到每一個資料中。 |
0x4000 | 3bit 標誌 + 13bit 片偏移 。3bit 標誌對應 R、DF、MF。目前只有後兩位有效,DF位:為1表示不分片,為0表示分片。MF:為1表示“更多的片”,為0表示這是最後一片。13bit 片位移:本分片在原先資料包文中相對首位的偏移位。(需要再乘以8 ) |
0x40 | 生存時間TTL。IP報文所允許通過的路由器的最大數量。每經過一個路由器,TTL減1,當為 0 時,路由器將該資料包丟棄。TTL 欄位是由傳送端初始設定一個 8 bit欄位.推薦的初始值由分配數字 RFC 指定。傳送 ICMP 回顯應答時經常把 TTL 設為最大值 255。TTL可以防止資料包陷入路由迴圈。 此處為54. |
0x06 | 協議型別。指出IP報文攜帶的資料使用的是哪種協議,以便目的主機的IP層能知道要將資料包上交到哪個程式。TCP 的協議號為6,UDP 的協議號為17。ICMP 的協議號為1,IGMP 的協議號為2。該 IP 報文攜帶的資料使用 TCP 協議,得到了驗證。 |
0x8810 | 16bitIP首部校驗和。 |
0x0a60 5cd4 | 32bit源ip地址。 |
0xac17 ccdc | 32bit目的ip地址。 |
剩餘的資料部分即為TCP協議相關的。TCP也是20B固定長度+可變長度部分。
位元組值 | 位元組含義 |
---|---|
0x1ba8 | 16bit源埠7080 |
0xdb9b | 16bit目的埠。56219 |
0x6d03 e87d | 32bit序列號。1828972669 |
0xa59a 492f | 32bit確認號。2778351919. 第三個包的序列號為2778351310, 加上資料長度609, 正好相等。 |
0x8 | 4bit首部長度,以4byte為單位。共8*4=32位元組。因此TCP報文的可選長度為32-20=12位元組 |
0b000000 | 6bit保留位。目前置為0. |
0b010000 | 6bitTCP標誌位。從左到右依次是緊急 URG、確認 ACK、推送 PSH、復位 RST、同步 SYN 、終止 FIN。ack有效 |
0x00ec | 滑動視窗大小,滑動視窗即tcp接收緩衝區的大小,用於tcp擁塞控制。4117 |
0xe04e | 16bit校驗和。 |
0x0000 | 緊急指標。僅在 URG = 1時才有意義,它指出本報文段中的緊急資料的位元組數。當 URG = 1 時,傳送方 TCP 就把緊急資料插入到本報文段資料的最前面,而在緊急資料後面的資料仍是普通資料。 |
可變長度部分,協議如下:
位元組值 | 位元組含義 |
---|---|
0x01 | 無操作 |
0x01 | 無操作 |
0x0402 | 表示支援SACK |
0x080a 2e4c b2ef 0f20 3acf | 時間戳。Ts val=253770487, ecr=776778479 |
資料部分為空,這個包僅為確認包。
再來看第六個包,位元組流如下:
0x0000: 4500 00f9 d28c 4000 4006 874a 0a60 5cd4
0x0010: ac17 ccdc 1ba8 db9b 6d03 e87d a59a 492f
0x0020: 8018 00ec e113 0000 0101 080a 0f20 3af8
0x0030: 2e4c b2ef 4854 5450 2f31 2e31 2032 3030
0x0040: 204f 4b0d 0a41 6363 6573 732d 436f 6e74
0x0050: 726f 6c2d 416c 6c6f 772d 4f72 6967 696e
0x0060: 3a20 2a0d 0a44 6174 653a 2054 6875 2c20
0x0070: 3033 204a 616e 2032 3031 3920 3132 3a32
0x0080: 333a 3437 2047 4d54 0d0a 436f 6e74 656e
0x0090: 742d 4c65 6e67 7468 3a20 3438 0d0a 436f
0x00a0: 6e74 656e 742d 5479 7065 3a20 7465 7874
0x00b0: 2f70 6c61 696e 3b20 6368 6172 7365 743d
0x00c0: 7574 662d 380d 0a0d 0a7b 2264 6174 6122
0x00d0: 3a7b 2261 7265 6122 3a34 3837 3634 3133
0x00e0: 3535 3937 2e38 3432 3630 367d 2c22 6572
0x00f0: 7273 7472 223a 2222 7d
複製程式碼
位元組值 | 位元組含義 |
---|---|
0x4 | IP版本為ipv4 |
0x5 | 首部長度為5 * 4位元組=20B |
0x00 | 服務型別,現在基本都置為0 |
0x00f9 | 總長度為249位元組,即整個包的長度是249位元組 |
0xd28c | 標識。同一個資料包的唯一標識。當IP資料包被拆分時,會複製到每一個資料中。 |
0x4000 | 3bit 標誌 + 13bit 片偏移 。3bit 標誌對應 R、DF、MF。目前只有後兩位有效,DF位:為1表示不分片,為0表示分片。MF:為1表示“更多的片”,為0表示這是最後一片。13bit 片位移:本分片在原先資料包文中相對首位的偏移位。(需要再乘以8 ) |
0x40 | 生存時間TTL。IP報文所允許通過的路由器的最大數量。每經過一個路由器,TTL減1,當為 0 時,路由器將該資料包丟棄。TTL 欄位是由傳送端初始設定一個 8 bit欄位.推薦的初始值由分配數字 RFC 指定。傳送 ICMP 回顯應答時經常把 TTL 設為最大值 255。TTL可以防止資料包陷入路由迴圈。 此處為64. |
0x06 | 協議型別。指出IP報文攜帶的資料使用的是哪種協議,以便目的主機的IP層能知道要將資料包上交到哪個程式。TCP 的協議號為6,UDP 的協議號為17。ICMP 的協議號為1,IGMP 的協議號為2。該 IP 報文攜帶的資料使用 TCP 協議,得到了驗證。 |
0x874a | 16bitIP首部校驗和。 |
0x0a60 5cd4 | 32bit源ip地址。 |
0xac17 ccdc | 32bit目的ip地址。 |
剩餘的資料部分即為TCP協議相關的。TCP也是20B固定長度+可變長度部分。
位元組值 | 位元組含義 |
---|---|
0x1ba8 | 16bit源埠7080 |
0xdb9b | 16bit目的埠。56219 |
0x6d03 e87d | 32bit序列號。1828972669 |
0xa59a 492f | 32bit確認號。2778351919 |
0x8 | 4bit首部長度,以4byte為單位。共8*4=32位元組。因此TCP報文的可選長度為32-20=12位元組 |
0b000000 | 6bit保留位。目前置為0. |
0b011000 | 6bitTCP標誌位。從左到右依次是緊急 URG、確認 ACK、推送 PSH、復位 RST、同步 SYN 、終止 FIN。ack有效,同時psh有效 |
0x00ec | 滑動視窗大小,滑動視窗即tcp接收緩衝區的大小,用於tcp擁塞控制。236 |
0xe113 | 16bit校驗和。 |
0x0000 | 緊急指標。僅在 URG = 1時才有意義,它指出本報文段中的緊急資料的位元組數。當 URG = 1 時,傳送方 TCP 就把緊急資料插入到本報文段資料的最前面,而在緊急資料後面的資料仍是普通資料。 |
可變長度部分,協議如下:
位元組值 | 位元組含義 |
---|---|
0x01 | 無操作 |
0x01 | 無操作 |
0x0402 | 表示支援SACK |
0x080a 0f20 3af8 2e4c b2ef | 時間戳。Ts val=0x2e4c b2ef=253770488, ecr=0x0f20 3acf=776778479 |
剩下來的就是資料部分了。我們一行一行地看。
首地址 | 位元組流 | 字元 |
---|---|---|
0x0030 | 4854 5450 2f31 2e31 2032 3030 | HTTP/1.1 200 |
0x0040 | 204f 4b0d 0a41 6363 6573 732d 436f 6e74 | OK \r\n Access-Cont |
0x0050 | 726f 6c2d 416c 6c6f 772d 4f72 6967 696e | rol-Allow-Origin |
0x0060 | 3a20 2a0d 0a44 6174 653a 2054 6875 2c20 | : * \r\n Date: Thu, |
0x0070 | 3033 204a 616e 2032 3031 3920 3132 3a32 | 03 Jan 2019 12:2 |
0x0080 | 333a 3437 2047 4d54 0d0a 436f 6e74 656e | 3:47 GMT \r\n Conten |
0x0090 | 742d 4c65 6e67 7468 3a20 3438 0d0a 436f | t-Length: 48\r\n Co |
0x00a0 | 6e74 656e 742d 5479 7065 3a20 7465 7874 | ntent-Type: text |
0x00b0 | 2f70 6c61 696e 3b20 6368 6172 7365 743d | /plain; charset= |
0x00c0 | 7574 662d 380d 0a0d 0a7b 2264 6174 6122 | utf-8\r\n\r\n{"data" |
0x00d0 | 3a7b 2261 7265 6122 3a34 3837 3634 3133 | :{"area":4876413 |
0x00e0 | 3535 3937 2e38 3432 3630 367d 2c22 6572 | 5597.842606},"er |
0x00f0 | 7273 7472 223a 2222 7d | rstr":""} |
把上表的最後一列連起來,就是:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Date: Thu, 03 Jan 2019 12:23:47 GMT
Content-Length: 48
Content-Type: text/plain; charset=utf-8
{"data":{"area":48764135597.842606},"errstr":""}
複製程式碼
Content-Length: 48,最後一行的長度即為48個位元組。
最後,第七個包,位元組流如下:
0x0000: 4500 0034 0000 4000 3606 649c ac17 ccdc
0x0010: 0a60 5cd4 db9b 1ba8 a59a 492f 6d03 e942
0x0020: 8010 100f 1eb9 0000 0101 080a 2e4c b314
0x0030: 0f20 3af8
複製程式碼
位元組值 | 位元組含義 |
---|---|
0x4 | IP版本為ipv4 |
0x5 | 首部長度為5 * 4位元組=20B |
0x00 | 服務型別,現在基本都置為0 |
0x0034 | 總長度為52位元組,即整個包的長度是52位元組 |
0x0000 | 標識。同一個資料包的唯一標識。當IP資料包被拆分時,會複製到每一個資料中。 |
0x4000 | 3bit 標誌 + 13bit 片偏移 。3bit 標誌對應 R、DF、MF。目前只有後兩位有效,DF位:為1表示不分片,為0表示分片。MF:為1表示“更多的片”,為0表示這是最後一片。13bit 片位移:本分片在原先資料包文中相對首位的偏移位。(需要再乘以8 ) |
0x36 | 生存時間TTL。IP報文所允許通過的路由器的最大數量。每經過一個路由器,TTL減1,當為 0 時,路由器將該資料包丟棄。TTL 欄位是由傳送端初始設定一個 8 bit欄位.推薦的初始值由分配數字 RFC 指定。傳送 ICMP 回顯應答時經常把 TTL 設為最大值 255。TTL可以防止資料包陷入路由迴圈。 此處為54. |
0x06 | 協議型別。指出IP報文攜帶的資料使用的是哪種協議,以便目的主機的IP層能知道要將資料包上交到哪個程式。TCP 的協議號為6,UDP 的協議號為17。ICMP 的協議號為1,IGMP 的協議號為2。該 IP 報文攜帶的資料使用 TCP 協議,得到了驗證。 |
0x649c | 16bitIP首部校驗和。 |
0xac17 ccdc | 32bit源ip地址。 |
0x0a60 5cd4 | 32bit目的ip地址。 |
剩餘的資料部分即為TCP協議相關的。TCP也是20B固定長度+可變長度部分。
位元組值 | 位元組含義 |
---|---|
0xdb9b | 16bit源埠。56219 |
0x1ba8 | 16bit目的埠7080 |
0xa59a 492f | 32bit序列號。2778351919 |
0x6d03 e942 | 32bit確認號號。1828972866. 第六個包的序列號為1828972669, 加上資料長度197, 正好相等 |
0x8 | 4bit首部長度,以4byte為單位。共8*4=32位元組。因此TCP報文的可選長度為32-20=12位元組 |
0b000000 | 6bit保留位。目前置為0 |
0b010000 | 6bitTCP標誌位。從左到右依次是緊急 URG、確認 ACK、推送 PSH、復位 RST、同步 SYN 、終止 FIN。ack有效 |
0x100f | 滑動視窗大小,滑動視窗即tcp接收緩衝區的大小,用於tcp擁塞控制。4111 |
0x1eb9 | 16bit校驗和。 |
0x0000 | 緊急指標。僅在 URG = 1時才有意義,它指出本報文段中的緊急資料的位元組數。當 URG = 1 時,傳送方 TCP 就把緊急資料插入到本報文段資料的最前面,而在緊急資料後面的資料仍是普通資料。 |
可變長度部分,協議如下:
位元組值 | 位元組含義 |
---|---|
0x01 | 無操作 |
0x01 | 無操作 |
0x080a 2e4c b314 0f20 3af8 | 時間戳。Ts val=0x2e4c b314=776778516, ecr=0x0f20 3af8=253770488 |
至此,一次完整的http請求的報文就解析完了。感覺如何,是不是很親切?
HTTP協議分析
上面我們把HTTP協議相關的資料給解構了,下面我將對照上面的資料拆解結果,一步步帶你深入理解HTTP協議。
整體介紹
HTTP
(Hypertext Transfer Protocol)超文字傳輸協議,是在網際網路上進行通訊時使用的一種協議。說得更形象一點:HTTP
是現代網際網路中使用的公共語言。它最著名的應用是用在瀏覽器的伺服器間的通訊。
HTTP屬於應用層協議,底層是靠TCP進行可靠地資訊傳輸。
HTTP在傳輸一段報文時,會以流
的形式將報文資料的內容通過一條開啟
的TCP連線按序傳輸。TCP接到上層應用交給它的資料流之後,會按序將資料流打散成一個個的分段。再交到IP層,通過網路進行傳輸。另一端的接收方則相反,它們將接收到的分段按序組裝好,交給上層HTTP協議進行處理。
編碼
我們再來回顧一下:
url | 值 |
---|---|
原始url | /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 | /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描述的資源為了能通過其他各種協議傳送,但是有些協議在傳輸過程中會剝去一些特定的字元;另一方面,URL還是可讀的,所以那些不可列印的字元就不能在URL中使用了,比如空格;最後,URL還得是完整的,它需要支援所有語言的字元。
總之,基於很多原因,URL設計者將US-ASCII碼和其轉義序列整合到URL中,通過轉義序列,就可以用US-ASCII字符集的有限子集對任意字元或資料進行編碼了。
轉義的方法:百分號(%
)後跟著兩個表示ASCII碼的十六進位制數。比如:
所以上面在瀏覽器傳送給伺服器的URL進行了非“安全字元”編碼,也就不奇怪了吧?
在URL中,當上面的保留字元用在保留用途之外的場合時,需要對URL進行編碼。
MIME型別
響應資料中,我們注意到有一個首部:
Content-Type: text/plain; charset=utf-8
複製程式碼
網際網路上有數千種不同的資料型別,HTTP給每種物件都打上了MIME(Multipurpose Internet Media Extension, 多用途因特網郵件擴充套件)標籤,也就是響應資料中的Content-Type
. MIME本來是用在郵件協議中的,後來被移植到了HTTP中。瀏覽器從伺服器上取回了一個物件時,會去檢視MIME型別,從而得知如何處理這種物件,是該展示圖片,還是呼叫音效卡播放聲音。MIME通過斜槓來標識物件的主型別和其中的特定的子型別,下表展示了一些常見的型別,其中的實體主體是指body部分:
URI/URL/URN
URI(Uniform Resource Identifier, 統一資源識別符號)表示伺服器資源,URL(Uniform Resource Locator, 統一資源定位符)和URN(Uniform Resource Name, 統一資源名)是URI的具體實現。URI是一個通用的概念,由兩個主要的子集URL和URN構成,URL通過位置、URN通過名字來標識資源。
URL定義了資源的位置,表示資源的實際地址,在使用URL的過程中,如果URL背後的資源發生了位置移動,訪問者就找不到它了。這個時候就要用到URN了,它給定資源一個名字,無論它移動到哪裡,都可以通過這個名字來訪問到它,簡直完美!
URL通常的格式是:
協議方案+伺服器地址+具體的資源路徑
複製程式碼
協議方案(scheme),如 http
, ftp
,告知web客戶端怎樣訪問資源);伺服器地址,如 www.oreilly.com
; 具體的資源路徑,如 index.html
.
HTTP方法
HTTP支援幾種不同的請求方法,每種方法對伺服器要求的動作不同,如下圖是幾種常見的方法:
HEAD方法只獲取頭部,不獲取資料部分。通過頭部可以獲取比如資源的型別(Content-Type)、資源的長度(Content-Length)這些資訊。這樣,客戶端可以獲取即將請求資源的一些情況,可以做到心中有數。
POST用於向伺服器傳送資料,常見的是提交表單;PUT用於向伺服器上的資源儲存資料。
狀態碼
每條HTTP的響應報文都會帶上一個三位數字的狀態碼和一條解釋性的“原因短語”,通知客戶端本次請求的狀態,幫助客戶端快速理解事務處理結果,最常見的是:
200 OK
404 Not Found
500 Internal Server Error
複製程式碼
我們平時使用瀏覽器的時候,很多的錯誤碼其實是由瀏覽器處理的,我們感知不到。但是404 Not Found
會穿透重重迷霧,來到我們面前,為何?那是因為他對我們愛的深沉啊!
客戶端可以據此狀態碼,決定下一步的行動(如重定向等)。
三位數字的第一位表示分類:
報文格式
HTTP報文實際上是由一行行的字串組成的,每行字串的末尾用\r\n
分隔,人類可以很方便的閱讀。順便說一句,不是所有的協議都對人類這麼友好的,像thrift協議,直接甩一堆位元組給你,告訴你說0x0001
表示呼叫方法,諸如此類的,你只能對著一個十六進位制的資料塊一個個地去“解碼”。不可能像HTTP協議這樣,直接將字元編碼,人類可以直接讀懂。
舉個簡單的請求報文和響應報文的格式的例子:
實際上,請求報文也是可以有body(主體)部分的。請求報文是由請求行(request line)、請求頭部(header)、空行、請求資料
四個部分組成。唯一要注意的一點就是,請求報文即使body部分是空的,請求頭部後的回車換行
符也是必須要有的。
響應報文的格式和請求報文的格式類似:
請求報文、響應報文的起始行和響應頭部裡的欄位都是文字化、結構化的。而請求body卻可以包含任意二進位制資料(如圖片、視訊、軟體等),當然也可以包含文字。
有些首部是通用的,有些則是請求或者響應報文才會有的。
首部 | 屬性 | 值 | 含義 |
---|---|---|---|
Date | 通用 | Thu, 03 Jan 2019 12:23:47 GMT | 報文構建的時間 |
Accept | 請求報文 | text/html,application/xhtml+xml,application/xm | 客戶端能接收的資料型別 |
Content-Type | 通用 | Content-Type: text/plain; charset=utf-8 | 報文中的body部分的資料型別。注意,若是請求報文中也有資料部分,也是需要此欄位的 |
順便提一下, 用telnet直連伺服器的http埠,telnet命令會建立一條TCP通道,然後就可以通過這個通道直接傳送HTTP請求資料,獲取響應資料了。
HTTP協議進階
代理
HTTP的代理伺服器既是Web伺服器,又是Web客戶端。
使用代理可以“接觸”到所有流過的HTTP流量,代理可以對其進行監視和修改。常見的就是對兒童過濾一些“成人”內容;網路工程師會利用代理伺服器來提高安全性,它可以限制哪些應用層的協議資料可以通過,過濾“病毒”等資料;代理可以儲存快取的檔案,直接返回給訪問者,無需請求原始的伺服器資源;對於訪問慢速網路上的公共內容時,可以假扮伺服器提供服務,從而提高訪問速度;這被稱為反向代理
;可以作為內容路由器,如對付費使用者,則將請求導到快取伺服器,提高訪問速度;可以將頁面的語言轉換到與客戶端相匹配,這稱為內容轉碼器
; 匿名代理
會主動從HTTP報文中刪除身份相關的資訊,如User-Agent
, Cookie
等欄位。
現實中,請求通過以下幾種方式打到代理伺服器上去:
報文每經過一箇中間點(代理或閘道器),都需要在首部via欄位的末尾插入一個可以代表本節點的獨特的字串,包含實現的協議版本和主機地址。注意圖中的via欄位。
請求和響應的報文傳輸路徑通常都是一致的,只不過方向是相反的。因此,響應報文上的via欄位表示的中間節點的順序是剛好相反的。
快取
當有很多請求訪問同一個頁面時,伺服器會多次傳輸同一份資料,這些資料重複地在網路中傳輸著,消耗著大量頻寬。如果將這些資料快取下來,就可以提高響應速度,節省網路頻寬了。
大部分快取只有在客戶端發起請求,並且副本已經比較舊的情況下才會對副本的新鮮度進行檢測。最常用的請求首部是If-Modified-Since
, 如果在xx時間(此時間即為If-Modified-Since的值)之後內容沒有變化,伺服器會迴應一個304 Not Modified
. 否則,伺服器會正常響應,並返回原始的檔案資料,而這個過程中被稱為再驗證命中
。
再驗證可能出現命中或未命中的情況。未命中時,伺服器回覆200 OK
,並且返回完整的資料;命中時,伺服器回覆304 Not Modified
; 還有一種情況,快取被刪除了,那麼根據響應狀態碼,快取伺服器也會刪除自己快取的副本。
順帶提一句,若要在專案中使用快取,就一定要關注快取命中比例。若命中比例不高,就要重新考慮設定快取的必要性了。
快取伺服器返回響應的時候,是基於已快取的伺服器響應的首部,再對一些首部欄位做一些微調。比如向其中插入新鮮度資訊(如Age
, Expires
首部等),而且通常會包含一個via
首部來說明快取是由一個快取代理提供的。注意,這時不要修改Date
欄位,它表示原始伺服器最初構建這條響應的日期。
HTTP通過文件過期機制
和伺服器再驗證機制
保持已快取資料和伺服器間的資料充分一致。
文件過期通過如下首部欄位來表示快取的有效期:
當上面兩個欄位暗示的過期時間已到,需要向伺服器再次驗證文件的新鮮度。如果這時快取仍和伺服器上的原始文件一致,快取只需要更新頭部的相關欄位。如上表中提到的Expires
欄位等。
為了更好的節省網路流量,快取伺服器可以通過相關首部向原始伺服器傳送一個條件GET
請求, 這樣只有在快取真正過期的情況下,才會返回原始的文件,否則只會返回相關的首部。條件GET
請求會用到如下的欄位:
cookie
cookie是伺服器“貼在”客戶端身上的標籤,由客戶端維護的狀態片段,並且只會回送給合適的站點。
有兩類cookie: 會話cookie、持久cookie. 會話cookie在退出瀏覽器後就被刪除了;而持久cookie則儲存在硬碟中,計算機重啟後仍然存在。
伺服器在給客戶端的響應欄位首部加上Set-cookie
或Set-cookie2
, 值為名字=值
的列表,即可以包含多個欄位。當下次瀏覽器再次訪問到相同的網站時,會將這些欄位通過Cookie
帶上。cookie中保留的內容是伺服器給此客戶端打的標籤,方便服務進行追蹤的識別碼。瀏覽器會將cookie以特定的格式儲存在特定的檔案中。
瀏覽器只會向產生這條cookie的站點發生cookie. Set-cookie
欄位的值會包含domain
這個欄位,告知瀏覽器可以把這條cookie傳送給給相關的匹配的站點。path
欄位也是相似的功能。如i瀏覽器收到如下的cookie:
Set-cookie: user="mary"; domain="stefno.com"
複製程式碼
那麼瀏覽器在訪問任意以stefno.com
結尾的站點都會傳送:
Cookie: user="mary"
複製程式碼
實體和編碼
響應報文中的body部分傳輸的資料本質上都是二進位制。我們從上面的報文資料也可以看出來,都是用十六進位制數來表示,關鍵是怎麼解釋這塊內容。如果Content-Type
定義是text/plain
, 那說明body內容就是文字,我們直接按文字編碼來解釋;如果Content-Type
定義是image/png
, 說明body部分是一幅圖片,那我們就按圖片的格式去解釋資料。
Content-Length
標示報文主體部分的資料長度大小,如果內容是壓縮的,那它表示的就是壓縮後的大小。另外,Content-Length
在長連線的情況下,可以對多個報文進行正確地分段。所以,如果沒有采用分塊編碼,響應資料中必須帶上Content-Length
欄位。分塊編碼的情形中,資料被拆分成很多小塊,每塊都有大小說明。因此,任何帶有主體部分的報文(請求或是響應)都應帶上正確的Content-Length
首部。
HTTP的早期版本採用關閉連線的方式來劃定報文的結束。這帶來的問題是顯而易見的:客戶端並不能分清是因為伺服器正常結束還是中途崩潰了。這裡,如果是客戶端用關閉來表示請求報文主體部分的結束,是不可取的,因為關閉之後,就無法獲取伺服器的響應了。當然,客戶端可以採用半關閉的方式,只關閉資料傳送方向,但是很多伺服器是不識別的,會把半關閉當成客戶端要成伺服器斷開來處理。
HTTP報文在傳輸的過程中可能會遭到代理或是其他通訊實體的無意修改,為了讓接收方知道這種情況,伺服器會對body部分作一個md5, 並把值放到Content-MD5
這個欄位中。但是,如果中間的代理即修改了報文主體,又修改了md5, 就不好檢測了。因此規定代理是不能修改Content-MD5
首部的。這樣,客戶端在收到資料後,先進行解碼,再算出md5, 並與Content-MD5
首部進行比較。這主要是防止代理對報文進行了無意的改動。
HTTP在傳送內容之前需要對其進行編碼,它是對報文主體進行的可逆變換。比如將報文用gzip格式進行壓縮,減少傳輸時間。常見的編碼型別如下:
當然,客戶端為了避免伺服器返回自己不能解碼的資料,請求的時候,會在Accept-Encoding
首部裡帶上自己支援的編碼方式。如果不傳輸的話,預設可以接受任何編碼方式。
上面提到的編碼是內容編碼,它只是在響應報文的主體報文將原始資料進行編碼,改變的是內容的格式。還有另一種編碼:傳輸編碼
。它與內容無關,它是為了改變報文資料在網路上傳輸的方式。傳輸編碼是在HTTP 1.1中引入的一個新特性。
通常,伺服器需要先生成資料,再進行傳輸,這時,可以計算資料的長度,並將其編碼到Content-Length
中。但是,有時,內容是動態生成的,伺服器希望在資料生成之前就開始傳輸,這時,是沒有辦法知道資料大小的。這種情況下,就要用到傳輸編碼
來標註資料的結束的。
HTTP協議中通過如下兩個首部來描述和控制傳輸編碼:
欄位 | 含義 | 典型值 |
---|---|---|
Transfer-Encoding | 傳送方告知接收方,我方已經進行了何種傳輸編碼 | chuncked 分塊編碼 |
TE | 請求方告知伺服器可以用哪種傳輸編碼 | trailers, chuncked 接受分塊編碼,並且願意接受在報文結尾上的拖掛 |
分塊編碼的報文形式是這樣的:
每個分塊包含一個長度值(十六進位制,位元組數)和該分塊的資料。<CR><LF>
用於區隔長度值和資料。長度值不包含分塊中的任何<CR><LF>
序列。最後一個分塊,用長度值0來表示結束。注意報文首部包含一個Trailer: Content-MD5
, 所以在緊跟著最後一個報文結束之後,就是一個拖掛。其他如,Content-Length
, Trailer
, Transfer-Encoding
也可以作為拖掛。
內容編碼和傳輸編碼是可以結合起來使用的。
國際化支援
HTTP為了支援國際化的內容,客戶端要告知伺服器自己能理解的何種語言,以及瀏覽器上安裝了何種字母表編碼演算法。這通過Accept-Charset
和Accept-Language
首部實現。
比如:
Accept-Language: fr, en;q=0.8
Accept-Charset: iso-8859-1, utf-8
複製程式碼
表示:客戶端接受法語(fr, 優先順序預設為1.0)、英語(en, 優先順序為0.8),支援iso-8859-1, utf-8兩種字符集編碼。伺服器則會在Content-Type
首部裡放上charset
.
本質上,HTTP報文的body部分存放的就是一串二進位制碼,我們先把二進位制碼轉換成字元程式碼(如ascii是一個位元組表示一個字元,而utf-8則表示一個字元的位元組數不定,每個字元1~6個位元組),之後,用字元程式碼去字符集中找到對應的元素。
比較常見的字符集是US-ASCII
: 這個字符集是所有字符集的始祖,早在1968年就釋出了標準。ASCII碼的程式碼值從0到127, 只需要7個bit位就可以覆蓋程式碼空間。HTTP報文的首部、URL使用的字符集就是ASCII碼。可以再看下上文報文分析部分的acsii碼集。
US-ASCII
是把每個字元編碼成固定的7位二進位制值。UTF-8
則是無固定的編碼方案。第一個位元組的高位用來表示編碼後的字元所用的位元組數(如果所用的位元組數是5,則第一個位元組前5bit都是1,第6bit是0),所需的後續的位元組都含有6位的程式碼值,前兩個bit位是用10
標識。
舉個例子,漢字“嚴”的Unicode編碼為4E25
(100111000100101
), 共有15位,落在上表中的第三行,因此“嚴”的編碼就需要三個位元組。將100111000100101
填入上表中的c
位即可。因此,嚴的UTF-8
編碼是11100100 10111000 10100101,轉換成十六進位制就是E4B8A5. 比如我在谷歌搜尋框裡搜尋“嚴”字,google發出的請求如下:
https://www.google.com.hk/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內容通常分散地分佈在很多地方,這可以防止“單點故障”,萬一某個地方發生地震了,機房被毀了,那還有其他地方的機房可以提供服務。一般都會有所謂的“雙活”,“多活”,所謂狡兔三窟
嘛。
這樣,使用者的請求會根據負載均衡
的原則,被重定向
到它應該去的地方。
HTTP重定向
伺服器收到客戶端請求後,向客戶端返回一條帶有狀態碼302
重定向的報文,告訴他們應該去其他的地方試試。web站點將重定向看成一種簡單的負載均衡策略來使用,重定向
伺服器找到可用的負載最小的機器,由於伺服器知道客戶端的地址,理論上來說,可以做到最優的重定向選擇。
當然,缺點也是顯而易見的,由於客戶端要傳送兩次請求,因此會增加耗時。
DNS重定向
DNS將幾個IP地址關聯到一個域上,採用演算法決定返回的IP地址。可以是簡單的輪轉
;也可以是更高階的演算法,如返回負載最輕的伺服器的IP地址,稱為負載均衡演算法
;如果考慮地理位置,返回給客戶端最近位置的地址,稱為鄰接路由演算法
;還有一種是繞過出現故障的地址,稱為故障遮蔽演算法
。
DNS伺服器總是會返回所有的IP地址,但是DNS客戶端一般只會使用第一個IP地址,而且會快取下來,之後會一直用這個地址。所以,DNS輪轉通常不會平衡單個客戶端的負載。但是,由於DNS伺服器對於不同的請求,總是會返回輪轉後的IP地址列表,因此,會把負載分散到多個客戶端。
HTTP連線
HTTP連線是HTTP報文傳輸的關鍵通道。
並行連線
對於一個頁面上同時出現多個物件的時候,如果瀏覽器並行地開啟多個連線,同時去獲取這些物件,多個連線的TCP握手時延可以進行重疊,速度會快起來。
如一個包含3張圖片的頁面,瀏覽器要傳送4次HTTP請求來獲取頁面。1個用於頂層的HTML頁面,3個用於圖片。如果採用序列方式,那麼連線時延會進行疊加。
採用並行連線之後:
但是並行連線也不絕對提升速度,如果一個頁面有數百個內嵌物件,那要啟動數百個連線,對伺服器的效能也是非常大的挑戰。所以,通常瀏覽器會限制並行連線的總資料在一個較小的值,通常是4個,而且服務端可以隨意關閉客戶端超量的連線。
另一方面,如果客戶端網路頻寬較小,每個連線都會去爭搶有限的頻寬,每個連線都會獲取較小的速度,即每個物件都會以較小的速度去載入。這樣,並行連線帶來的速度提升就會比較小,甚至沒有提升。
持久連線
HTTP keep-alive機制
我們知道HTTP請求是“請求-應答”模式,每次請求-應答都要新建一個連線,完成之後要斷開連線。HTTP是無狀態的,連線之間沒有任何關係。
HTTP是應用層協議,TCP是傳輸層協議。HTTP底層仍然採用TCP進行傳輸資料。TCP為HTTP提供了一層可靠的位元傳輸通道。HTTP一般交換的資料都不大,而每次連線都要進行TCP三次握手,很大一部分時間都消耗在這上面,有時候甚至能達到50%。如果能複用連線,就可以減少由於TCP三次握手所帶來的時延。
HTTP 1.1預設開啟keep-alive機制,從上面抓到的包也可以看到。這樣,資料傳輸完成之後保持TCP連線不斷開,之後同域名下複用連線,繼續用這個通道傳輸資料。伺服器在響應一個請求後,可以保持這個連線keep-alive timeout的時間,在這個時間內沒有請求,則關閉此連線;否則,重新開始倒數計時keep-alive timeout時間。
HTTP有keep-alive機制,目的是可以在一個TCP 連線上傳輸多個HTTP事務,以此提高通訊效率。底層的TCP其實也有keep-alive機制,它是為了探測TCP連線的活躍性。TCP層的keepalive可以在任何一方設定,可以是一端設定、兩端同時設定或者兩端都沒有設定。新建socket的時候需要設定,從而使得協議棧呼叫相關函式tcp_set_keepalive,來啟用連線的keep-alive屬性。
當網路兩端建立了TCP連線之後,閒置(雙方沒有任何資料流傳送往來)時間超過tcp_keepalive_time
後,伺服器核心就會嘗試向客戶端傳送偵測包,來判斷TCP連線狀況(有可能客戶端崩潰、強制關閉了應用、主機不可達等等)。如果沒有收到對方的回答(ack包),則會在 tcp_keepalive_intvl
後再次嘗試傳送偵測包,直到收到對方的ack,如果一直沒有收到對方的ack,一共會嘗試 tcp_keepalive_probes次,每次的間隔時間在這裡分別是15s, 30s, 45s, 60s, 75s。如果嘗試tcp_keepalive_probes
次後,依然沒有收到對方的ack包,則會丟棄該TCP連線。TCP連線預設閒置時間是2小時,一般設定為30分鐘足夠了。
管道化連線
在keep-alive的基礎上,我們可以做地更進一步,在響應到達之前,我們將多條請求按序放入請求佇列,服務端在收到請求後,必須按照順序對應請求的響應。但由於網路環境非常複雜,因此即使請求是按順序傳送的,也不一定是按順序到達服務端的。而且就算是服務端按序處理的,也不一定是按序返回給客戶端,所以最好是在響應中附帶一些可以標識請求的引數。
為了安全起見,管道化的連線只適合“冪等”的請求,一般我們認為:GET/HEAD/PUT/DELETE/TRACE/OPTIONS等方法都是冪等的。
小結
以上,就是所有HTTP的通訊細節了,足夠在日常開發 作中使用了。更多沒有涉及的細節可以在用到的時候再去仔細研究。
文章看完了,不知道你對HTTP的理解有沒有更上一層樓?歡迎一起交流探討。
參考資料
【http長連線】www.cnblogs.com/cswuyg/p/36…
【http/tcp keep alive】segmentfault.com/a/119000001…
【http/tcp keep alive】laravel-china.org/articles/80…
【tcp keep alive】blog.51cto.com/zxtong/1788…
【http權威指南】book.douban.com/subject/107…
【HTTP狀態碼】www.cnblogs.com/starof/p/50…
【HTTP協議】www.cnblogs.com/ranyonsue/p…
【HTTP狀態分類】www.runoob.com/http/http-s…
【url編碼】www.ruanyifeng.com/blog/2010/0…
【http/tcp keep alive】www.nowamagic.net/academy/det…