Linux 網絡基礎(二) (傳輸協議層:UDP、TCP)

目錄

一、傳輸層的意義

二、端口號?

1、五元組標識一個通信

2、端口號范圍劃分?

3、知名端口號(Well-Know Port Number)

(1)查看端口號

4、綁定端口號數目問題

5、pidof & netstat 命令?

(1)netsate 命令

?編輯?(2)pidof 命令

三、UDP 協議?

1、UDP 協議段格式

?2、UDP 報文

(1)UDP 數據封裝過程

(2)UDP 數據交付過程

3、UDP?的特點

(1)面向數據報

4、UDP 的緩沖區

5、UDP 傳輸最大長度

6、基于?UDP?的應用層協議

四、TCP 協議(傳輸控制協議)

1、TCP?協議段格式?

(1)TCP 報頭當中各個字段的含義

(2)4 位頭部長度

(3)TCP 報頭

(4)序號與確認序號

(5)16 位窗口大小

(6)六個標志位?

2、確認應答(ACK)機制

(1)超時重傳機制

3、連接管理機制?

(1)面向連接相關概念

(2)理解連接

(3)三次握手

(4)四次揮手

(5)bind 綁定失敗(端口復用)

5、流量控制

6、滑動窗口

(1)如何理解滑動窗口

(2)情況一 ——?數據包已經抵達,ACK 應答丟了

(3)情況二 ——?數據包直接丟了

7、擁塞控制

(1)擁塞窗口

8、延遲應答

9、捎帶應答

10、面向字節流

11、粘包問題

(1)什么是粘包

12、TCP?異常情況

(1)進程終止

(2)機器重啟

(3)機器掉電?/?網線斷開

13、小結

(1)可靠性

(2)提高性能

(3)其他

(4)基于?TCP?應用層協議

五、TCP / UDP?對比

六、理解?listen 的第二個參數


一、傳輸層的意義

數據從應用層下來并不是直接發送給網絡,而是從網絡協議棧自頂向下傳輸,經過傳輸層、網絡層,數據鏈路層,最后通過硬件發送到網絡。
前面學習了應用層的兩個協議:HTTP 和 HTTPS,應用層的主要目的是為了保證數據的安全,而傳輸層的目的則是為了保證數據能夠可靠地傳送到目標地址。

二、端口號?

端口號(Port)標識了一個主機上進行通信的不同的應用程序。

1、五元組標識一個通信

因為主機上存在不同的服務,從網絡中獲取的數據在進行向上交付時,在傳輸層就會提取出該數據對應的目的端口號,進而確定該數據應該交付給當前主機上的哪一個服務進程(應用層),即一臺主機上可以同時部署端口號不同的服務。

在?TCP/IP?協議中,用?“源IP”,“源端口號”,“目的?IP”,“目的端口號”,“協議號”?這樣一個五元組來標識一個通信。

可以通過命令:netstat -nltp 進行查看:

舉例:打開瀏覽器后,用不同的頁面訪問 CSDN,雖然源 IP 地址是一樣的,但是源端口號不同,就表示兩個不同的通信。通過這個五元組,服務器能夠準確的區分請求是從哪里來的。

通信流程:

  1. 先提取出數據當中的目的 IP 地址和目的端口號,確定該數據是發送給當前服務進程的。
  2. 然后提取出數據當中的協議號,為該數據提供對應類型的服務。
  3. 最后提取出數據當中的源 IP 地址和源端口號,將其作為響應數據的目的 IP 地址和目的端口號,將響應結果發送給對應的客戶端進程。

?

2、端口號范圍劃分?

端口號是一個 16 位的整數,它的取值范圍是 0~65535。

  • 0~1023:知名端口號。比如 HTTP,FTP,SSH 等這些廣為使用的應用層協議,它們的端口號都是固定的。(類比 120 或 110)
  • 1024~65535:操作系統動態分配的端口號。客戶端程序的端口號就是由操作系統從這個范圍分配的,允許用戶手動綁定。

3、知名端口號(Well-Know Port Number)

有些服務器是非常常用的,為了使用方便,人們約定一些常用的服務器,都是用以下這些固定的端口號:

  • ssh 服務器使用 22 端口
  • ftp 服務器使用 21 端口
  • telnet 服務器使用 23 端口
  • http 服務器使用 80 端口
  • https 服務器使用 443

(1)查看端口號

可以通過命令 vim /etc/services 查看文件內容,該文件是記錄網絡服務名和它們對應使用的端口號及協議。

當自己寫一個程序使用端口號時,要避開這些知名端口號。?

4、綁定端口號數目問題

個進程可不可以 bind 多個端口號?

可以。假設綁定了兩個端口號 A 和 B,這兩個端口號標識的是同一個進程 ,這與端口號用來標識進程的唯一性并不沖突。

一個端口號是否可以被多個進程 bind?

不行,因為端口號的作用就是標識唯一的一個進程。

如果綁定了多個進程,如何找到對應的進程呢?所以如果綁定一個已經被綁定的端口號就會出現綁定失敗的問題。

5、pidof & netstat 命令?

(1)netsate 命令

netstat?是一個用來查看網絡狀態的重要工具。

A. 常用選項

  • n:拒絕顯示別名,能顯示數字的全部轉化成數字。
  • l:僅列出有在 Listen(監聽)的服務狀態。
  • p:顯示建立相關鏈接的程序名(進程:process)。
  • t(tcp):僅顯示 tcp 相關選項。
  • u(udp):僅顯示 udp 相關選項。
  • a(all):顯示所有選項,默認不顯示 LISTEN 相關。

?(2)pidof 命令

通過進程名來查看服務器進程的 pid。

注意:這里的 xargs 是將標準輸入的內容轉換成命令行參數,也就是把從管道讀取的數據拼接到 kill -9 的后邊。

三、UDP 協議?

1、UDP 協議段格式

  • 16 位源端口號:表示數據從哪里來。
  • 16 位目的端口號:表示數據要到哪里去。
  • 16 位 UDP 長度:表示整個數據報(UDP 首部 + UDP 數據)的最大長度。
  • 16 位 UDP 檢驗和:如果UDP報文的檢驗和出錯,就會直接將報文丟棄。

為什么我們前面在應用層編寫代碼的時候,每一次寫端口號都喜歡用?uint16_t 呢?

其根本原因就是因為傳輸層協議中的端口號就是 16 位的。

send 數據并不是直接發送到網絡里,而是發給了傳輸層。

如何將報頭和有效載荷進行分離?

UDP 采用的是固定報頭,UDP 的報頭中只包含四個字段,每個字段的長度都是 16 位,總共 8 字節,所以直接提取前 8 個字節就是報頭,其他的就是有效載荷。

UDP 具有將報文一個個正確接收的能力,UDP 是面向數據報的。

UDP 如何交付?(有效載荷交給上層的哪一個協議?)

應用層每個進程都綁定有端口號,UDP 就是通過報頭當中的目的端口號來找到對應的應用層進程的,把有效載荷交出去。

?2、UDP 報文

這里的報頭其實就是一種結構化數據對象(位段):

struct udp_hdr
{uint16_t src_port;  // 源端口uint16_t dsc_port;  // 目的端口uint16_t udp_len;   // UDP長度uint16_t udp_check; // 校驗和
};

(1)UDP 數據封裝過程

首先要知道應用層 sendto 數據是發給傳輸層的。

創建一塊內存,計算出有效載荷的起始地址,拷貝有效載荷,強轉填寫報頭部分,最后形成 UDP 報文。

(2)UDP 數據交付過程

因為是定長報頭,直接取出目的端口號,把有效載荷向上交付給指定協議(進程)。

3、UDP?的特點

UDP?傳輸的過程類似于寄信。

  • 無連接:知道對端的 IP 和端口號就直接進行傳輸,不需要建立連接。
  • 不可靠:沒有確認機制,沒有重傳機制。如果因為網絡故障該段無法發到對方,UDP 協議層也不會給應用層返回任何錯誤信息。
  • 面向數據報:不能夠靈活的控制讀寫數據的次數和數量。

(1)面向數據報

舉例理解:別人發了三個快遞,那么我們就一定要收到三個,不會出現只收到一個,一個半這樣的情況。如果只有一個包裹,那我們也不能只拿走一半。

結論:發送了一個報文,要么不讀,要么 recvfrom 等到讀取完一整個報文再返回。

應用層交給 UDP 多長的報文,UDP 原樣發送,既不會拆分,也不會合并。

用 UDP 傳輸 100 個字節的數據:如果發送端調用一次 sendto,發送 100 字節,那么接收端也必須調用對應的一次recvfrom,接收100個字節;而不能循環調用10次recvfrom,每次接收10個字節。

4、UDP 的緩沖區

  • UDP 沒有真正意義上的發送緩沖區。因為它沒有可靠機制,不需要把數據暫存起來。它直接調用 sendto 會直接交給內核,由內核將數據傳給網絡層協議進行后續的傳輸動作。
  • UDP 具有接收緩沖區。但是這個接收緩沖區不能保證收到的 UDP 報的順序和發送 UDP 報的順序一致,如果緩沖區滿了,再到達的 UDP 數據就會被丟棄。

UDP 的 socket 既能讀,也能寫,所以是?全雙工的。

為什么 UDP 要有緩沖區?

如果 UDP 沒有接收緩沖區,那么就要求上層及時將 UDP 獲取到的報文讀取上去,如果一個報文在 UDP 沒有被讀取,那么此時 UDP 從底層獲取上來的報文數據就會被迫丟棄。

5、UDP 傳輸最大長度

UDP 協議首部中有一個 16 位的最大長度,因此一個 UDP 能傳輸的數據最大長度是 64K(包含 UDP 報頭的大小)。

然而 64K 在當今的互聯網環境下是一個非常小的數字,如果我們要傳輸的數據大于 64K,就需要在應用層進行手動分包,多次發送并在接收端進行手動拼裝。

6、基于?UDP?的應用層協議

  • NFS:網絡文件系統
  • TFTP:簡單文件傳輸協議
  • DHCP:動態主機配置協議
  • BOOTP:啟動協議(用于無盤設備啟動)
  • DNS:域名解析協議

當然也包括我們自己寫?UDP?程序時自定義的應用層協議。

四、TCP 協議(傳輸控制協議)

TCP(Transmission Control Protocol),它要對數據的傳輸進行一個詳細的控制。

1、TCP?協議段格式?

(1)TCP 報頭當中各個字段的含義

  • 源/目的端口號:表示數據是從哪個進程來,到發送到對端主機上的哪個進程。
  • 32 位序號 / 32 位確認號:分別代表 TCP 報文當中每個字節數據的編號以及對對方的確認,是 TCP 保證可靠性的重要字段。
  • 4 位TCP報頭長度:表示該 TCP 報頭的長度 —— TCP 頭部有多少個 32 位 bit(有多少個 4 字節),所以 TCP 頭部最大長度是 15 * 4 = 60
  • 6 位標志位:TCP 報頭中暫時未使用的 6 個比特位。
  • 16 位窗口大小:保證 TCP 可靠性機制和效率提升機制的重要字段。
  • 16 位檢驗和:由發送端填充,采用 CRC 校驗。接收端校驗不通過,則認為接收到的數據有問題。(檢驗和包含 TCP 首部 + TCP 數據部分)
  • 16 位緊急指針:標識哪部分數據是緊急數據,就是標識緊急數據在報文中的偏移量,需要配合標志字段當中的 URG 字段統一使用。
  • 選項字段:TCP 報頭當中允許攜帶額外的選項字段,最多 40 字節。

(2)4 位頭部長度

這里有 4 個比特位,按照正常的計算取值范圍就是【0~15】,但很明顯有問題,因為報頭最少要為 20 字節。所以,規定 TCP 報頭當中的 4 位首部長度描述的基本單位是 4 字節,這樣取值范圍就是【0~60】,所以整個報頭的大小范圍是【20~60】,那么報頭中選項字段的長度最多是 40 字節。計算可得,4 位首部長度取值范圍是【5~15】,轉換成二進制就是【0101~1111】。

TCP 如何將報頭與有效載荷進行分離?

觀察 TCP 協議格式,報文部分把選項除去一共是 20 字節。那么可以先提取 20 字節,再根據標準報頭,提取其中的 4 位首部長度,再將首部長度 *4 就表示的是報頭的大小,如果結果等于 20,說明把報頭讀完了,否則沒讀完,這時再讀取前面的結果 -20 的長度對應的字節數據,這時就把選項讀完了,也就讀完了報頭。讀取完 TCP 的基本報頭和選項字段后,剩下的就是有效載荷了。

有效載荷如何向上交付?

因為應用層的每個進程都會綁定一個端口號:服務端顯示綁定一個端口號,客戶端由操作系統自動綁定一個端口號。上面把報頭提取了出來,而報頭里含有目的端口,就可以向上找到對應的協議了。

?內核中用哈希的方式維護了端口號與進程 ID 之間的映射關系,因此傳輸層可以通過端口號快速找到其對應的進程 ID,進而找到對應的應用層進程。綁定映射關系的時機:bind 端口的時候。

(3)TCP 報頭

跟前面講的 UDP 報頭一樣,TCP 報頭是一個結構化對象,也是內核創建一塊內存,后邊就拷貝有效載荷,前面就強轉成結構化數據然后填寫每個字段。

(4)序號與確認序號

A. 理解可靠性

a. 造成不可靠的原因

現在計算機基本都是基于馮諾依曼體系結構,雖然這些設備都在同一臺機器上,但它們都是獨立的硬件設備,如果它們之間想要進行數據交互,就必須要進行通信。因此這幾個設備實際是用 “線” 連接起來的,其中連接內存和外設之間的 “線” 叫做 IO 總線,而連接內存和 CPU 之間的 “線” 叫做系統總線。

而在一臺機器內,這些 “線” 的長度很短,所以傳輸數據發生錯誤的概率很小,但是如果要通信的兩個機器相隔很遠(網絡),那么傳輸數據出錯(丟包)的概率也會大大增加,所以網絡傳輸的不可靠問題本質就是距離變長了。


b.?不可靠問題場景

  • 丟包
  • 亂序(網絡阻塞)
  • 校驗錯誤(比特位翻轉)
  • 重復
  • ......

如何保證自己說的話對方聽到了呢?

得到對方的回復(應答),只有收到了應答,才能保證歷史消息被對方收到了。只有確認了應答,才算可靠。

而雙方通信一定會存在最新消息,最新發送消息的一方無法保證發送出去的數據被對方收到。所以,沒有絕對的可靠性,只有相對的可靠性。在網絡中不存在 100% 可靠的協議,但在局部上(發出去的消息有匹配的應答),能做到 100% 可靠。

TCP 保證可靠性的機制之一就是確認應答機制,只要一個報文收到了對應的應答,就能保證發出去的數據被對方收到了。

所以雙方進行通信時可能除了正常的數據段,還會包含確認數據段。

上圖是雙方通信使用串行方式,即只有收到了確認應答才會繼續發送數據。這樣的效率是非常低的。但實際工作中并不會這樣,而是一方同時發送多條數據段,只要保證所有數據段都有應答即可:

但這些數據段到達對面的順序不一定就是發送的順序。比如,發送了四個數據段,結果只收到了三個確認應答,那客戶端是如何知道哪個數據段發送失敗了呢?

因為任何一方都會收到報文,報文中會攜帶序號,根據報文收到的多個序號進行排序,報文也就可以按序到達。

B.?32 位序號

解決上面的問題靠的就是 TCP 報頭中的 32 位序號字段將請求和應答進行一一對應。

TCP 將發送出去的每個數據段都進行了編號,這個編號叫作序列號,以此保證了傳遞數據段的有序性。

每一個 ACK 都帶有對應的確認序列號,意思是告訴發送者,我已經收到了哪些數據,下一次你從哪里開始發。舉例:假設現在要發送 4000 字節的數據,分 4 次發送,就需要發送 4 個 TCP 報文。此時這 4 個 TCP 報文中的 32 位序號填的就是發送數據中首個字節的序列號,因此分別填的是 1、1001、2001 和 3001。

當主機 B 接收到這 4 個 TCP 報文時,就可以利用這 4 個報頭中的序號字段進行排序。


C. 32 位確認序號

TCP 報頭當中的 32 位確認序號是告訴對端,我當前已經收到了哪些數據,你的數據下一次應該從哪里開始發

比方說客戶端發送的數據段的序號是 1,報文中含有 1000 字節的數據,如果服務端收到了,那么就會把返回給客戶端的響應報頭中的32位確認序號填寫成 1001,那么這個 1001 就有兩層含義:

  1. 告訴主機 A,序列號在 1001 之前的字節數據我已經收到了。
  2. 告訴主機 A,下次向我發送數據時應該從序列號為 1001 的字節數據開始進行發送。?

通過序號和確認序號可以表示:接收方已經收到 ACK 序號(確認序號)之前的所有(連續)報文,告訴對方下次發送從確認序號指明的序號發送,允許部分確認丟失或者不給應答。

舉例:發送的數據都是 1000 字節大小:

如果序號 1001 的數據段沒有傳遞到主機 B,其他的傳遞到了主機 B,那么 1001 之后的數據段的確認序號都只能填寫 1001,這表明的就是序列號在 1001 之前的數據段都被收到了。


為什么要有兩組序號?為什么不能把 32 位序號和 32 位確認序號壓縮為一個字段,發送的時候就填序號,返回的時候就填確認序號?

如果一端發送數據,一端接收數據當然可以使用這種方式,但任何通信的一方的工作方式都是 TCP 全雙工的,雙方可能同時要給對方發送消息,也就是說在發送確認時可能攜帶新的數據。

雙方發出的報文當中,不僅需要填充 32 位序號來表明自己當前發送數據的序號,還需要填充 32 位確認序號,對對方上一次發送的數據進行確認,告訴對方下一次應該從哪一字節序號開始進行發送。


(5)16 位窗口大小

TCP 是有發送緩沖區和接收緩沖區。

  • 當上層調用 write/send,并不是把數據直接 write/send 到網絡中,實際上是把應用層中的數據拷貝到發送緩沖區。
  • 當上層調用 read/recv,實際上是把數據拷貝到接收緩沖區。

從本質上來說,TCP 通信是雙方在進行通信時,發送方的發送緩沖區和接收方的接收緩沖區,以及接收方的發送緩沖區和發送方的接收緩沖區,兩個緩沖區之間來回拷貝。

這就會導致兩種情況:

  1. 發送數據過快,導致接收緩沖區被打滿,剩下的報文都會被丟棄掉。
  2. 發送數據過慢,影響到上層的業務處理。

Client 發送數據不能太快,也不能太慢。那如何保證發送方發送數據不要太快或太慢呢?

TCP 要控制傳輸速度,所以發送方必須要知道對方緩沖區的接收能力,接收方要給發送方同步自己的接收能力,也就是接收緩沖區剩余空間的大小這種策略稱為 “流量控制”。

接收能力由什么來決定?

接收方的接收緩沖區中剩余空間的大小。

如何知道對方緩沖區的剩余空間呢?

通過 16 位窗口的字段填寫發送方的剩余緩沖區的大小,也就是當前主機接收數據的能力,那么接收方知道了以后就會調整發送速度。

  • 窗口大小字段越大,說明接收端接收數據的能力越強,此時發送端可以提高發送數據的速度。
  • 窗口大小字段越小,說明接收端接收數據的能力越弱,此時發送端可以減小發送數據的速度。
  • 如果窗口大小的值為 0,說明接收端接收緩沖區已經被打滿了,此時發送端就不應該再發送數據了。

補充:因為窗口有 16 位,所以窗口最大的內存位 64k。如果數據量太大了就可以用選項字段的一些選項把窗口擴大。

(6)六個標志位?

為什么需要多個標志位?

  1. TCP 的報文也是有類型的,比方說正常通信的常規報文,建立連接時發送的報文,斷開連接發送的報文,確認報文,其它類型的報文等。
  2. 針對這些不同類型的報文需要有對應的動作,比方說如果收到的是正常通信的報文,就需要把數據放到緩沖區中,如果收到的是建立連接的報文,就要進行三次握手。
  3. 六個標志位就是為了區分不同報文的類型。

A. SYN

報文當中的 SYN 被設置為1,表明該報文是一個連接建立的請求報文
只有在連接建立階段,SYN 才被設置,正常通信時 SYN 不會被設置。


B. FIN

報文當中的 FIN 被設置為 1,表明該報文是一個斷開連接的請求報文
只有在斷開連接階段,FIN 才被設置,正常通信時 FIN 不會被設置。


C. ACK

確認應答標志位,凡是該報文具有應答特征,那么其中的 ACK 都會被設置為 1,大部分的網絡報文 ACK 都被設置為 1,表明該報文可以對收到的報文進行確認。
一般除了第一個請求報文沒有設置 ACK 以外,其余報文基本都會設置 ACK,第一個連接請求報文在歷史上,客戶端給服務器從來沒有發送過數據,那么這個報文就不具備任何的 ACK 數據。

因為發送出去的數據本身就對對方發送過來的數據具有一定的確認能力,因此雙方在進行數據通信時,可以順便對對方上一次發送的數據進行響應。


D. RST

  1. reset,報文當中的 RST 被設置為 1,表示需要讓對方重新建立連接。
  2. 在通信雙方在連接未建立好的情況下,一方向另一方發數據,此時另一方發送的響應報文當中的 RST 標志位就會被置 1,表示要求對方重新建立連接。
  3. 還有一種情況是服務端網線被拔了,連接被斷開了,但客戶端不知道,他還會發消息,這時服務端就會把 RST 設置為 1,讓客戶端建立一個新連接。

E. PSH

push,報文當中的 PSH 被設置為 1,是在告訴對方上層盡快去取走數據。
因為可能接收方的窗口值比較小,而發送發就需要阻塞等待接收方取走緩沖區的數據后才能發送,那么此時就可以用 PSH 標志位來催促。


F. URG & 16 位緊急指針

緊急標志位,報文當中 URG 被設置為 1,是告訴對方這個數據是要特殊盡快處理。
因為 TCP 是可靠傳輸,具有按序到達機制的(優點),所以數據段一定是有序的被接收方收到,但是如果有數據段想要插隊就可以設置 URG。

并不是說這個數據段的有效載荷的所有部分都要被緊急處理,可能只是一小部分,那么怎么找到位置呢?

TCP 報頭有一個字段是緊急指針,它填寫的是偏移量,就可以找到緊急數據。

因為緊急指針只有一個,它只能標識數據段中的一個位置,因此緊急數據只能發送一個字節。

URG 一般用來發送帶外數據,它不用走 TCP 流,因為接收方直接處理。比方說我們現在發了很多數據,對方正在處理,但是我們突然發現不需要這些數據了,此時就可以發送緊急帶外數據,把套接字關了。

2、確認應答(ACK)機制

TCP 保證可靠性的機制之一就是確認應答機制。

確認應答機制是靠 TCP 報頭中的 32 位序號和 32 位確認序號實現的,收到的確認應答說明該序號之前的數據全部被收到了。

如何理解 TCP 將每個字節的數據都進行了編號?

可以把 TCP 的發送緩沖區看成一個 char sendbuffer[NUM] 樣的數組,當把應用層的數據拷貝到發送緩沖區時,每個字節的數據就天然的有了一個編號(下標),只不過這個下標不是從 0 開始的,而是從 1 開始往后遞增的

發送方發送數據時報頭當中所填的序號,實際上就是發送的若干字節數據當中,首個字節數據在發送緩沖區當中對應的下標。
接收方接收到數據進行響應時,響應報頭當中的確認序號實際上就是接收緩沖區中接收到的最后一個有效數據的下一個位置所對應的下標。
當發送方收到接收方的響應后,就可以從下標為確認序號的位置繼續進行發送了。

(1)超時重傳機制

A.?丟包的兩種情況

a. 情況一

發送的數據報文丟失了,此時發送端在一定時間內收不到對應的響應報文,就會進行超時重傳

  • 主機 A 發送數據給 B 之后,可能因為網絡擁堵等原因,數據無法到達主機 B。
  • 如果主機 A 在一個特定時間間隔內沒有收到 B 發來的確認應答,就會進行重發

b. 情況二

對方發來的響應報文丟包了,此時發送端也會因為收不到對應的響應報文,而進行超時重傳。但是主機?A?未收到?B?發來的確認應答,也可能是因為?ACK?丟失了。

因此主機?B?會收到很多重復數據,那么?TCP?協議需要能夠識別出那些包是重復的包,并且把重復的丟棄掉,這時候就可以利用前面提到的序列號,就可以很容易做到去重(通過序號)的效果。?

c. 總結

  • 當出現丟包情況的時候,發送方是不會知道究竟是數據段發送的時候丟包了,還是確認應答的時候丟包,所以發送方只能進行超時重傳。
  • 如果是第二種丟包情況,接收方就可能會收到一份同樣的數據。因為重復的報文也是不可靠的一種,所以主機 B 需要進行去重(通過序號)。

因為需要超時重傳,所以數據發送出去后不會立即清除,而是保留一段時間,直到收到該數據的響應報文后,發送緩沖區中的這部分數據才可以被刪除或覆蓋。

B.?超時重傳的等待時間

我們是通過超時來判斷是否丟包的,那如果超時的話,時間該如何確定?

  1. 我們知道數據發送的時間是由網絡狀況決定的,而網絡會因為環境的變化不斷變化,所以超時重傳的時間一定不是固定的。
  2. 超時時間是無法固定的。最理想的情況下,找到一個最小的單元時間,保證確認應答一定能在這個時間內返回。但是這個時間的長短隨著網絡環境的不同是有差異的,如果超時時間設的太長會影響整體的重傳效率,如果超時時間設的太短可能會頻繁發送重復的包。所以,超時的時間不能太長,也不能太短。
  3. TCP 為了保證無論在任何環境下都能比較高性能的通信,因此會動態計算這個最大超時時間。
  4. Linux 中(BSD Unix 和 Windows 也是如此),超時以 500ms 為一個單位進行控制,每次判定超時重發的超時時間都是 500ms 的整數倍。如果重發一次之后,仍然得不到應答,等待 2*500ms 后再進行重傳。如果仍然得不到應答,等待 4*500ms 進行重傳,依次類推,以指數形式遞增。當累計到一定的重傳次數后,TCP 認為網絡或者對端主機出現異常,強制關閉連接。

3、連接管理機制?

(1)面向連接相關概念


面向連接是通過要連接的兩臺主機分別在自己的主機上開辟一塊區域,然后通過 TCP 協議來共同維護這兩塊區域,來實現網絡傳輸的可靠性。所以,面向連接就是為了保證數據的可靠性。

再對比一下面向無連接理解一下,UDP 協議就是典型的無連接協議,在兩臺主機之間網絡通信時,不需要知道目標主機 ip 和目標端口是否存在,可以按照定義的 ip 和端口直接發送到網絡中,而面向連接則是先根據給定的目標 ip 和端口號發送到網絡中一些消息來確認目標主機是否存在,如果不存在則不能完成接下來的網絡通信。

所以,面向連接是需要先建立連接才能進行網絡通信的,建立連接就是確定對方存在并協商好一些控制量來確保接下來的通信是可靠的。


A. 無連接協議和面向連接協議的概念

無連接協議中的分組被稱為數據報,每個分組都是獨立尋址,并由應用程序發送的。從協議的角度來看,每個數據報都是一個獨立的實體,與在兩個相同的對等實體之間傳送的任何其他數據報都沒有關系,這就意味著協議很可能是不可靠的。也就是說,網絡會盡最大努力傳送每一個數據報,但并不保證數據報不丟失、不延遲或者不錯序傳輸。
另一方面,面向連接的協議則維護了分組之間的狀態,使用這種協議的應用程序通常都會進行長期的對話。記住這些狀態,協議就可以提供可靠的傳輸。比如,發送端可以記住哪些數據已經發送出去了但還未被確認,以及數據是什么時候發送的。如果在某段時間間隔內沒有收到確認,發送端可以重傳數據。接收端可以記住已經收到了哪些數據,并將重復的數據丟棄。如果分組不是按序到達的,接收端可以將其保存下來,直到邏輯上先于它的分組到達為止。
典型的面向連接協議有三個階段:

  1. 第一階段,在對等實體間建立連接。
  2. 接下來是數據傳輸階段,在這個階段中,數據在對等實體間傳輸。
  3. 最后,當對等實體完成數據傳輸時,連接被拆除。

一種標準的類比是,使用無連接協議就像寄信,而使用面向連接的協議就像打電話。

(2)理解連接

因為有大量的 Client 將來可能連接?Server,所以 Server 端一定會存在大量的連接。

OS 需要管理這些連接嗎?

需要,先描述,再組織。

所謂的連接,本質其實就是內核的一種數據結構類型,建立連接成功時,就是在內存中創建對應的連接對象,再對多個連接對象進行某種數據結構的組織。

維護連接是有成本的(內存 + CPU)

TCP為什么要建立連接?

因為要保證可靠性,連接不能直接保證可靠性。只要建立了連接,就會有連接結構體,里面包含了超時重傳、按序到達、流量控制、擁塞控制等等策略以及通信狀態和報文屬性等。連接結構體就是保證數據可靠性的基礎,而三次握手是建立連接結構體的基礎,所以三次握手間接的保證了可靠性。而 UDP 不需要通信狀態以及報文屬性等等,所以不需要建立連接。

(3)三次握手

雙方在進行 TCP 通信之前都需要先建立連接,建立連接的這個過程我們稱之為三次握手

為什么下圖中,連接的那條線不是橫著畫過去,而是斜著畫的呢?

因為有一個隱形的成本?——?時間。服務端接收的時間一定晚于客戶端發送的時間

  1. 第一次握手:客戶端向服務器發送的報文當中的 SYN 位被設置為 1,表示請求與服務器建立連接。
  2. 第二次握手:服務器收到客戶端發來的連接請求報文后,緊接著向客戶端發起連接建立請求并對客戶端發來的連接請求進行響應,此時服務器向客戶端發送的報文當中的 SYN 位和 ACK 位均被設置為 1。
  3. 第三次握手:客戶端收到服務器發來的報文后,得知服務器收到了自己發送的連接建立請求,并請求和自己建立連接,最后客戶端再向服務器發來的報文進行響應。

建立連接時不是 100% 成功的,三次握手中的任何一次都有可能出現丟包的情況,前兩次握手是能夠保證被對方收到的,因為它們都有應答,如果沒有的話,大不了超時重傳。

如果是第三次 ACK 應答丟了呢?

當客戶端發送 ACK 應答的一瞬間,它就會認為三次握手已經建立成功了,此時如果 ACK 應答丟了,此時就會連接建立失敗,但是不用擔心。

假設服務端沒有收到應答,那么它就會重傳第二次握手,客戶端就會意識到連接沒有被建立成功。除此之外,就算客戶端已經發送了數據,因為只有三次握手成功了才能發送消息,所以服務端就會返回 RST 報文要求客戶端重新建立連接。

一次或兩次握手能否行得通?

一次握手就是客戶端發送連接請求后就認為連接建立好了,服務端就會維護這個連接。那么如果客戶端寫一個多線程來不斷地向服務端發送連接請求,服務端就會認為這些連接都建立好了,服務端就會維護這些連接。如果連接過多就會導致資源被占滿,這個情況被稱為 “SYN 洪水”。其次也無法驗證全雙工通信信道是通暢的(客戶端無法保證自己發送了消息被服務端收到),所以一次握手是不可能完成連接的。
在第二次握手發出報文時,服務端就認為連接建立好了,客戶端可能壓根就沒收到這個報文,所以就會產生一次握手同樣的問題(SYN 洪水)。其次也無法驗證全雙工通信信道是通暢的(服務端不能證明自己能發送消息被對方收到)。

上面兩種情況會導致單機攻擊服務器的本質原因是,客戶端還沒有建立連接時服務端已經建立好連接了,所以必須讓客戶端先建立連接,再讓服務端建立連接。

為什么要三次握手?

  • Server 可以嫁接同等的成本給 Client
  • 驗證全雙工

三次握手是用最小的成本驗證全雙工通信信道是通暢的。

要想服務端建立連接,客戶端必須先建立連接,所以可以有效的規避單主機對服務器攻擊問題。

A. ddos 攻擊

注意:三次握手并不能解決安全問題,比方說大量的主機同時發送 TCP 請求也會導致服務端崩潰。

假設黑客黑掉了很多主機同時給服務端發送 TCP 連接請求:

此時再有客戶端發送連接請求,服務端就提供不了服務了(連不上),這種攻擊手段就是 ddos 攻擊(服務拒絕攻擊)

四次握手行不行?

可以,但沒必要,會降低效率。
服務端是把第二次握手的 SYN 和 ACK 分開發送,既然這兩個可以合并發送就沒有必要分開分兩次來發送。

所以,出于優化的目的,四次握手中的第二、三次可以合并為一次

B. 三次握手的狀態變化

最開始的時候,客戶端和服務器都處于 CLOSED 狀態。

  1. 服務器為了能夠接收客戶端發來的連接請求,需要由 CLOSED 狀態變為 LISTEN 狀態。
  2. 此時客戶端就可以向服務器發起三次握手了,當客戶端發起第一次握手后,狀態變為SYN_SENT狀態。
  3. 處于LISTEN狀態的服務器收到客戶端的連接請求后,將該連接放入內核等待隊列中,并向客戶端發起第二次握手,此時服務器的狀態變為 SYN_RCVD。
  4. 當客戶端收到服務器發來的第二次握手后,緊接著向服務器發送最后一次握手,此時客戶端的連接已經建立,狀態變為 ESTABLISHED。
  5. 而服務器收到客戶端發來的最后一次握手后,連接也建立成功,此時服務器的狀態也變成 ESTABLISHED

C. 套接字和三次握手之間的關系

在客戶端發起連接建立請求之前,服務器需要先進入 LISTEN 狀態,此時就需要服務器調用對應 listen 函數設置套接字屬性。當服務器進入 LISTEN 狀態后,客戶端就可以向服務器發起三次握手了,此時客戶端對應調用的就是 connect 函數。

注意:connect 函數不參與底層的三次握手,connect 函數的作用只是發起三次握手。

當 connect 函數返回時,要么是底層已經成功完成了三次握手連接建立成功,要么是底層三次握手失敗。如果服務器端與客戶端成功完成了三次握手,此時在服務器端就會建立一個連接,但這個連接在內核的等待隊列當中,服務器端需要通過調用 accept 函數將這個建立好的連接獲取上來。當服務器端將建立好的連接獲取上來后,雙方就可以通過調用 read/recv 函數和 write/send 函數進行數據交互了。


(4)四次揮手

由于雙方維護連接都是需要成本的,因此當雙方 TCP 通信結束之后就需要斷開連接,斷開連接的這個過程我們稱之為四次揮手。

哪邊不想給對方發消息了就要發送斷開連接請求,比如說客戶端要斷開連接:

  • 客戶端發送斷開連接請求,服務端返回 ACK 應答,這就已經兩次揮手了。
  • 服務端也要斷開連接,發送請求,客戶端返回 ACK 應答,一共就是四次揮手。

既然前面客戶端已經說明不給服務端發送數據了,為什么后邊還會發送確認應答呢?

這里的不發送數據的數據指的是用戶數據(應用層不發數據了),但并不代表底層沒有報文交互。

注意:這里的二三次揮手是有可能合并為一次的。


A. 四次揮手時的狀態變化

在揮手前客戶端和服務器都處于連接建立后的 ESTABLISHED 狀態。

  1. 客戶端為了與服務器斷開連接主動向服務器發起連接斷開請求,此時客戶端的狀態變為 FIN_WAIT_1。
  2. 服務器收到客戶端發來的連接斷開請求后對其進行響應,此時服務器的狀態變為 CLOSE_WAIT。
  3. 當服務器沒有數據需要發送給客戶端的時,服務器會向客戶端發起斷開連接請求,等待最后一個 ACK 到來,此時服務器的狀態變為 LASE_ACK。
  4. 客戶端收到服務器發來的第三次揮手后,會向服務器發送最后一個響應報文,此時客戶端進入 TIME_WAIT 狀態。
  5. 當服務器收到客戶端發來的最后一個響應報文時,服務器會徹底關閉連接,變為 CLOSED 狀態。
  6. 而客戶端則會等待一個 2MSL(Maximum Segment Lifetime,報文最大生存時間)才會進入 CLOSED 狀態。

B. 套接字和四次揮手之間的關系

  • 客戶端發起斷開連接請求,對應就是客戶端主動調用 close 函數關閉套接字。
  • 服務器發起斷開連接請求,對應就是服務器主動調用 close 函數關閉套接字。

一個 close 對應的就是兩次揮手,雙方都要調用 close,因此就是四次揮手。

  • 主動斷開連接的一方最終狀態是 TIME_WAIT。
  • 被動斷開連接的一方兩次揮手完成后的狀態是 CLOSE_WAIT。

a. CLOSE_WAIT 狀態

雙方在進行四次揮手時,如果只有客戶端調用了 close 函數,而服務器不調用 close 函數(不會發送 FIN),此時服務器就會進入 CLOSE_WAIT 狀態,而客戶端則會進入到 FIN_WAIT_2 狀態。

如果發現服務器具有大量的 CLOSE_WAIT 狀態的連接時,是什么原因導致的呢?

如果服務器沒有主動關閉對應的不需要的文件描述符 sockfd,此時在服務器端就會存在大量處于 CLOSE_WAIT 狀態的連接,而每個連接都會占用服務器的資源,最終就會導致服務器可用資源越來越少。

因此在編寫網絡套接字代碼時,如果發現服務器端存在大量處于 CLOSE_WAIT 狀態的連接,此時就可以檢查一下是不是服務器沒有及時調用 close 函數關閉對應的文件描述符。

此時服務器進入了 CLOSE_WAIT 狀態,結合我們四次揮手的流程圖,可以認為四次揮手沒有正確完成。

總結 :對于服務器上出現大量的 CLOSE_WAIT 狀態,原因就是服務器沒有正確的關閉 socket,導致四次揮手沒有正確完成。這是一個 BUG,只需要加上對應的 close 即可解決問題。

b. TIME_WAIT 狀態

四次揮手前三次如果發生了丟包情況,我們都可以利用超時重傳機制,最擔心的自然是第四次ACK應答時丟包

如果客戶端在發出第四次揮手后立即進入 CLOSED 狀態,此時服務器雖然進行了超時重傳,但已經得不到客戶端的響應了,因為客戶端已經將連接關閉了。

服務器在經過若干次超時重發后得不到響應,最終也一定會將對應的連接關閉,但在服務器不斷進行超時重傳期間還需要維護這條廢棄的連接,這樣對服務器是非常不友好的。

為了避免這種情況,因此客戶端在四次揮手后沒有立即進入 CLOSED 狀態,而是進入到了 TIME_WAIT 狀態進行等待,此時要是第四次揮手的報文丟包了,客戶端也能收到服務器重發的報文然后進行響應。

所以,TIME_WAIT 會保證最后一個 ACK 應答盡量被對方收到,而且可能斷開之前發送的報文還滯留在網絡中,那么 TIME_WAIT 就可以保證雙方通信信道上的數據在網絡中盡可能的消散。

c. TIME_WAIT 的等待時長

TCP 協議規定,主動關閉連接的一方在四次揮手后要處于 TIME_WAIT 狀態,等待兩個 MSL(maximum segment lifetime 報文最大生存時間)的時間才能進入 CLOSED 狀態。把從發送方到接收方經過的最大時間叫做 MSL。

使用 Ctrl+C 終止了 Server,所以 Server 是主動關閉連接的一方,在 TIME_WAIT 期間仍然不能再次監聽同樣的 Server 端口。

MSL 在 RFC1122 中規定為兩分鐘,但是各操作系統的實現不同,在 Centos7 上默認配置的值是60s。

為什么 TIME_WAIT 的等待時長設置為 2MSL?

MSL 是 TCP 報文的最大生存時間,因此 TIME_WAIT 狀態持續存在 2MSL 的話,就能保證在兩個傳輸方向上的尚未被接收或遲到的報文段都已經消失(否則服務器立刻重啟,可能會收到來自上一個進程的遲到的數據,但是這種數據很可能是錯誤的)。同時也是在理論上保證最后一個報文可靠到達的時間(假設最后一個 ACK 丟失,那么服務器會再重發一個 FIN,這時雖然客戶端的進程不在了,但是 TCP 連接還在,仍然可以重發 LAST_ACK)。

雖然 4 次揮手已經完成,但是主動斷開連接的一方要維持一段時間的 TIME_WAIT 狀態。在該狀態下,連接其實已經結束,但是它的地址信息(比如 ip)、port 依舊是被占用的。?

(5)bind 綁定失敗(端口復用)

為什么會 bind 綁定失敗?

上面的代碼里發現如果是服務端主動斷開連接,就會導致一段時間內無法 bind,就是因為服務器是 CLOSE_WAIT 狀態,該端口和連接依舊存在,所以會綁定失敗(端口被占用)。

服務器不能立即重啟的危害:比方說雙十一的時候,連接過多導致服務器掛掉了,此時想要立即重啟卻要等很久(60S),那么就會造成巨大的損失。

在 Server?的?TCP?連接沒有完全斷開之前不允許重新監聽,某些情況下可能是不合理的。

  • 服務器需要處理非常大量的客戶端的連接(每個連接的生存時間可能很短,但是每秒都有很大數量的客戶端來請求)。
  • 這個時候如果由服務器端主動關閉連接(比如某些客戶端不活躍,就需要被服務器端主動清理掉),就會產生大量 TIME_WAIT 連接。
  • 由于我們的請求量很大,就可能導致 TIME_WAIT 的連接數很多, 每個連接都會占用一個通信五元組(源 ip,源端口,目的 ip,目的端口,協議)。其中服務器的 ip 和端口和協議是固定的。如果新來的客戶端連接的 ip 和端口號和 TIME_WAIT 占用的鏈接重復了,就會出現問題。

如何解決這個問題呢?

設置套接字復用:使用 setsockopt() 設置 socket 描述符的選項 SO_REUSEADDR?為 1,表示允許創建端口號相同但 IP 地址不同的多個 socket 描述符。

在正常情況下,TCP?要經過三次握手建立連接,四次揮手斷開連接。

????服務端狀態轉化:

  • [CLOSED -> LISTEN]:服務器端調用 listen 后進入 LISTEN 狀態,等待客戶端連接。
  • [LISTEN -> SYN_RCVD]:一旦監聽到連接請求(同步報文段),就將該連接放入內核等待隊列中,并向客戶端發送 SYN 確認報文。
  • [SYN_RCVD -> ESTABLISHED]:服務端一旦收到客戶端的確認報文,就進入 ESTABLISHED 狀態,可以進行讀寫數據了。
  • [ESTABLISHED -> CLOSE_WAIT]:當客戶端主動關閉連接(調用close), 服務器會收到結束報文段, 服務器返回確認報文段并進入CLOSE_WAIT;
  • [CLOSE_WAIT -> LAST_ACK]:進入CLOSE_WAIT后說明服務器準備關閉連接(需要處理完之前的數據)。當服務器真正調用 close 關閉連接時, 會向客戶端發送 FIN,此時服務器進入 LAST_ACK 狀態,等待最后一個 ACK 到來(這個 ACK 是客戶端確認收到了 FIN)。
  • [LAST_ACK -> CLOSED]:服務器收到了對 FIN 的 ACK,徹底關閉連接。

客戶端狀態轉化:

  • [CLOSED -> SYN_SENT]:客戶端調用 connect,發送同步報文段。
  • [SYN_SENT -> ESTABLISHED]:connect 調用成功, 則進入 ESTABLISHED 狀態,開始讀寫數據。
  • [ESTABLISHED -> FIN_WAIT_1]:客戶端主動調用 close 時,向服務器發送結束報文段,同時進入 FIN_WAIT_1。
  • [FIN_WAIT_1 -> FIN_WAIT_2]:客戶端收到服務器對結束報文段的確認,則進入 FIN_WAIT_2,開始等待服務器的結束報文段。
  • [FIN_WAIT_2 -> TIME_WAIT]:客戶端收到服務器發來的結束報文段,進入 TIME_WAIT,并發出 LAST_ACK。
  • [TIME_WAIT -> CLOSED]:客戶端要等待一個 2MSL(Max Segment Life,報文最大生存時間)的時間,才會進入 CLOSED 狀態。

下圖是?TCP?狀態轉換的一個匯總:

  • 較粗的虛線表示服務端的狀態變化情況。
  • 較粗的實線表示客戶端的狀態變化情況。
  • CLOSED 是一個假想的起始點,不是真實狀態。

5、流量控制

接收端處理數據的速度是有限的。如果發送端發的太快,導致接收端的緩沖區被打滿,這個時候如果發送端繼續發送就會造成丟包,繼而引起丟包重傳等等一系列連鎖反應。

TCP 支持根據接收端的接收數據的能力來決定發送端發送數據的速度,這個機制叫做流量控制Flow Control)

前面在講 16 位窗口大小的時候,說傳輸數據的時候速度要適中,所以報頭中有 16 位窗口大小來控制傳輸速度,通過填寫 16 位窗口大小告訴對端自己的接收能力(接收緩沖區還剩多少)。

在進行流量控制時,發送方是如何在第一次發送數據的時候,得知對方的接收能力的呢?

需要通過交換報文,第一次發送數據不等同于第一次交換報文。在通信之前,就已經三次握手(第一次交換報文時)了,TCP 報文就是窗口大小。所以在握手期間,就可以互相交換窗口大小了。

當發送端得知接收端接收數據的能力為 0 時就會停止發送數據,此時發送端會通過以下兩種方式來得知何時可以繼續發送數據:

  1. 等待告知:接收端上層將接收緩沖區當中的數據讀走后,接收端向發送端發送一個 TCP 報文,主動將自己的窗口大小告知發送端,發送端得知接收端的接收緩沖區有空間后就可以繼續發送數據了。
  2. 主動詢問:發送端每隔一段時間向接收端發送報文(窗口探測),該報文不攜帶有效數據,只是為了詢問發送端的窗口大小,直到接收端的接收緩沖區有空間后發送端就可以繼續發送數據了。

這兩種策略在實際中是同時使用的,哪個先到就先處理哪個。

  • 接收端將自己可以接收的緩沖區大小放入 TCP 首部中的 “窗口大小” 字段,通過 ACK 端通知發送端。
  • 窗口大小字段越大,說明網絡的吞吐量越高。
  • 接收端一旦發現自己的緩沖區快滿了,就會將窗口大小設置成一個更小的值來通知給發送端。
  • 發送端接受到這個窗口之后,就會減慢自己的發送速度。
  • 如果接收端緩沖區滿了就會將窗口置為 0。這時發送方不再發送數據,但是需要定期發送一個窗口探測數據段,使接收端把窗口大小告訴發送端。

接收端如何把窗口大小告訴發送端呢?

TCP 的首部中有一個?16?位窗口字段,就是存放了窗口大小信息。

16?位數字最大表示?65535,那么?TCP?窗口最大就是?65535?字節嗎?

是的。
實際上 TCP 首部 40 字節選項中還包含了一個窗口擴大因子 M,實際窗口大小是窗口字段的值左移 M 位。
但窗口大小的調整還跟對方接收緩沖區的大小有關,如果對擴大因子進行了調整,那么可能就需要重新編譯源代碼的內核或者對 TCP 進行重新配置,需要更改操作系統的接收緩沖區的大小。

6、滑動窗口

在發送數據后,但沒收到應答之前,我們必須要把數據先保存起來,以支持后續可能出現的超時重傳,那么保存在哪里呢?

滑動窗口。

前面說過,多個報文一般是并行發送,即還沒收到應答,下一個報文就已經發送出去了,目的是為了提高效率。

可以把發送緩沖區分成三個部分

  1. 已經發送并且已經收到 ACK 的數據(上層考慮數據時可以直接覆蓋掉)。
  2. 已經發送還但沒有收到 ACK 的數據。
  3. 還沒有發送的數據。

滑動窗口的本質:sender 方可以一次性向對方推送數據的上限。滑動窗口是自己的發送緩沖區的一部分,通過不斷地滑動來重新劃分三段區間。

(1)如何理解滑動窗口

把緩沖區看成一個數組,那么滑動窗口的移動其實就是下標進行更新


A. 滑動窗口的大小

滑動窗口有上限,其大小和對方的接收能力有關。不管如何滑動,都要保證對方能夠正常接收(滑動窗口大小 <= 對方接受能力)。

滑動窗口一定會整體右移嗎?

不一定,可能會向右滑動,也可能保持不變。因為數據可能在對方的接收緩沖區遲遲沒有被拿走,那就只會導致滑動窗口的左側不斷向右移動,而右側不動。

滑動窗口可以為 0 嗎?

可以,當 win_start = win_end 時,滑動窗口就為 0。

因為如果發送方發送數據,而接收方的上層一直不取走數據,發送方卻一直發送數據,就會導致性能越來越差,tcp_win 越來越小,也就是滑動窗口的左側一直向后移動,右側卻不變,最終滑動窗口會變為 0。

滑動窗口如何滑動更新?

當發送端收到對方的應答時,如果應答報文中的確認序號為 ACK_SEQ,收到的應答報文中的滑動窗口大小為 tcp_win,此時就可以將 win_start 更新為 ACK_SEQ,win_end 更新為 win_start + tcp_win。

如果收到的 ACK 不是最左側數據(最開始的報文)的應答,而是中間的,有可能嗎?會有影響嗎?

可能。因為 TCP 是可靠傳輸,不可能出現亂序,所以如果收到了中間數據的應答,一定是發生了丟包

超時重傳背后的含義:在沒有收到應答時,數據必須被暫時保存起來。

確認應答策略對每一個發送的數據段都要給一個?ACK?確認應答,收到?ACK?后再發送下一個數據段。這樣做有一個比較大的缺點,就是性能較差,尤其是數據往返的時間較長的時候。

既然這樣一發一收的方式性能較低,那么一次發送多條數據就可以大大的提高性能(其實是將多個段的等待時間重疊在一起了)

  • 窗口大小指的是無需等待確認應答而可以繼續發送數據的最大值。上圖的窗口大小就是 4000 個字節(四個段)。
  • 發送前四個段的時候不需要等待任何 ACK,直接發送。
  • 收到第一個 ACK 后,滑動窗口向后移動,繼續發送第五個段的數據,依次類推。
  • 操作系統內核為了維護這個滑動窗口,需要開辟發送緩沖區來記錄當前還有哪些數據沒有應答。只有確認應答過的數據,才能從緩沖區刪掉。
  • 窗口越大,則網絡的吞吐率就越高。

如果滑動窗口一直向右滑動,是否會出現越界問題?總有空間用完的時候,該如何處理呢?

不會出現越界問題,TCP 的發送緩沖區被內核組織成了環形結構

流量控制是以可靠性問題為主,防止我們發送過多的數據;以效率問題為輔,對一個已經傳輸的數據進行丟棄,就得重傳,因此就影響了效率。

滑動窗口解決的是效率問題還是可靠性問題?

滑動窗口的側重點在于解決效率問題,它可以限制緩沖區的范圍,能夠一次性向對方發送大量的數據。以效率為主,可靠為輔。

那么如果出現了丟包,如何進行重傳?

(2)情況一 ——?數據包已經抵達,ACK 應答丟了

根據確認序號的定義,如果收到的是 3001,那么說明 3000 以前的數據全部都收到了,那么就把 win_start 移動到 3001 即可。

這種情況下,部分?ACK?丟了并不要緊,因為可以通過后續的?ACK?進行確認。


(3)情況二 ——?數據包直接丟了

當 1001~2000 的數據包丟失后,發送端會一直收到確認序號為 1001 的響應報文,就是在提醒發送端 “下一次應該從序號為 1001 的字節數據開始發送”。

而如果連續收到三個同樣的確認序號,就會觸發重傳機制。這種機制被稱為 “高速重發控制”(也叫 “快重傳”)。
快重傳是能夠快速進行數據的重發,當發送端連續收到三次相同的應答時就會觸發快重傳,而不像超時重傳一樣需要通過設置重傳定時器,在固定的時間后才會進行重傳。

  • 當某一段報文段丟失之后,發送端會一直收到 1001 這樣的 ACK,就像是在提醒發送端 “我想要的是 1001” 一樣。
  • 如果發送端主機連續三次收到了同樣一個 "1001" 這樣的應答,就會將對應的數據 1001 - 2000 重新發送;
  • 這個時候接收端收到了 1001 之后,再次返回的 ACK 就是 7001 了(因為 2001 - 7000)接收端其實之前就已經收到了,被放到了接收端操作系統內核的接收緩沖區中。

總結:滑動窗口的左端就是通過確認序號確定的,右端是通過左端和對方接收緩沖區的剩余空間決定的。

既然有了快重傳,為什么還要有超時重傳?

因為快重傳是有條件的,必須收到連續三個以上的同樣的 ACK。快重傳和超時重傳不是對立的,而是協作的。

7、擁塞控制

1000 個報文丟掉一兩個很正常,重復發即可,但如果 1000 個報文有 999 個都丟了,那我們還要選擇重傳嗎?

舉例:一個班 40 個人考試,結果只有一個人掛科了,那大概率是這個人的問題;但如果掛了 39個,那還是學生的問題嗎?

針對這種大面積的丟包情況,TCP 就會考慮是網絡擁塞問題,此時重傳就沒什么用了,重傳也只會加重網絡故障問題。

如何解決網絡擁塞問題?

當網絡出現擁塞問題時,通信雙方雖然不能提出特別有效的解決方案,但雙方主機可以做到不加重網絡的負擔。雙方通信時如果出現大量丟包,不應該立即將這些報文進行重傳,而應該少發數據甚至不發數據,等待網絡狀況恢復后雙方再慢慢恢復數據的傳輸速率。

注意:網絡擁塞時影響的不只是一臺主機,而幾乎是該網絡當中的所有主機,此時所有使用 TCP 傳輸控制協議的主機都會執行擁塞避免算法。

雖然 TCP 有了滑動窗口,能夠高效可靠的發送大量的數據。但是如果在剛開始階段就發送大量的數據仍然可能引發問題,因為網絡上有很多的計算機,可能當前的網絡狀態就比較擁堵,在不清楚當前網絡狀態下貿然發送大量的數據,是很有可能引起雪上加霜的。

TCP 引入慢啟動機制,在剛開始通信時先發送少量的數據,摸清當前的網絡擁堵狀態,再決定按照多大的速度傳輸數據。

(1)擁塞窗口

單臺主機一次向網絡中發送大量數據時,可能會引發網絡擁塞的上限值。超過擁塞窗口這個值的時候就可能引發網絡擁塞問題。

發送開始的時候定義為 1,每次接收到一個 ACK 應答,就加 1,每次發送數據包的時候,將擁塞窗口和接收端主機反饋的窗口大小做比較,取較小的值作為實際發送數據的窗口大小,即滑動窗口的大小。

滑動窗口大小 = min(擁塞窗口,對方窗口大小[接收能力])

每收到一個 ACK 應答擁塞窗口的值就加 1,此時擁塞窗口的增長速度是以指數級別進行增長的,如果先不考慮對方接收數據的能力,那么滑動窗口的大小就只取決于擁塞窗口的大小,此時擁塞窗口的大小變化為:1 2 4 8 …… 但指數增長是非常恐怖的,此時就有可能導致網絡再次擁塞。

此時就引入了慢啟動的閾值,當擁塞窗口的大小超過這個閾值時,就不再按指數的方式增長,而是按線性的方式增長。慢啟動只是指初使時慢,但是增長速度非常快。為了不增長的那么快,因此不能使擁塞窗口單純的加倍。

前期慢開始是為了讓網絡自主恢復,后面快是為了盡快恢復通信。

  • 當?TCP?開始啟動的時候,慢啟動閾值設置為對方窗口大小的最大值。
  • 在每次超時重發的時候,慢啟動閾值會變成原來的一半,同時擁塞窗口置回?1,如此循環下去

少量的丟包僅僅是觸發超時重傳,大量的丟包就認為網絡擁塞。

當 TCP?通信開始后,網絡吞吐量會逐漸上升,隨著網絡發生擁堵,吞吐量會立刻下降。擁塞控制歸根結底是?TCP?協議想盡可能快的把數據傳輸給對方,但又要避免給網絡造成太大壓力的折中方案。

8、延遲應答

現在接收方緩沖區有很多數據,但是應用層有很大概率會馬上把數據拿走,如果等一等再應答就可以返回更大的窗口。需要注意的是,延遲應答的目的不是為了保證可靠性,而是留出一點時間讓接收緩沖區中的數據盡可能被上層應用層消費掉,此時在進行 ACK 響應的時候報告的窗口大小就可以更大,從而增大網絡吞吐量,進而提高數據的傳輸效率。

注意:不是所有的數據包都可以延遲應答。

如果接收數據的主機立刻返回 ACK 應答,這時候返回的窗口可能比較小。假設接收端緩沖區為 1M,一次收到了 500K 的數據。如果立刻應答,返回的窗口就是 500K。但實際上可能處理端處理的速度很快,10ms 之內就把 500K 數據從緩沖區消費掉了。在這種情況下,接收端處理還遠沒有達到自己的極限,即使窗口再放大一些也能處理過來。如果接收端稍微等一會再應答,比如等待 200ms 再應答,那么這個時候返回的窗口大小就是 1M。

記住:窗口越大,網絡吞吐量就越大,傳輸效率就越高,目標是在保證網絡不擁塞的情況下盡量提高傳輸效率。

所有的包都可以延遲應答嗎?

不是。

  • 數量限制:每隔 N 個包就應答一次。
  • 時間限制:超過最大延遲時間就應答一次(這個時間不會導致誤超時重傳)。

延遲應答具體的數量和超時時間依操作系統不同也有差異,一般 N 取 2,超時時間取 200ms。

9、捎帶應答

接收方收到數據要給發送方一個應答,如果剛好接收方也要發送數據,可以直接一起返回。捎帶應答最直觀的角度實際上也是發送數據的效率,此時雙方通信時就可以不用再發送單純的確認報文了。

在延遲應答的基礎上,發現很多情況下,客戶端服務器在應用層也是 “一發一收” 的,意味著客戶端給服務器說了 "How are you",服務器也會給客戶端回一個 "Fine, thank you"。那么這個時候 ACK 就可以搭順風車,和服務器回應的 "Fine, thank you" 一起回給客戶端。

10、面向字節流

創建一個 TCP 的 socket,同時在內核中創建一個發送緩沖區和一個接收緩沖區

  • 調用 write 時,數據會先寫入發送緩沖區中。
  • 如果發送的字節數太長,會被拆分成多個 TCP 的數據包發出。
  • 如果發送的字節數太短,就會先在緩沖區里等待,等到緩沖區長度差不多了,或者其他合適的時機發送出去。
  • 接收數據時,數據也是從網卡驅動程序到達內核的接收緩沖區。
  • 然后應用程序可以調用 read 從接收緩沖區拿數據。
  • 另一方面,TCP 的一個連接既有發送緩沖區,也有接收緩沖區。那么對于這一個連接既可以讀數據,也可以寫數據,這個概念叫做全雙工。

由于緩沖區的存在,TCP?程序的讀和寫不需要一一匹配,例如:

  • 寫 100 個字節數據時, 可以調用一次 write 寫 100 個字節,也可以調用 100 次 write,每次寫一個字節。
  • 讀100個字節數據時, 也完全不需要考慮寫的時候是怎么寫的, 既可以一次read 100個字節, 也可以一次 read 一個字節,重復 100 次。

實際上對于 TCP 來說,它并不關心發送緩沖區當中的是什么數據,在 TCP 看來這些只是一個個的字節數據,它的任務就是將這些數據準確無誤的發送到對方的接收緩沖區當中就行了,而至于如何解釋這些數據完全由上層應用來決定,這就叫做面向字節流。

對比 UDP,UDP 不是面向字節流的,發 1 次必須就要讀 1 次,發 10 次就必須讀 10 次。這種報文和報文在傳輸層有明顯邊界的的協議就叫做面向數據報。

11、粘包問題

(1)什么是粘包

因為 TCP 是面向字節流的,所以需要應用層來分開這些報文,如果處理的不好就會出現多讀了或者少讀了影響到了后續報文,這種問題就叫做粘包

首先要明確,粘包問題中的 “包” 是指的應用層的數據包。

  • 在 TCP 的協議頭中,沒有如同 UDP 一樣的 “報文長度” 這樣的字段,但是有一個序號這樣的字段。
  • 站在傳輸層的角度,TCP 是一個個報文過來的,按照序號排好序放在緩沖區中。
  • 站在應用層的角度,看到的只是一串連續的字節數據。那么應用程序看到了這么一連串的字節數據,就不知道從哪個部分開始到哪個部分,是一個完整的應用層數據包。

如何避免粘包問題呢?

明確兩個包之間的邊界。解決粘包問題的本質就是要確定報文與報文之間的邊界。

  • 對于定長的包,保證每次都按固定大小讀取即可。例如上面的 Request 結構,是固定大小的,那么就從緩沖區從頭開始按 sizeof(Request) 依次讀取即可。
  • 對于變長的包,可以在包頭的位置,約定一個包總長度的字段,從而就知道了包的結束位置。
  • 對于變長的包,還可以在包和包之間使用明確的分隔符(應用層協議,是程序猿自己來定的,只要保證分隔符不和正文沖突即可)。

對于 UDP 協議來說,是否也存在 “粘包問題” 呢?

UDP 的報文與報文之間的邊界是明確的,因為 UDP 有標準報頭定長和 16 為 UDP 長度,所以當它收到報文時,去掉報頭剩下的就是有效載荷,就能夠保證讀到的就是完整的報頭。
對于 UDP,如果還沒有上層交付數據,UDP 的報文長度仍然在。同時,UDP 是一個個把數據交付給應用層,本身就有很明確的數據邊界。
站在應用層的角度,使用 UDP 的時候,要么收到完整的 UDP 報文,要么不收。不會出現 “半個” 的情況。

12、TCP?異常情況

(1)進程終止

兩個已經建立連接的進程,其中一個進程突然掛掉了,此時建立好的連接會怎么樣?

其實連接也是個文件,而文件描述符是隨進程的,進程退出,操作系統就會 close 掉這個文件。所以操作系統會正常四次揮手斷開連接,跟自己?close 掉沒區別。

進程終止會釋放文件描述符,仍然可以發送?FIN,和正常關閉沒有什么區別。


(2)機器重啟

和進程終止的情況相同。

當重啟主機時,操作系統會先殺掉所有進程然后再進行關機重啟,因此機器重啟和進程終止的情況是一樣的,此時雙方操作系統也會正常完成四次揮手,然后釋放對應的連接資源。

(3)機器掉電?/?網線斷開

當客戶端掉線后,服務器端在短時間內無法知道客戶端掉線了,因此在服務器端會維持與客戶端建立的連接,但這個連接也不會一直維持,因為 TCP 是有保活策略的。
正常的一方會不停的詢問對方連接是否還存在,發現不在了就直接斷開。

接收端認為連接還在,一旦接收端有寫入操作,接收端發現連接已經不在了就會進行 reset。即使沒有寫入操作,TCP 自己也內置了一個保活定時器,會定期詢問對方是否還在。如果對方不在,也會把連接釋放。

另外,應用層的某些協議也有一些這樣的檢測機制。例如 HTTP 長連接中,也會定期檢測對方的狀態。例如 QQ,在 QQ 斷線之后也會定期嘗試重新連接。

13、小結

為什么?TCP?這么復雜?

因為要保證可靠性,同時又盡可能的提高性能。

(1)可靠性

  • 校驗和
  • 序列號(按序到達)
  • 確認應答
  • 超時重發
  • 連接管理
  • 流量控制
  • 擁塞控制

(2)提高性能

  • 滑動窗口
  • 快速重傳
  • 延遲應答
  • 捎帶應答

(3)其他

  • 定時器(超時重傳定時器,保活定時器,TIME_WAIT 定時器等)

(4)基于?TCP?應用層協議

  • HTTP
  • HTTPS
  • SSH
  • Telnet
  • FTP
  • SMTP

當然,也包括自己寫的?TCP?程序時自定義的應用層協議。

五、TCP / UDP?對比

TCP 和 UDP 之間的優點和缺點,不能簡單,絕對的進行比較。

  • TCP 用于可靠傳輸的情況,應用于文件傳輸,重要狀態更新等場景。
  • UDP 用于對高速傳輸和實時性要求較高的通信領域。例如,早期的 QQ,視頻傳輸等,另外UDP可以用于廣 播。

歸根結底,TCP 和 UDP 都是程序員的工具,什么時機用,具體怎么用,還是要根據具體的需求場景去判定。

六、理解?listen 的第二個參數

accept 要不要參與三次握手的過程呢?

不需要參與三次握手,accept 從底層直接獲取已經建立好的連接。

換而言之,需要先建立好連接,然后才能 accept 獲取對應的連接。

如果不調用 accept,能否建立連接成功呢?

能。因為能否建立連接跟 accept 沒有關系,雖然會有影響,但不起決定性因素。一定是要先建立好連接,然后才能調用 accept 成功。

如果上層來不及調用 accept,并且對端還來了大量的連接,難道所有的連接都應該先建立好嗎?

并不是。服務器在進行連接獲取的時候,服務器本身要維護一個連接隊列,不能沒有、不能太長,和 listen 的第二個參數有關。

listen 的第二個參數意義底層全連接隊列的長度 = listen的第二個參數 + 1

此時啟動?2?個客戶端同時連接服務器,用 netstat 查看服務器狀態,一切正常。但是啟動第 3 個客戶端時,發現服務器對于第 3 個連接的狀態存在問題了。

客戶端狀態正常,但是服務器端出現了 SYN_RECV 狀態,而不是 ESTABLISHED 狀態。

這是因為?Linux?內核協議棧為一個?tcp?連接管理使用兩個隊列:

  1. 半鏈接隊列(生命周期很短,用來保存處于?SYN_SENT?和?SYN_RECV?狀態的請求)
  2. 全連接隊列(accpetd?隊列,用來保存處于?established?狀態,但是應用層沒有調用?accept?取走的請求)

而全連接隊列的長度會受到 listen 第 2 個參數的影響。全連接隊列滿了的時候,就無法繼續讓當前連接的狀態進入 established 狀態了。這個隊列的長度通過上述實驗可知,是 listen 的第二個參數 + 1。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/902221.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/902221.shtml
英文地址,請注明出處:http://en.pswp.cn/news/902221.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

得佳勝哲訊科技 SAP項目啟動會:膠帶智造新起點 數字轉型新征程

在全球制造業加速向數字化、智能化轉型的浪潮中&#xff0c;膠帶制造行業正迎來以“自動化生產、數據化運營、智能化決策”為核心的新變革。工業互聯網、大數據分析與智能裝備的深度融合&#xff0c;正推動膠帶制造從傳統生產模式向“柔性化生產精準質量控制全鏈路追溯”的智慧…

大數據學習棧記——MapReduce技術

本文介紹hadoop中的MapReduce技術的應用&#xff0c;使用java API。操作系統&#xff1a;Ubuntu24.04。 MapReduce概述 MapReduce概念 MapReduce是一個分布式運算程序的編程框架&#xff0c;核心功能是將用戶編寫的業務邏輯代碼和自帶默認組件整合成一個完整的分布式運算程序…

Centos9 離線安裝 MYSQL8

centos 9 離線安裝 mysql 8 參考教程 1. 官網下載mysql 下載地址 2. 將文件傳輸到Centos中解壓 軟件全部安裝到了/opt中 在opt中新建mysql目錄&#xff0c;解壓到mysql目錄中 tar -xvf mysql壓縮文件 mysql[rootcentoshost mysql]# ls mysql-community-client-8.4.5-1.e…

helm的go模板語法學習

1、helm chart 1.0、什么是helm&#xff1f; 介紹&#xff1a;就是個包管理器。理解為java的maven、linux的yum就好。 安裝方法也可參見官網&#xff1a; https://helm.sh/docs/intro/install 通過前面的演示我們知道&#xff0c;有了helm之后應用的安裝、升級、查看、停止都…

display的一些學習記錄

收集的SDM的log&#xff1a; 01-01 00:00:15.311 933 933 I SDM : Creating Display HW Composer HAL 01-01 00:00:15.311 933 933 I SDM : Scheduler priority settings completed 01-01 00:00:15.311 933 933 I SDM : Configuring RPC threadpool 0…

【Rust 精進之路之第2篇-初體驗】安裝、配置與 Hello Cargo:踏出 Rust 開發第一步

系列&#xff1a; Rust 精進之路&#xff1a;構建可靠、高效軟件的底層邏輯 **作者&#xff1a;**碼覺客 發布日期&#xff1a; 2025-04-20 引言&#xff1a;磨刀不誤砍柴工&#xff0c;裝備先行&#xff01; 在上一篇文章中&#xff0c;我們一起探索了 Rust 誕生的緣由&…

【深度學習】計算機視覺(17)——ViT理解與應用

文章目錄 Embedding1 概念2 Q&A &#xff08;1&#xff09;3 Positional Encoding4 Q&A &#xff08;2&#xff09; ViT樣例及Embedding可視化理解1 簡化ViT練習2 CLS Token3 Embedding可視化4 多頭注意力可視化 Embedding技術體系結構參考來源 在研究中對特征的編碼和…

肖特基二極管詳解:原理、作用、應用與選型要點

一、肖特基二極管的基本定義 肖特基二極管&#xff08;Schottky Diode&#xff09; 是一種基于金屬-半導體結&#xff08;肖特基勢壘&#xff09;的二極管&#xff0c;其核心特性是低正向壓降&#xff08;Vf≈0.3V&#xff09;和超快開關速度。 結構特點&#xff1a;陽極采用金…

DeepSeek在數據倉庫的10大應用場景

一、智能數據集成與清洗 多源數據整合&#xff1a;DeepSeek能夠從多種數據源中提取、轉換和加載數據&#xff0c;實現跨系統數據的高效整合。 數據清洗與標準化&#xff1a;通過智能算法自動識別并糾正數據中的錯誤、不一致性和缺失值&#xff0c;提升數據質量。 二、數據倉…

提示詞構成要素對大語言模型跨模態內容生成質量的影響

提示詞構成要素對大語言模型跨模態內容生成質量的影響 提示詞清晰度、具象性與質量正相關 限定指向性要素優于引導指向性要素 大語言模型生成內容保真度偏差 以訊飛星火大模型為實驗平臺,選取100名具備技術素養的人員,從提示詞分類、構成要素和實踐原則歸納出7種提示詞組…

BeautifulSoup 庫的使用——python爬蟲

文章目錄 寫在前面python 爬蟲BeautifulSoup庫是什么BeautifulSoup的安裝解析器對比BeautifulSoup的使用BeautifulSoup 庫中的4種類獲取標簽獲取指定標簽獲取標簽的的子標簽獲取標簽的的父標簽(上行遍歷)獲取標簽的兄弟標簽(平行遍歷)獲取注釋根據條件查找標簽根據CSS選擇器查找…

關于MacOS使用Homebrew的詳細介紹

Homebrew 是 macOS&#xff08;和 Linux&#xff09;上最流行的包管理工具&#xff08;Package Manager&#xff09;&#xff0c;用于快速安裝、更新和管理各種開發工具、命令行程序、開源軟件等。它類似于&#xff1a; Ubuntu/Debian 的 aptCentOS/RHEL 的 yumWindows 的 Cho…

最新扣子空間實操指南

一、首先要先獲取到內部測試的邀請碼&#xff0c; 我們先打開扣子空間官網&#xff1a;https://space.coze.cn/ 輸入邀請碼后進入該頁面&#xff1a; 它這里支持文件上傳&#xff0c;擴展里面有很多插件&#xff0c;頁支持MCP各種插件. 探索模式有兩種&#xff0c;一種是ai自…

ubuntu22.04安裝dukto

1.添加源 sudo add-apt-repository ppa:xuzhen666/dukto2.進行更新和安裝 sudo apt update sudo apt install dukto3.報錯 $ sudo apt install dukto 正在讀取軟件包列表... 完成 正在分析軟件包的依賴關系樹... 完成 正在讀取狀態信息... 完成 您也許需要…

Java編程基礎(第四篇:字符串初次介紹)

前言 HelloWorld寫的多了&#xff0c;語法熟悉一點了吧&#xff0c;其中有段代碼還沒介紹&#xff0c;它就是字符串 public class HelloWorld { public static void main(String[] args) { printBaby(); } static void printBaby() { System.out.print("baby"); } } …

安卓手機怎樣配置數據加速

利用系統自帶功能&#xff1a; 選擇網絡模式&#xff1a;進入手機 “設置”&#xff0c;找到 “網絡” 或 “移動網絡” 選項&#xff0c;點擊 “高級設置”&#xff0c;選擇合適的網絡模式&#xff0c;如優先選擇 4G 或 5G 網絡&#xff0c;以獲得更快的速度。開啟網絡加速功能…

Day3:個人中心頁面布局前端項目uniapp壁紙實戰

接下來我們來弄一下個人中心頁面布局user.vue <template><view class"userLayout"><view class"userInfo"><view class"avatar"><image src"../../static/Kx.jpg" mode"aspectFill"></im…

線性回歸之正則化(regularization)

文章目錄 機器學習中的"防過擬合神器"&#xff1a;正則化全解析1. 正則化&#xff1a;不只是"規矩"那么簡單1.1 魯棒性案例說明 2. L1正則化&#xff1a;冷酷的特征選擇器3. L2正則化&#xff1a;溫柔的約束者4. L1 vs L2&#xff1a;兄弟間的較量5. 正則化…

mapbox基礎,加載視頻到地圖

????? 主頁: gis分享者 ????? 感謝各位大佬 點贊?? 收藏? 留言?? 加關注?! ????? 收錄于專欄:mapbox 從入門到精通 文章目錄 一、??前言1.1 ??mapboxgl.Map 地圖對象1.2 ??mapboxgl.Map style屬性1.3 ??raster 柵格圖層 api二、??加載視頻到…

Linux系統的遠程終端登錄、遠程圖形桌面訪問、 X圖形窗口訪問

目錄 一、配置Ubuntu系統的網絡和用戶 1、設置虛擬機網絡為橋接模式 2.查看當前ip、子網掩碼、網關 3.修改配置文件 二、遠程終端登錄Ubuntu 三、使用XShell遠程連接 1、確保SSH服務已啟動 2、檢查SSH服務狀態 3、獲取樹莓派IP地址 4、Xming安裝好之后打開讓它在后臺…