[TOC]
# 協議頭

* 至少占用24個字節
* source port:客戶端的端口
* destination port:服務端口
* 端口最大值是2^16-1
* Sequence Number:順序號,4個字節的整型,網絡包分塊傳輸時走的路由可能不一致,先發的包可能后到
* Acknowledgment Number:應答號,
<br>
<br>
# 狀態機
TCP 的狀態機是很復雜的,并且與建立斷開連接時的握手息息相關,接下來就來詳細描述下兩種握手。
<br>
TCP有6種標示:
* SYN(建立聯機)
* ACK(確認)
* PSH(傳送)
* FIN(結束)
* RST(重置)
* URG(緊急)
<br>

<br>
## 建立連接三次握手

首先假設主動發起請求的一端稱為客戶端,被動連接的一端稱為服務端。不管是客戶端還是服務端,TCP 連接建立完后都能發送和接收數據,所以 TCP 是一個全雙工的協議。
起初,兩端都為 CLOSED 狀態。在通信開始前,雙方都會創建 TCB。 服務器創建完 TCB 后便進入 LISTEN 狀態,此時開始等待客戶端發送數據。
**第一次握手**
客戶端向服務器發出連接請求報文,這時報文首部中的同部位 **SYN=1**,同時隨機生成初始序列號 **seq=x**,此時,TCP客戶端進程進入了 **SYN-SENT**(同步已發送狀態)狀態。TCP規定,SYN報文段(SYN=1的報文段)不能攜帶數據,但需要消耗掉一個序號。這個三次握手中的開始。表示客戶端想要和服務端建立連接。
**第二次握手**
TCP服務器收到請求報文后,如果同意連接,則發出確認報文。確認報文中應該 **ACK=1**,**SYN=1**,確認號是 **ack=x+1**,同時也要為自己隨機初始化一個序列號 seq=y,此時,TCP服務器進程進入了 SYN-RCVD(同步收到)狀態。這個報文也不能攜帶數據,但是同樣要消耗一個序號。這個報文帶有SYN(建立連接)和ACK(確認)標志,詢問客戶端
是否準備好。
**第三次握手**
TCP客戶進程收到確認后,還要向服務器給出確認。確認報文的 **ACK=1**, **ack=y+1**,此時,TCP連接建立,客戶端進入ESTABLISHED(已建立連接)狀態。
TCP規定,ACK報文段可以攜帶數據,但是如果不攜帶數據則不消耗序號。這里客戶端表示我已經準備好。
<br>
### 每一次握手的作用
第一次握手:Client 什么都不能確認;Server 確認了對方發送正常
第二次握手:Client 確認了:自己發送、接收正常,對方發送、接收正常;Server 確認了:自己接收正常,對方發送正常
第三次握手:Client 確認了:自己發送、接收正常,對方發送、接收正常;Server 確認了:自己發送、接收正常,對方發送接收正常
所以三次握手就能確認雙發收發功能都正常,缺一不可。
<br>
### 為什么要三次握手
舉例:已失效的連接請求報文段。
? ?client發送了第一個連接的請求報文,但是由于網絡不好,這個請求沒有立即到達服務端,而是在某個網絡節點中滯留了,直到某個時間才到達server,本來這已經是一個失效
的報文,但是server端接收到這個請求報文后,還是會想client發出確認的報文,表示同意連接。假如不采用三次握手,那么只要server發出確認,新的建立就連接了,但其實這個
請求是失效的請求,client是不會理睬server的確認信息,也不會向服務端發送確認的請求,但是server認為新的連接已經建立起來了,并一直等待client發來數據,這樣,server的
很多資源就沒白白浪費掉了,采用三次握手就是為了防止這種情況的發生,server會因為收不到確認的報文,就知道client并沒有建立連接。這就是三次握手的作用。
## 斷開鏈接四次握手

TCP 是全雙工的,在斷開連接時兩端都需要發送 FIN 和 ACK。
**第一次握手**
TCP發送一個 **FIN(結束)**,用來關閉客戶到服務端的連接。
? ? 客戶端進程發出連接釋放報文,并且停止發送數據。釋放數據報文首部,**FIN=1**,其序列號為**seq=u**(等于前面已經傳送過來的數據的最后一個字節的序號加1),
此時,客戶端進入FIN-WAIT-1(終止等待1)狀態。 TCP規定,FIN報文段即使不攜帶數據,也要消耗一個序號。
<br>
<br>
**第二次握手**
服務端收到這個FIN,他發回一個**ACK(確認)**,確認收到序號為收到序號+1,和SYN一樣,一個FIN將占用一個序號。
<br>
? ? 服務器收到連接釋放報文,發出確認報文,**ACK=1,ack=u+1**,并且帶上自己的序列號 **seq=v**,此時,服務端就進入了CLOSE-WAIT(關閉等待)狀態。TCP服務器
通知高層的應用進程,客戶端向服務器的方向就釋放了,**這時候處于半關閉狀態,即客戶端已經沒有數據要發送了,但是服務器若發送數據,客戶端依然要接受**。這個狀態還要持續一段時間,也就是整個CLOSE-WAIT狀態持續的時間。
<br>
客戶端收到服務器的確認請求后,此時,客戶端就進入FIN-WAIT-2(終止等待2)狀態,等待服務器發送連接釋放報文(在這之前還需要接受服務器發送的最后的數據)。
<br>
<br>
**第三次握手**
服務端發送一個 **FIN(結束)** 到客戶端,服務端關閉客戶端的連接。
? ? ?服務器將最后的數據發送完畢后,就向客戶端發送連接釋放報文,FIN=1,ack=u+1,由于在半關閉狀態,服務器很可能又發送了一些數據,假定此時的序列號為seq=w,此時,服務器就進入了LAST-ACK(最后確認)狀態,等待客戶端的確認。
<br>
<br>
**第四次握手**
?客戶端發送 **ACK(確認)** 報文確認,并將確認的序號+1,這樣關閉完成。
客戶端收到服務器的連接釋放報文后,必須發出確認,**ACK=1,ack=w+1**,而自己的序列號是 seq=u+1,此時,客戶端就進入了 TIME-WAIT(時間等待)狀態。注意此時 TCP 連接還沒有釋放,必須經過2?MSL(最長報文段壽命)的時間后,當客戶端撤銷相應的TCB后,才進入CLOSED狀態。
服務器只要收到了客戶端發出的確認,立即進入CLOSED狀態。同樣,撤銷TCB后,就結束了這次的TCP連接。可以看到,服務器結束TCP連接的時間要比客戶端早一些。
**為什么 A 要進入 TIME-WAIT 狀態,等待 2MSL 時間后才進入 CLOSED 狀態?**
為了保證 B 能收到 A 的確認應答。若 A 發完確認應答后直接進入 CLOSED 狀態,如果確認應答因為網絡問題一直沒有到達,那么會造成 B 不能正常關閉。
### 為什么要四次揮手
**為了確保數據能夠完成傳輸。**
關閉連接時,當收到對方的FIN報文通知時,它僅僅表示對方沒有數據發送給你了;但未必你所有的數據都全部發送給對方了,所以你可以未必會馬上會關閉SOCKET,也即你可能還需要發送一些數據給對方之后,再發送FIN報文給對方來表示你同意現在可以關閉連接了,所以它這里的ACK報文和FIN報文多數情況下都是分開發送的。可能有人會有疑問,tcp我握手的時候為何ACK(確認)和SYN(建立連接)是一起發送。揮手的時候為什么是分開的時候發送呢.
<br>
因為當 Server 端收到 Client 端的 SYN 連接請求報文后,可以直接發送 SYN+ACK 報文。其中 ACK 報文是用來應答的,SYN 報文是用來同步的。但是關閉連接時,當 Server 端收到 FIN 報文時,很可能并不會立即關閉 SOCKET,所以只能先回復一個 ACK 報文,告訴 Client 端,"你發的FIN報文我收到了"。只有等到我 Server 端所有的報文都發送完了,我才能發送 FIN 報文,因此不能一起發送。故需要四步握手。
<br>
<br>
# TCP 協議如何保證可靠傳輸
1. 應用數據被分割成 TCP 認為最適合發送的數據塊。
2. TCP 給發送的每一個包進行編號,接收方對數據包進行排序,把有序數據傳送給應用層。
3. **校驗和:** TCP 將保持它首部和數據的檢驗和。這是一個端到端的檢驗和,目的是檢測數據在傳輸過程中的任何變化。如果收到段的檢驗和有差錯,TCP 將丟棄這個報文段和不確認收到此報文段。
4. TCP 的接收端會丟棄重復的數據。
5. **流量控制:** TCP 連接的每一方都有固定大小的緩沖空間,TCP的接收端只允許發送端發送接收端緩沖區能接納的數據。當接收方來不及處理發送方的數據,能提示發送方降低發送的速率,防止包丟失。TCP 使用的流量控制協議是可變大小的滑動窗口協議。 (TCP 利用滑動窗口實現流量控制)
6. **擁塞控制:** 當網絡擁塞時,減少數據的發送。
7. **停止等待協議** 也是為了實現可靠傳輸的,它的基本原理就是每發完一個分組就停止發送,等待對方確認。在收到確認后再發下一個分組。 **超時重傳:** 當 TCP 發出一個段后,它啟動一個定時器,等待目的端確認收到這個報文段。如果不能及時收到一個確認,將重發這個報文段。
<br>
## 停止等待協議
* 停止等待協議是為了實現可靠傳輸的,它的基本原理就是每發完一個分組就停止發送,等待對方確認。在收到確認后再發下一個分組;
* 在停止等待協議中,若接收方收到重復分組,就丟棄該分組,但同時還要發送確認;
**1) 無差錯情況:**

發送方發送分組,接收方在規定時間內收到,并且回復確認.發送方再次發送。
**2) 出現差錯情況(超時重傳):**

停止等待協議中超時重傳是指只要超過一段時間仍然沒有收到確認,就重傳前面發送過的分組(認為剛才發送過的分組丟失了)。因此每發送完一個分組需要設置一個超時計時器,其重轉時間應比數據在分組傳輸的平均往返時間更長一些。這種自動重傳方式常稱為 **自動重傳請求 ARQ** 。另外在停止等待協議中若收到重復分組,就丟棄該分組,但同時還要發送確認。**連續 ARQ 協議** 可提高信道利用率。發送維持一個發送窗口,凡位于發送窗口內的分組可連續發送出去,而不需要等待對方確認。接收方一般采用累積確認,對按序到達的最后一個分組發送確認,表明到這個分組位置的所有分組都已經正確收到了。
**3) 確認丟失和確認遲到**
* **確認丟失**:確認消息在傳輸過程丟失

當A發送M1消息,B收到后,B向A發送了一個M1確認消息,但卻在傳輸過程中丟失。而A并不知道,在超時計時過后,A重傳M1消息,B再次收到該消息后采取以下兩點措施:
1. 丟棄這個重復的M1消息,不向上層交付。
2. 向A發送確認消息。(不會認為已經發送過了,就不再發送。A能重傳,就證明B的確認消息丟失)。
* **確認遲到** :確認消息在傳輸過程中遲到

A發送M1消息,B收到并發送確認。在超時時間內沒有收到確認消息,A重傳M1消息,B仍然收到并繼續發送確認消息(B收到了2份M1)。此時A收到了B第二次發送的確認消息。接著發送其他數據。過了一會,A收到了B第一次發送的對M1的確認消息(A也收到了2份確認消息)。處理如下:
1. A收到重復的確認后,直接丟棄。
2. B收到重復的M1后,也直接丟棄重復的M1。
## 自動重傳請求 ARQ 協議
停止等待協議中超時重傳是指只要超過一段時間仍然沒有收到確認,就重傳前面發送過的分組(認為剛才發送過的分組丟失了)。因此每發送完一個分組需要設置一個超時計時器,其重轉時間應比數據在分組傳輸的平均往返時間更長一些。這種自動重傳方式常稱為自動重傳請求ARQ。
**優點:** 簡單
**缺點:** 信道利用率低
## 連續ARQ協議
連續 ARQ 協議可提高信道利用率。發送方維持一個發送窗口,凡位于發送窗口內的分組可以連續發送出去,而不需要等待對方確認。接收方一般采用累計確認,對按序到達的最后一個分組發送確認,表明到這個分組為止的所有分組都已經正確收到了。
**優點:** 信道利用率高,容易實現,即使確認丟失,也不必重傳。
**缺點:** 不能向發送方反映出接收方已經正確收到的所有分組的信息。 比如:發送方發送了 5條 消息,中間第三條丟失(3號),這時接收方只能對前兩個發送確認。發送方無法知道后三個分組的下落,而只好把后三個全部重傳一次。這也叫 Go-Back-N(回退 N),表示需要退回來重傳已經發送過的 N 個消息。
## 滑動窗口
* TCP 利用滑動窗口實現流量控制的機制。
* 滑動窗口(Sliding window)是一種流量控制技術。早期的網絡通信中,通信雙方不會考慮網絡的擁擠情況直接發送數據。由于大家不知道網絡擁塞狀況,同時發送數據,導致中間節點阻塞掉包,誰也發不了數據,所以就有了滑動窗口機制來解決此問題。
* TCP 中采用滑動窗口來進行傳輸控制,滑動窗口的大小意味著接收方還有多大的緩沖區可以用于接收數據。發送方可以通過滑動窗口的大小來確定應該發送多少字節的數據。當滑動窗口為 0 時,發送方一般不能再發送數據報,但有兩種情況除外,一種情況是可以發送緊急數據,例如,允許用戶終止在遠端機上的運行進程。另一種情況是發送方可以發送一個 1 字節的數據報來通知接收方重新聲明它希望接收的下一字節及發送方的滑動窗口大小。
## 流量控制
* TCP 利用滑動窗口實現流量控制。
* 流量控制是為了控制發送方發送速率,保證接收方來得及接收。
* 接收方發送的確認報文中的窗口字段可以用來控制發送方窗口大小,從而影響發送方的發送速率。將窗口字段設置為 0,則發送方不能發送數據。
## 擁塞控制
在某段時間,若對網絡中某一資源的需求超過了該資源所能提供的可用部分,網絡的性能就要變壞。這種情況就叫擁塞。擁塞控制就是為了防止過多的數據注入到網絡中,這樣就可以使網絡中的路由器或鏈路不致過載。擁塞控制所要做的都有一個前提,就是網絡能夠承受現有的網絡負荷。擁塞控制是一個全局性的過程,涉及到所有的主機,所有的路由器,以及與降低網絡傳輸性能有關的所有因素。相反,流量控制往往是點對點通信量的控制,是個端到端的問題。流量控制所要做到的就是抑制發送端發送數據的速率,以便使接收端來得及接收。
為了進行擁塞控制,TCP 發送方要維持一個 **擁塞窗口(cwnd)** 的狀態變量。擁塞控制窗口的大小取決于網絡的擁塞程度,并且動態變化。發送方讓自己的發送窗口取為擁塞窗口和接收方的接受窗口中較小的一個。
TCP的擁塞控制采用了四種算法,即 **慢開始** 、 **擁塞避免** 、**快重傳** 和 **快恢復**。在網絡層也可以使路由器采用適當的分組丟棄策略(如主動隊列管理 AQM),以減少網絡擁塞的發生。
* **慢開始:** 慢開始算法的思路是當主機開始發送數據時,如果立即把大量數據字節注入到網絡,那么可能會引起網絡阻塞,因為現在還不知道網絡的符合情況。經驗表明,較好的方法是先探測一下,即由小到大逐漸增大發送窗口,也就是由小到大逐漸增大擁塞窗口數值。cwnd初始值為1,每經過一個傳播輪次,cwnd加倍。

* **擁塞避免:** 擁塞避免算法的思路是讓擁塞窗口cwnd緩慢增大,即每經過一個往返時間RTT就把發送放的cwnd加1.
* **快重傳與快恢復:** 在 TCP/IP 中,快速重傳和恢復(fast retransmit and recovery,FRR)是一種擁塞控制算法,它能快速恢復丟失的數據包。沒有 FRR,如果數據包丟失了,TCP 將會使用定時器來要求傳輸暫停。在暫停的這段時間內,沒有新的或復制的數據包被發送。有了 FRR,如果接收機接收到一個不按順序的數據段,它會立即給發送機發送一個重復確認。如果發送機接收到三個重復確認,它會假定確認件指出的數據段丟失了,并立即重傳這些丟失的數據段。有了 FRR,就不會因為重傳時要求的暫停被耽誤。 ?當有單獨的數據包丟失時,快速重傳和恢復(FRR)能最有效地工作。當有多個數據信息包在某一段很短的時間內丟失時,它則不能很有效地工作。
# 參考資料
[前端面試之道 - 掘金小冊](https://juejin.im/book/5bdc715fe51d454e755f75ef/section/5bdc729af265da615a414603)
[搞定計算機網絡面試,看這篇就夠了(補充版)](https://juejin.im/post/5b7be0b2e51d4538db34a51e#heading-18)
[【TCP協議】(2)---TCP三次握手和四次揮手](https://www.cnblogs.com/qdhxhz/p/8470997.html)
- 第一部分 HTML
- meta
- meta標簽
- HTML5
- 2.1 語義
- 2.2 通信
- 2.3 離線&存儲
- 2.4 多媒體
- 2.5 3D,圖像&效果
- 2.6 性能&集成
- 2.7 設備訪問
- SEO
- Canvas
- 壓縮圖片
- 制作圓角矩形
- 全局屬性
- 第二部分 CSS
- CSS原理
- 層疊上下文(stacking context)
- 外邊距合并
- 塊狀格式化上下文(BFC)
- 盒模型
- important
- 樣式繼承
- 層疊
- 屬性值處理流程
- 分辨率
- 視口
- CSS API
- grid(未完成)
- flex
- 選擇器
- 3D
- Matrix
- AT規則
- line-height 和 vertical-align
- CSS技術
- 居中
- 響應式布局
- 兼容性
- 移動端適配方案
- CSS應用
- CSS Modules(未完成)
- 分層
- 面向對象CSS(未完成)
- 布局
- 三列布局
- 單列等寬,其他多列自適應均勻
- 多列等高
- 圣杯布局
- 雙飛翼布局
- 瀑布流
- 1px問題
- 適配iPhoneX
- 橫屏適配
- 圖片模糊問題
- stylelint
- 第三部分 JavaScript
- JavaScript原理
- 內存空間
- 作用域
- 執行上下文棧
- 變量對象
- 作用域鏈
- this
- 類型轉換
- 閉包(未完成)
- 原型、面向對象
- class和extend
- 繼承
- new
- DOM
- Event Loop
- 垃圾回收機制
- 內存泄漏
- 數值存儲
- 連等賦值
- 基本類型
- 堆棧溢出
- JavaScriptAPI
- document.referrer
- Promise(未完成)
- Object.create
- 遍歷對象屬性
- 寬度、高度
- performance
- 位運算
- tostring( ) 與 valueOf( )方法
- JavaScript技術
- 錯誤
- 異常處理
- 存儲
- Cookie與Session
- ES6(未完成)
- Babel轉碼
- let和const命令
- 變量的解構賦值
- 字符串的擴展
- 正則的擴展
- 數值的擴展
- 數組的擴展
- 函數的擴展
- 對象的擴展
- Symbol
- Set 和 Map 數據結構
- proxy
- Reflect
- module
- AJAX
- ES5
- 嚴格模式
- JSON
- 數組方法
- 對象方法
- 函數方法
- 服務端推送(未完成)
- JavaScript應用
- 復雜判斷
- 3D 全景圖
- 重載
- 上傳(未完成)
- 上傳方式
- 文件格式
- 渲染大量數據
- 圖片裁剪
- 斐波那契數列
- 編碼
- 數組去重
- 淺拷貝、深拷貝
- instanceof
- 模擬 new
- 防抖
- 節流
- 數組扁平化
- sleep函數
- 模擬bind
- 柯里化
- 零碎知識點
- 第四部分 進階
- 計算機原理
- 數據結構(未完成)
- 算法(未完成)
- 排序算法
- 冒泡排序
- 選擇排序
- 插入排序
- 快速排序
- 搜索算法
- 動態規劃
- 二叉樹
- 瀏覽器
- 瀏覽器結構
- 瀏覽器工作原理
- HTML解析
- CSS解析
- 渲染樹構建
- 布局(Layout)
- 渲染
- 瀏覽器輸入 URL 后發生了什么
- 跨域
- 緩存機制
- reflow(回流)和repaint(重繪)
- 渲染層合并
- 編譯(未完成)
- Babel
- 設計模式(未完成)
- 函數式編程(未完成)
- 正則表達式(未完成)
- 性能
- 性能分析
- 性能指標
- 首屏加載
- 優化
- 瀏覽器層面
- HTTP層面
- 代碼層面
- 構建層面
- 移動端首屏優化
- 服務器層面
- bigpipe
- 構建工具
- Gulp
- webpack
- Webpack概念
- Webpack工具
- Webpack優化
- Webpack原理
- 實現loader
- 實現plugin
- tapable
- Webpack打包后代碼
- rollup.js
- parcel
- 模塊化
- ESM
- 安全
- XSS
- CSRF
- 點擊劫持
- 中間人攻擊
- 密碼存儲
- 測試(未完成)
- 單元測試
- E2E測試
- 框架測試
- 樣式回歸測試
- 異步測試
- 自動化測試
- PWA
- PWA官網
- web app manifest
- service worker
- app install banners
- 調試PWA
- PWA教程
- 框架
- MVVM原理
- Vue
- Vue 餓了么整理
- 樣式
- 技巧
- Vue音樂播放器
- Vue源碼
- Virtual Dom
- computed原理
- 數組綁定原理
- 雙向綁定
- nextTick
- keep-alive
- 導航守衛
- 組件通信
- React
- Diff 算法
- Fiber 原理
- batchUpdate
- React 生命周期
- Redux
- 動畫(未完成)
- 異常監控、收集(未完成)
- 數據采集
- Sentry
- 貝塞爾曲線
- 視頻
- 服務端渲染
- 服務端渲染的利與弊
- Vue SSR
- React SSR
- 客戶端
- 離線包
- 第五部分 網絡
- 五層協議
- TCP
- UDP
- HTTP
- 方法
- 首部
- 狀態碼
- 持久連接
- TLS
- content-type
- Redirect
- CSP
- 請求流程
- HTTP/2 及 HTTP/3
- CDN
- DNS
- HTTPDNS
- 第六部分 服務端
- Linux
- Linux命令
- 權限
- XAMPP
- Node.js
- 安裝
- Node模塊化
- 設置環境變量
- Node的event loop
- 進程
- 全局對象
- 異步IO與事件驅動
- 文件系統
- Node錯誤處理
- koa
- koa-compose
- koa-router
- Nginx
- Nginx配置文件
- 代理服務
- 負載均衡
- 獲取用戶IP
- 解決跨域
- 適配PC與移動環境
- 簡單的訪問限制
- 頁面內容修改
- 圖片處理
- 合并請求
- PM2
- MongoDB
- MySQL
- 常用MySql命令
- 自動化(未完成)
- docker
- 創建CLI
- 持續集成
- 持續交付
- 持續部署
- Jenkins
- 部署與發布
- 遠程登錄服務器
- 增強服務器安全等級
- 搭建 Nodejs 生產環境
- 配置 Nginx 實現反向代理
- 管理域名解析
- 配置 PM2 一鍵部署
- 發布上線
- 部署HTTPS
- Node 應用
- 爬蟲(未完成)
- 例子
- 反爬蟲
- 中間件
- body-parser
- connect-redis
- cookie-parser
- cors
- csurf
- express-session
- helmet
- ioredis
- log4js(未完成)
- uuid
- errorhandler
- nodeclub源碼
- app.js
- config.js
- 消息隊列
- RPC
- 性能優化
- 第七部分 總結
- Web服務器
- 目錄結構
- 依賴
- 功能
- 代碼片段
- 整理
- 知識清單、博客
- 項目、組件、庫
- Node代碼
- 面試必考
- 91算法
- 第八部分 工作代碼總結
- 樣式代碼
- 框架代碼
- 組件代碼
- 功能代碼
- 通用代碼