HarmonyOS-ArkUI: Web組件加載流程1
HarmonyOS-ArkUI Web控件基礎鋪墊1-HTTP協議-數據包內容
HarmonyOS-ArkUI Web控件基礎鋪墊2-DNS解析
HarmonyOS-ArkUI Web控件基礎鋪墊3--TCP協議- 從規則本質到三次握手-CSDN博客
接上文,上文我們講解了:
- 數據在四層網絡岑層架構中流轉方式
- 傳輸層存在的意義
- TCP協議具備的能力分析
- TCP數據包部分字段設計由來
- TCP建立連接的三次握手。
本文續上文講。將TCP是如何斷開連接的。
TCP的全雙工
TCP連接是全雙工的。雙工的意思就是,同時可以進行信號的雙向傳輸。也就是 A->B, 與 B->A可以同時發生。
我們在上文中其實提到過,TCP建立連接之前,會讓操作系統為在內核區劃分一些緩存。等到真正的建連之后,又會新建自己專門處理TCP事宜的緩沖區。這些緩存如果注意的話,您會發現均有一個,
- 發送緩沖區。
- 接收緩沖區。
開了倆緩沖區,就是為了更好的實現全雙工!如圖:
TCP協議,全生命周期中,都是按照全雙工來處理的。包括三次握手這么早的操作也是全雙工。 所以我們才會那么強調 發送包與響應包要--對得上,因為可能會收到"野包"!所以三次握手的時候,便強調了seq字段和ack字段的搭配規則,因為要用他們排除野包。
TCP斷聯,最主要斷的是什么
TCP斷開連接其實就是AB雙方比較安全的不再對數據包進行處理的過程。確定了可以不再處理之后便可以釋放自己本地的資源。我們知道了全雙工這種支持,無論是A還是B,其代碼運行時中必然會包含:
- 發送數據處理邏輯,以及運行時發送相關占用的資源,如內核中的發送緩沖區。
- 接收數據處理邏輯,以及運行時接收相關占用的資源,如內核中的接收緩沖區。
如下圖所示,A和B如果他倆想斷聯,回收掉自己占用的資源,則必須打斷二者通路:
而TCP中的斷聯,就是將這套全雙工通路斷聯,也就是兩個方向的傳輸全部斷聯!
斷掉一個方向上的傳輸是由哪一方決定的?
答: 發送方!
斷掉一個方向上的傳輸發起者,無非是發送方,和接收方。
- 假設發送方是發起者
- 發送方最了解自己的數據,什么時候結尾它最清楚!發完了就斷聯,也順理成章。
- 假設接收方是發起者
- 接收方就是個被動接收者,著實不知道對方傳來的數據是什么情況。 傳沒傳完不知道,提前斷聯數據會丟失。 斷聯的太晚了,發送方數據早傳輸完了它還不斷聯,自己的資源被占用啥活也干不成。
綜上,發起斷聯者必定是,發送方!
而 A 和 B這套系統里,因為是全雙工工作, 他倆是互為發送方的!
僅斷掉一個方向上的傳輸,怎么做?
那就是發送方向接收方發送一個數據, 這個數據里的內容可以讓對方檢測出,自己想要斷掉這條連路,以后不會傳輸業務數據了! 解決辦法就是用 FIN 字段標記:
FIN為finish的縮寫,表示發送方已經完成數據傳輸,并請求關閉連接。占用1個Bit位。其核心作用便是通知對端,本方向的數據傳輸已經結束了。
如圖所示為單方向斷掉連接發生的細節, 也是TCP四次揮手中,前兩次揮手的細節!尤其要注意一下最左側的紅色備注,這個關系到對各個字段設置的理解:
上圖中值得注意的是,第一次的揮手包, 我們的ACK=0, ack序列也是無效的。但是之后服務端處理的時候,它的ACK倒是1, ack也是正常的。 這是為什么呢??如果您之前了解四次揮手,會曉得第三次揮手,服務端發過來的包ACK = 1 !如下圖為四次揮手的全部字段變化:
揮手 | 方向 | SYN | ACK | FIN | seq | ack |
第一次揮手 | A -> B | 0 | 0 | 1 | a | - |
第二次揮手 | B -> A | 0 | 1 | 0 | b | a+1 |
第三次揮手 | B -> A | 0 | 1 | 1 | b+ n(半關閉數據長度) | a+1 |
第四次揮手 | A -> B | 0 | 1 | 0 | a + 1 | b+ n+ 1 |
因為斷開聯接是一個事務!不可被打斷,不可被顛倒!
A第一次揮手的時候ACK=0, 既代表著A以后都不打算發送業務數據包了,這個包就是一個控制包,還是自己主動發起的!并且以此為TCP斷開連接的起點。之后不容打斷。。也就是假設A已經關閉了 A -> B的通路時候,萬一收到一個包,這個包的格式正是服務端發起的第一次揮手包, 就直接被扔掉不予處理。避免混亂!
TCP全雙工斷聯--四次揮手
到目前為止我們已經處理完一半了,如果具象化的話,現在A 和 B之間應是這樣的場景!
也就是此時其實B仍然可以向A發數據的!可能有信息還沒有傳完,需要繼續傳輸。 也可能也沒什么數據好傳了。但任務總有完成的那一刻。完工后便要發起 B -> A 方向的斷聯了!
接下來是第三次揮手和第四次揮手的流程:
四次揮手總結:
我們上文著重講了下原理。下方為四次揮手的流程歸納:
第一次揮手(Client → Server)
字段值:SYN=0, ACK=0, FIN=1, seq=u, ack=v(無效)
- seq=u:u為客戶端最后發送數據的下一字節序號(例:最后數據字節序號為100,則u=101)
- ACK=0:因主動發起關閉,無需確認對方數據
- FIN=1:觸發關閉流程,占用1序列號(下次seq=u+1)
- 狀態變化:Client →
FIN_WAIT_1
2. 第二次揮手(Server → Client)
字段值:SYN=0, ACK=1, FIN=0, seq=v, ack=u+1
- ack=u+1:確認客戶端的FIN(FIN占1序列號,故+1)
- seq=v:v為服務器當前數據流位置(與客戶端FIN無關)
- FIN=0:僅確認關閉請求,不立即關閉本方連接
- 狀態變化:Server →
CLOSE_WAIT
, Client →FIN_WAIT_2
3. 第三次揮手(Server → Client)
字段值:SYN=0, ACK=1, FIN=1, seq=w, ack=u+1
- seq=w:w = v + 半關閉期間數據長度(例:第二次揮手后發送80字節數據,則w=v+80)
- ack=u+1:仍為首次揮手的FIN確認(客戶端無新數據)
- FIN=1:服務端數據發送完畢,關閉本方通道
- 狀態變化:Server →
LAST_ACK
4. 第四次揮手(Client → Server)
字段值:SYN=0, ACK=1, FIN=0, seq=u+1, ack=w+1
- seq=u+1:首次揮手的FIN消耗序號u,故下一序號為u+1
- ack=w+1:確認服務端的FIN(FIN占1序列號,故w+1)
- ACK=1:必須置1以確認FIN
- 狀態變化:Client →
TIME_WAIT
(2MSL后關閉),Server →CLOSED
為什么揮手要四次
仍是一個八股問題。以下是貼的答案:
服務器在收到客戶端的 FIN 報文段后,可能還有一些數據要傳輸,所以不能馬上關閉連接,但是會做出應答,返回 ACK 報文段.
接下來可能會繼續發送數據,在數據發送完后,服務器會向客戶單發送 FIN 報文,表示數據已經發送完畢,請求關閉連接。服務器的ACK和FIN一般都會分開發送,從而導致多了一次,因此一共需要四次揮手。