用雙十一的故事串起碎片的網絡協議(中)

上一節我們講到,手機App經過了一個複雜的過程,終於拿到了電商網站的SLB的IP地址,是不是該下單了?

別忙,俗話說的好,買東西要貨比三家。大部分客戶在購物之前要看很多商品圖片,比來比去,最後好不容易纔下決心,點了下單按鈕。下單按鈕一按,就要開始建立連接。建立連接這個過程也挺複雜的,最終還要經過層層封裝,才構建出一個完整的網絡包。今天我們就來看這個過程。

4.購物之前看圖片,靜態資源CDN

客戶想要在購物網站買一件東西的時候,一般是先去詳情頁看看圖片,是不是想買的那一款。

我們部署電商應用的時候,一般會把靜態資源保存在兩個地方,一個是接入層nginx後面的varnish緩存裏面,一般是靜態頁面;對於比較大的、不經常更新的靜態圖片,會保存在對象存儲裏面。這兩個地方的靜態資源都會配置CDN,將資源下發到邊緣節點。

配置了CDN之後,權威DNS服務器上,會爲靜態資源設置一個CNAME別名,指向另外一個域名cdn.com,返回給本地DNS服務器。
 
當本地DNS服務器拿到這個新的域名時,需要繼續解析這個新的域名。這個時候,再訪問的時候就不是原來的權威DNS服務器了,而是 cdn.com 的權威DNS服務器。這是CDN自己的權威DNS服務器。

在這個服務器上,還是會設置一個CNAME,指向另外一個域名,也即CDN網絡的全局負載均衡器。
 
本地DNS服務器去請求CDN的全局負載均衡器解析域名,全局負載均衡器會爲用戶選擇一臺合適的緩存服務器提供服務,將IP返回給客戶端,客戶端去訪問這個邊緣節點,下載資源。緩存服務器響應用戶請求,將用戶所需內容傳送到用戶終端。

如果這臺緩存服務器上並沒有用戶想要的內容,那麼這臺服務器就要向它的上一級緩存服務器請求內容,直至追溯到網站的源服務器,將內容拉到本地。

5.看上寶貝點下單,雙方開始建連接

當你瀏覽了很多圖片,發現實在喜歡某個商品,於是決定下單購買。

電商網站會對下單的情況提供RESTful的下單接口,而對於下單這種需要保密的操作,需要通過HTTPS協議進行請求。

在所有這些操作之前,首先要做的事情是建立連接

HTTPS協議是基於TCP協議的,因而要先建立TCP的連接。在這個例子中,TCP的連接是從手機上的App和負載均衡器SLB之間的。

儘管中間要經過很多的路由器和交換機,但是TCP的連接是端到端的。TCP這一層和更上層的HTTPS無法看到中間的包的過程。儘管建立連接的時候,所有的包都逃不過在這些路由器和交換機之間的轉發,轉發的細節我們放到那個下單請求的發送過程中詳細解讀,這裏只看端到端的行爲。

對於TCP連接來講,需要通過三次握手建立連接,爲了維護這個連接,雙方都需要在TCP層維護一個連接的狀態機。

一開始,客戶端和服務端都處於CLOSED狀態。服務端先是主動監聽某個端口,處於LISTEN狀態。然後客戶端主動發起連接SYN,之後處於SYN-SENT狀態。服務端收到發起的連接,返回SYN,並且ACK客戶端的SYN,之後處於SYN-RCVD狀態。

客戶端收到服務端發送的SYN和ACK之後,發送ACK的ACK,之後處於ESTABLISHED狀態。這是因爲,它一發一收成功了。服務端收到ACK的ACK之後,處於ESTABLISHED狀態,因爲它的一發一收也成功了。

當TCP層的連接建立完畢之後,接下來輪到HTTPS層建立連接了,在HTTPS的交換過程中,TCP層始終處於ESTABLISHED。

對於HTTPS,客戶端會發送Client Hello消息到服務器,用明文傳輸TLS版本信息、加密套件候選列表、壓縮算法候選列表等信息。另外,還會有一個隨機數,在協商對稱密鑰的時候使用。
 
然後,服務器會返回Server Hello消息,告訴客戶端,服務器選擇使用的協議版本、加密套件、壓縮算法等。這也有一個隨機數,用於後續的密鑰協商。
 
然後,服務器會給你一個服務器端的證書,然後說:“Server Hello Done,我這裏就這些信息了。”
 
客戶端當然不相信這個證書,於是你從自己信任的CA倉庫中,拿CA的證書裏面的公鑰去解密電商網站的證書。如果能夠成功,則說明電商網站是可信的。這個過程中,你可能會不斷往上追溯CA、CA的CA、CA的CA的CA,反正直到一個授信的CA,就可以了。
 
證書驗證完畢之後,覺得這個服務端是可信的,於是客戶端計算產生隨機數字Pre-master,發送Client Key Exchange,用證書中的公鑰加密,再發送給服務器,服務器可以通過私鑰解密出來。
 
接下來,無論是客戶端還是服務器,都有了三個隨機數,分別是:自己的、對端的,以及剛生成的Pre-Master隨機數。通過這三個隨機數,可以在客戶端和服務器產生相同的對稱密鑰。
 
有了對稱密鑰,客戶端就可以說:“Change Cipher Spec,咱們以後都採用協商的通信密鑰和加密算法進行加密通信了。”
 
然後客戶端發送一個Encrypted Handshake Message,將已經商定好的參數等,採用協商密鑰進行加密,發送給服務器用於數據與握手驗證。
 
同樣,服務器也可以發送Change Cipher Spec,說:“沒問題,咱們以後都採用協商的通信密鑰和加密算法進行加密通信了”,並且也發送Encrypted Handshake Message的消息試試。

當雙方握手結束之後,就可以通過對稱密鑰進行加密傳輸了。

真正的下單請求封裝成網絡包的發送過程,我們先放一放,我們來接着講這個網絡包的故事。

6.發送下單請求網絡包,西行需要出網關

當客戶端和服務端之間建立了連接後,接下來就要發送下單請求的網絡包了。

在用戶層發送的是HTTP的網絡包,因爲服務端提供的是RESTful API,因而HTTP層發送的就是一個請求。

POST /purchaseOrder HTTP/1.1
Host: www.geektime.com
Content-Type: application/json; charset=utf-8
Content-Length: nnn
 
{
 "order": {
  "date": "2018-07-01",
  "className": "趣談網絡協議",
  "Author": "劉超",
  "price": "68"
 }
}

HTTP的報文大概分爲三大部分。第一部分是請求行,第二部分是請求的首部,第三部分纔是請求的正文實體

在請求行中,URL就是 www.geektime.com/purchaseOrder ,版本爲HTTP 1.1。

請求的類型叫作POST,它需要主動告訴服務端一些信息,而非獲取。需要告訴服務端什麼呢?一般會放在正文裏面。正文可以有各種各樣的格式,常見的格式是JSON。

請求行下面就是我們的首部字段。首部是key value,通過冒號分隔。

Content-Type是指正文的格式。例如,我們進行POST的請求,如果正文是JSON,那麼我們就應該將這個值設置爲JSON。

接下來是正文,這裏是一個JSON字符串,裏面通過文本的形式描述了,要買一個課程,作者是誰,多少錢。

這樣,HTTP請求的報文格式就拼湊好了。接下來瀏覽器或者移動App會把它交給下一層傳輸層。

怎麼交給傳輸層呢?也是用Socket進行程序設計。如果用的是瀏覽器,這些程序不需要你自己寫,有人已經幫你寫好了;如果在移動APP裏面,一般會用一個HTTP的客戶端工具來發送,並且幫你封裝好。

HTTP協議是基於TCP協議的,所以它使用面向連接的方式發送請求,通過Stream二進制流的方式傳給對方。當然,到了TCP層,它會把二進制流變成一個的報文段發送給服務器。

在TCP頭裏面,會有源端口號和目標端口號,目標端口號一般是服務端監聽的端口號,源端口號在手機端,往往是隨機分配一個端口號。這個端口號在客戶端和服務端用於區分請求和返回,發給那個應用。

在IP頭裏面,都需要加上自己的地址(即源地址)和它想要去的地方(即目標地址)。當一個手機上線的時候,PGW會給這個手機分配一個IP地址,這就是源地址,而目標地址則是雲平臺的負載均衡器的外網IP地址。

在IP層,客戶端需要查看目標地址和自己是否是在同一個局域網,計算是否是同一個網段,往往需要通過CIDR子網掩碼來計算。

對於這個下單場景,目標IP和源IP不會在同一個網段,因而需要發送到默認的網關。一般通過DHCP分配IP地址的時候,也會同時配置默認網關的IP地址。

但是客戶端不會直接使用默認網關的IP地址,而是發送ARP協議,來獲取網關的MAC地址,然後將網關MAC作爲目標MAC,自己的MAC作爲源MAC,放入MAC頭,發送出去。

一個完整的網絡包的格式是這樣的。


 
真不容易啊,本來以爲上篇就發送下單包了,結果到中篇這個包還沒發送出去,只是封裝了一個如此長的網絡包。彆着急,你可以自己先預想一下,接下來的內容將在後面的文章中更新,歡迎你來訂閱我的專欄。

戳我試讀或訂閱《趣談網絡協議》,歡迎你來給我留言和討論。

文章選自《趣談網絡協議》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章