大家好,我是小蛋,
資料傳輸的整個程序總結為四個詞就是創建、連接、發送、斷開,每個互動程序都會進行詳細說明,耐心往下看哦,
互動程序如下圖所示:

套接字如何創建的
協議堆疊內部結構

如上圖所示,整個請求互動程序分為了幾個部分,首先最上層就是應用程式,接著往下是 Socket 庫,
再下面就是作業系統的內部了,這里面就包括了協議堆疊,協議堆疊上半部分為 TCP 和 UDP ,它們都是負責資料的收發,只是一個需要 連接,一個不需要連接可以直接收發資料,這兩者的詳細區別我會在后期文章單獨講解,這里大家先了解下就行,
協議堆疊的下半部分是 IP 協議,用來真正將資料轉變為網路包進行實際資料傳送的媒介,
IP 下面就是網卡驅動程式,用來控制網卡硬體,
認識套接字
在協議堆疊內部有一塊用來存放控制資訊的記憶體空間,這里面記錄了需要連接的物件 IP 地址、埠號、進行狀態等資訊,
而套接字本身其實只是一個概念,實際并沒有這樣一個東西,這個概念如果非要賦予它一個物體,那控制資訊可以認為就是它的物體,
在發送資料時,我們需要看下套接字要進行連接的物件 IP 地址和埠號;發送資料之后,套接字里面會記錄發送資料經過了多長時間,如果發送收到回應,也會進行記錄,
我們來實際看下 套接字 都有哪些資訊,可以在你電腦的控制臺輸入 netstat 命令進行查詢:

- Proto: 表示協議型別,這里是 tcp ,如果用到了 udp 就會顯示為 udp,
- Local Address : 本機的 IP 地址,
- Foreign Address : 通信物件的 IP 地址
- state : 通信狀態,ESTABLISHED 表示完成連接 ,CLOSE_WAIT 表示等待關閉,還有一個狀態也很常見,LISTENING:等待對方連接,
當瀏覽器通過 Socket 庫向協議堆疊發出 socket 呼叫時,協議堆疊就會根據申請執行創建套接字的操作,
協議堆疊首先會分配一個存放套接字的記憶體空間,然后往里面存入控制資訊,這樣套接字就創建好了,
連接服務器
創建好套接字后,瀏覽器會呼叫 connect ,協議堆疊就會將本地的套接字和服務器的套接字進行連接,
連接就是通信雙方互相交換控制資訊,連接操作所交換的控制資訊是根據通信規則來確定的,只要雙方根據規則進行連接,就能建立起連接關系,完成資料收發的準備,
控制資訊
控制資訊一般可以分為兩類,一類是客戶端和服務器相互聯系時交換的控制資訊,這個資訊是兩者建立連接、資料收發、斷開連接整個通信程序都需要的資訊,一般這些內容是通過 TCP 協議進行定義的,這些資訊會被添加進網路包的開頭,因此也叫作頭部,以太網和 IP 協議也有自己的控制資訊,這個資訊也叫頭部,為了進行區分,我們分別叫作 TCP 頭部、以太網頭部、IP 頭部,

這里羅列了部分 TCP 頭部的資訊,僅供參考,
控制資訊還有一類,是保存在套接字里的,應用程式傳遞的資訊和從通信物件接受的資訊都會保存在這里,以及資料收發操作的執行狀態也會在這里面,
連接操作的實際程序
連接操作的第一步就是在 TCP 模塊處創建表示連接控制資訊的頭部,當 TCP 頭部創建好后,TCP 模塊會將資訊傳遞給 IP 模塊委托其進行發送,IP 執行發送后,網路包會通過網路到達服務器,服務器上的 IP 模塊將接收到的資料傳遞給 TCP 模塊,TCP 模塊根據頭部資訊找到對應的套接字,套接字中會寫入相應的資訊,然后將狀態改為正在連接,
于此同時,在回傳回應時,會將 ACK 控制位設為 1,代表已接收到網路包,服務器 TCP 模塊會將回應訊息通過 IP 模塊向客戶端做出回應,
客戶端接收到回應后,其 IP 模塊將資訊傳遞給 TCP 模塊,然后通過 TCP 頭部資訊確認連接是否成功,SYN 等于 1 就代表成功,客戶端還會將 ACK 設定為1 并發回給服務器,服務器收到這個包后才算連接操作真正的完成,
建立連接后,就可以隨時進行收發資料了,在呼叫 close 之前,連接會一直存在,
收發資料
收發資料的觸發操作是應用程式發起的,通過調研 write,指定發送資料的長度,
一般當協議堆疊接受到資料時可能并不會馬上發出去,而是放在發送緩沖區中,為什么要這樣做呢?
有些程式可能一次性會傳所有資料,但有些程式會逐行傳遞,在這種情況下,如果收到資料就發送,可能會造成發送大量小包資料,導致效率低下,至于需要積累多少資料才發送一般是根據兩方面因素來考量,一個是每個網路包的資料長度,還有一個緯度是處理時間,
網路包容納的資料長度
首先介紹下兩個名詞:
**MTU:**一個網路包的最大長度,以太網中一般是1500位元組,是包含頭部的總長度,
MSS: 除去頭部后,一個網路包所有容納的資料最大長度,

處理時間
當一個應用程式發送資料的頻率不高時,如果每次都需要等到長度達到 MSS 才發送,就會造成等待時間過長,為了解決這種情況,協議堆疊會有一個計時器,如果達到一定時間,即使還遠未達到 MSS 長度,也會把網路包發送出去,
ACK 機制確認網路包接收情況
當客戶端向服務端發送資料時,TCP會將資料的位元組數算好寫在 TCP 頭部,同時會生成一個亂數 當作 ACK 一并發送給服務端,服務端接受后就會根據實際收到的長度和TCP頭部給的長度做對比,來確保資料沒有遺漏,同時客戶端還需要告知服務端是從哪個位元組開始發送的,而我們的 ACK是個隨機值,這時候我們就需要通過 SYN 控制位設定為1發送給服務器,這樣服務器就知道其初始是從哪個位元組開始發送的,
接受方收到資料后,如果資料沒問題,就需要告知發送方收到了多少資料,也是通過 ACK 號的操作來回傳的,這個 ACK 的值就是一共接收了多少位元組,

通過這種機制,我們就可以確認接收方是否正確收到資料,如果沒有準確收到,就可以重新發送網路包,
無論網路發生何種錯誤,我們就都可以發現并采取補救措施,
視窗滑動
一般如果我們每發送一個網路包就等待 ACK 回傳確認后再發送下一個包,這個等待 ACK 的時間啥都不做就會很浪費,
視窗滑動的概念就是每次發送一個網路包,不會等 ACK 回傳就會繼續發送下一個包,減少等待時間的浪費,

但這種方式也會存在問題,假如發送方不斷發送資料給接收方,接收方第一個資料還沒處理完,第二個資料就來了,這些來不及處理的資料會進入接識訓沖區,資料會不斷增多,就會造成溢位,避免這種方式的處理是通過接收方告知發送方自己最大能接收多少資料,發送方會根據這個值對發送的資料進行控制,
洗掉套接字
當我們資料收發完成后,就會啟動斷開機制,以 Web 為例,收發資料結束時,服務器會發起斷開程序,會呼叫 Socket 庫的 close 程式,服務器協議堆疊會生成一個包含斷開資訊的 TCP 頭部,就是將 FIN 位元設定為1,協議堆疊會委托 IP 模塊向客戶端發送資料,

當客戶端接收到 FIN 為 1 的 TCP 頭部時,客戶端協議堆疊會將自己的套接字標記為進入斷開操作狀態,然后告知服務器已經收到 FIN 為 1的包,客戶端會向服務器回傳一個 ACK 號,
UDP 協議收發操作
之前我們都是以 TCP 協議講解的資料收發操作,可以看出整個流程下來其實是挺復雜的,但是有時候可能我們并不需要這么復雜的安全校驗,UDP 就可以滿足一些簡單的資料收發,例如像我們之前提到的 向 DNS 服務器查詢 IP 地址,我們就是用的 UDP 協議,
UDP 沒有 TCP 的接收確認、視窗等機制,在收發資料之前是不需要進行交換控制資訊,不需要進行連接操作,
接收資料也很簡單,只需要根據 IP 頭部中的接收方和發送方 IP 地址,以及 UDP 頭部中的接收方和發送方埠號,找到對應的套接字然后將資料交給相應的應用程式即可,

關注小蛋,一起成長,一起進步
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/377010.html
標籤:其他
