【网络是怎样连接的】—— TCP/IP 传输数据
協議棧的工作原理
1. 協議棧的內部結構
操作系統中的網絡控制軟件(協議棧)的內部如圖所示,分為幾個部分,分別承擔不同的功能。這張圖中的上下關系具有一定的規則,上面的部分會向下面的部分委派工作,下面的部分接受委派的工作并實際執行
- 圖中最上面的部分是網絡應用程序,也就是瀏覽器、電子郵件客戶端、Web 服務器、電子郵件服務器等程序,它們會將收發數據等工作委派給下層的部分來完成
- 應用程序的下面是 Socket 庫,其中包括解析器,解析器用來向 DNS 服務器發出查詢
- 再下面是操作系統內部,其中包括協議棧。協議棧的上半部分有兩塊,分別是負責用 TCP 協議收發數據的部分和負責用 UDP 協議收發數據的部分,它們會接受應用程序的委托執行收發數據的操作。下半部是用 IP 協議控制網絡包收發操作的部分。在互聯網上傳送數據時,數據會被切分成一個一個的網絡包,而將網絡包發送給通信對象的操作就是由 IP 來負責
- IP 下面的網卡驅動程序負責控制網卡硬件,而最下面的網卡則負責完成實際的收發操作,也就是對網線中的信號執行發送和接收的操作
2. 套接字
我們已經了解了協議棧的內部結構,再來了解在數據收發中扮演關鍵角色的套接字。
在協議棧內部有一塊用于存放控制信息的內存空間,這里記錄了用于控制通信操作的控制信息,例如通信對象的 IP 地址、端口號、通信操作的進行狀態等。本來套接字就只是一個概念,并不存在實體,如果一定要賦予它一個實體,我們可以說這些控制信息就是套接字的實體,或者說存放控制信息的內存空間就是套接字的實體。協議棧在執行操作時需要參閱這些控制信息
3. 調用 socket
知道套接字的具體樣子后,再來看看當瀏覽器調用 socket、connect 等 Socket 庫中的程序組件時,協議棧內部是如 何工作的:
- 瀏覽器通過 Socket 庫向協議棧發出委托的一系列操作,例如,瀏覽器委托協議棧使用 TCP 協議來收發數據
- 應用程序調用 socket 申請創建套接字
- 協議棧分配用于存放一個套接字所需的內存空間,并寫入初始狀態的控制信息
- 將表示這個套接字的描述符告知應用程序
- 應用程序在向協議棧進行收發數據委托時需要提供這個描述符,完成數據通信
連接服務器
1. 連接的含義
創建套接字之后,應用程序(瀏覽器)就會調用 connect,隨后協議棧會將本地的套接字與服務器的套接字進行連接。連接實際上是通信雙方交換控制信息,所謂控制信息,就是用來控制數據收發操作所需的一些信息,IP 地址 和端口號就是典型的例子。此外,當執行數據收發操作時,我們還需要一塊用來臨時存放要收發的數據的內存空間,這塊內存空間稱為緩沖區,它也是在連接操作的過程中分配的。上面這些就是連接一詞的具體含義
2. 控制信息
控制信息大體上可以分為兩類:
- 頭部中記錄的信息
- 套接字中記錄的信息
第一類是客戶端和服務器相互聯絡時交換的控制信息,這些信息不僅連接時需要,包括數據收發和斷開連接操作在內,整個通信過程中都需要,這些內容在 TCP 協議的規格中進行了定義
這些信息會被添加在客戶端與服務器之間傳遞的網絡包的開頭,在連接階段,由于數據收發還沒有開始,所以網絡包中沒有實際的數據,只有控制信息。這些控制信息位于網絡包的開頭,因此被稱為頭部
第二類控制信息就是,就是保存在套接字中,用來控制協議棧操作的信息。應用程序傳遞來的信息以及從通信對象接收到的信息都會保存在這里,還有收發數據操作的執行狀態等信息也會保存在這里,協議棧根據這些信息來執行每一步的操作
3. 實際過程
這個過程從應用程序調用 Socket 庫的 connect 開始
connect(< 描述符 >, < 服務器 IP 地址和端口號 >, ......)上面的調用提供了服務器的 IP 地址和端口號,這些信息會傳遞給協議棧中的 TCP 模塊。然后,TCP 模塊會與該 IP 地址對應的對象,也就是與服務器的 TCP 模塊交換控制信息,這一交互過程包括下面幾個步驟:
- 客戶端創建一個包含表示開始數據收發操作的控制信息的頭部,將頭部的控制位的 SYN 位設為 1,表示可以連接,此外還需要設置適當的序號和窗口大小
- TCP 模塊會將信息傳遞給 IP 模塊并委托它進行發送
- 服務器上的 IP 模塊會將接收到的數據傳遞給 TCP 模塊
- 服務器的 TCP 模塊根據 TCP 頭部中的信息找到端口號對應的套接字,寫入相應的信息,并將狀態改為正在連接
- 服務器的 TCP 模塊會返回響應,在 TCP 頭部中設置發送方和接收方端口號以及設置 SYN 為 1,此外,還需要將 ACK 控制位設為 1,表示已經接收到相應的網絡包
- 網絡包會返回到客戶端,通過 IP 模塊到達 TCP 模塊,并通過 TCP 頭部的信息確認連接服務器的操作是否成功,如果 SYN 為 1 則表示連接成功。這時會向套接字中寫入服務器的 IP 地址、端口號等信息,同時還會將狀態改為連接完畢
- 客戶端也需要將 ACK 設置為 1 并發回服務器,告訴服務器剛才的響應包已經收到。當這個服務器收到這個返回包之后,連接操作才算全部完成
收發數據
1. 將 HTTP 請求消息交給協議棧
數據收發操作是從應用程序調用 write 將要發送的數據交給協議棧開始的,協議棧收到數據后執行發送操作。協議棧并不是一收到數據就馬上發送出去,而是會將數據存放在內部的發送緩沖區中,并等待應用程序的下一段數據,在數據積累到一定量時再發送出去。至于要積累多少數據才能發送,不同種類和版本的操作系統會有所不同
2. 數據確認
在發送網絡包之后,還需要進行確認操作,確認對方是否成功收到網絡包
首先,TCP 模塊在拆分數據時, 會先算好每一塊數據相當于從頭開始的第幾個字節,接下來在發送這一塊數據時,將算好的字節數寫在 TCP 頭部中,序號字段就是這個時候派上用場的。然后,發送數據的長度也需要告知接收方,不過這個并不是放在 TCP 頭部里面,因為用整個網絡包的長度減去頭部的長度就可以得到數據的長度,所以接收方可以用這種方法來進行計算。有了上面兩個數值, 我們就可以知道發送的數據是從第幾個字節開始,長度是多少了
通過這些信息,接收方能夠檢查收到的網絡包有沒有遺漏。如果確認沒有遺漏,接收方會將到目前為止接收到的數據長度加起來,計算出一共已經收到了多少個字節,然后將這個數值寫入 TCP 頭部的 ACK 號中發送給發送方。這個返回 ACK 號的操作被稱為確認響應,通過這樣的方式,發送方就能夠確認對方到底收到 了多少數據
在實際的通信中, 序號并不是從 1 開始的,而是需要用隨機數計算出一個初始值。在之前的連接過程中,有一個將 SYN 控制位設為 1 并發送給服務器的操作,就是在這一步將序號的初始值告知對方的
我們剛剛只考慮了單向的數據傳輸,但 TCP 數據收發是雙向的,在客戶端向服務器發送數據的同時,服務器也會向客戶端發送數據,這是我們只需要反過來就可以了
3. 實際過程
首先,客戶端在連接時需要計算出與從客戶端到服務器方向通信相關的序號初始值,并將這個值發送給服務器。接下來,服務器會通過這個初始值計算出 ACK 號并返回給客戶端,同時,服務器也需要計算出與從服務器到客戶端方向通信相關的序號初始值,并將這個值發送給客戶端。接下來像剛才一樣,客戶端也需要根據服務器發來的初始值計算出 ACK 號并返回給服務器。到這里,序號和 ACK 號都已經準備完成了,接下來就可以進入數據收發階段了。數據收發操作本身是可以雙向同時進行的,但 Web 中是先由客戶端向服務器發送請求,序號也會跟隨數據一起發送。然后,服務器收到數據后再返回 ACK 號。從服務器向客戶端發送數據的過程則正好相反
4. 使用窗口有效管理 ACK
每發送一個包就等待一個 ACK 號的方式是最簡單也最容易理解的,但在等待 ACK 號的這段時間中,如果什么都不做那實在太浪費了。為了減少這樣的浪費,TCP 采用滑動窗口的方式來管理數據發送和 ACK 號的操作。所謂滑動窗口,就是在發送一個包之后,不等待 ACK 號返回,而是直接發送后續的一系列包。這樣一來,等待 ACK 號的這段時間就被有效利用起來了
雖然這樣做能夠減少等待 ACK 號時的時間浪費,但有一些問題需要注意。在一來一回方式中,接收方完成接收操作后返回 ACK 號,然后發送方收到 ACK 號之后才繼續發送下一個包,因此不會出現發送的包太多 接收方處理不過來的情況。但如果不等返回 ACK 號就連續發送包,就有可能會出現發送包的頻率超過接收方處理能力的情況
下面來具體解釋一下。當接收方的 TCP 收到包后,會先將數據存放到接收緩沖區中。然后,接收方需要計算 ACK 號,將數據塊組裝起來還原成原本的數據并傳遞給應用程序,如果這些操作還沒完成下一個包就到了也不用擔心,因為下一個包也會被暫存在接收緩沖區中。如果數據到達的 速率比處理這些數據并傳遞給應用程序的速率還要快,那么接收緩沖區中 的數據就會越堆越多,最后就會溢出。緩沖區溢出之后,后面的數據就進不來了,因此接收方就收不到后面的包了,這就和中途出錯的結果是一樣 的,也就意味著超出了接收方處理能力。我們可以通過下面的方法來避免這種情況的發生。首先,接收方需要告訴發送方自己最多能接收多少數據, 然后發送方根據這個值對數據發送操作進行控制,這就是滑動窗口方式的基本思路
關于滑動窗口的具體工作方式,還是看圖更容易理解。在這張圖中,接收方將數據暫存到接收緩沖區中并執行接收操作。當接收操作完成后,接收緩沖區中的空間會被釋放出來,也就可以接收更多的數據了, 這時接收方會通過 TCP 頭部中的窗口字段將自己能接收的數據量告知發送方。這樣一來,發送方就不會發送過多的數據,導致超出接收方的處理能力了
此外,單從圖上看,大家可能會以為接收方在等待接收緩沖區被填滿 之前似乎什么都沒做,實際上并不是這樣。這張圖是為了講解方便,故意體現一種接收方來不及處理收到的包,導致緩沖區被填滿的情況。實際上,接收方在收到數據之后馬上就會開始進行處理,如果接收方的性能高,處理速度比包的到達速率還快,緩沖區馬上就會被清空,并通過窗口字段告知發送方
5. ACK 與窗口合并
要提高收發數據的效率,還需要考慮另一個問題,那就是返回 ACK 號和更新窗口的時機。如果假定這兩個參數是相互獨立的,分別用兩個單獨的包來發送,結果會如何呢?
首先,發送方的數據到達接收方,在接收操作完成之后就需要向發送方返回 ACK 號,而再經過一段時間,當數據傳遞給應用程序之后才需要更新窗口大小。但如果根據這樣的設計 來實現,每收到一個包,就需要向發送方分別發送 ACK 號和窗口更新這兩個單獨的包。這樣一來,接收方發給發送方的包就太多了,導致網絡效率下降
因此,接收方在發送 ACK 號和窗口更新時,并不會馬上把包發送出 去,而是會等待一段時間,在這個過程中很有可能會出現其他的通知操作, 這樣就可以把兩種通知合并在一個包里面發送了。舉個例子,在等待發送 ACK 號的時候正好需要更新窗口,這時就可以把 ACK 號和窗口更新放在 一個包里發送,從而減少包的數量。當需要連續發送多個 ACK 號時,也可以減少包的數量,這是因為 ACK 號表示的是已收到的數據量,也就是說,它是告訴發送方目前已接收的數據的最后位置在哪里,因此當需要連續發送 ACK 號時,只要發送最后一個 ACK 號就可以了,中間的可以全部省略。當需要連續發送多個窗口更新時也可以減少包的數量,因為連續發生窗口更新說明應用程序連續請求了數據,接收緩沖區的剩余空間連續增 加。這種情況和 ACK 號一樣,可以省略中間過程,只要發送最終的結果就可以了
6. 接收響應
對于響應消息,瀏覽器需要進行接收操作,這一操作也需要協議棧的參與
瀏覽器在委托協議棧發送請求消息之后,會調用 read 程序來獲取響應消息。然后,控制流程會通過 read 轉移到協議棧,然后協議棧會執行接下來的操作。和發送數據一樣,接收數據也需要將數據暫存到接收緩沖區中
協議棧嘗試從接收緩沖區中取出數據并傳遞給應用程序,但這個時候請求消息剛剛發送出去,響應消息可能還沒返回。響應消息的返回還需要等待一段時間,因此這時接收緩沖區中并沒有數據,那么接收數據的操作也就無法繼續。這時,協議棧會將應用程序的委托,也就是從接收緩沖區中取出數據并傳遞給應用程序的工作暫時掛起,等服務器返回的響應消息到達之后再繼續執行接收操作
協議棧會檢查收到的數據塊和 TCP 頭部的內容,判斷是否有數據丟失,如果沒有問題則返回 ACK 號。然后, 協議棧將數據塊暫存到接收緩沖區中,并將數據塊按順序連接起來還原出原始的數據,最后將數據交給應用程序
從服務器斷開
1. 數據發送完畢斷開連接
收發數據結束的時間點應該是應用程序判斷所有數據都已經發送完畢的時候,這時客戶端會先發起斷開過程。這一判斷是應用程序作出的,協議棧在設計上允許任何一方先發起斷開過程
完成數據發送的一方會發起斷開過程,這里我們以服務器一方發起斷開過程為例來進行講解:
2. 刪除套接字
和服務器的通信結束之后,用來通信的套接字也就不會再使用了,這時就可以刪除這個套接字了。不過,套接字并不會立即被刪除,而是會等待一段時間之后再被刪除
等待這段時間是為了防止誤操作,引發誤操作的原因有很多,例如,如果最后客戶端返回的 ACK 號丟失了,服務器沒有接收到 ACK 號,可能會重發一次 FIN。這時,客戶端的套接字已經刪除了,套接字中保存的控制信息也就跟著消失了,套接字對應的端口號就會被釋放出來。如果別的應用程序要創建套接字,新套接字碰巧又被分配了同一個端口號,而服務器重發的 FIN 正好到達,這個 FIN 就會錯誤地跑到新套接字里面,新套接字就開始執行斷開操作了
至于具體等待多長時間,這和包重傳的操作方式有關。網絡包丟失之后會進行重傳,這個操作通常要持續幾分鐘。如果重傳了幾分鐘之后依然無效,則停止重傳。在這段時間內,網絡中可能存在重傳的包,也就有可能發生前面講到的這種誤操作,因此需要等待到重傳完全結束。協議中對于這 個等待時間沒有明確的規定,一般來說會等待幾分鐘之后再刪除套接字
總結
TCP 整體流程如下:
總結
以上是生活随笔為你收集整理的【网络是怎样连接的】—— TCP/IP 传输数据的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 服务器网络连接详细信息,Windows1
- 下一篇: 网络是怎样连接的pdf