## TCP三次握手與四次揮手

### 三次握手
#### 為什么連接建立需要三次握手,而不是兩次握手?
**防止失效的[連接請求報文](https://www.zhihu.com/search?q=%E8%BF%9E%E6%8E%A5%E8%AF%B7%E6%B1%82%E6%8A%A5%E6%96%87&search_source=Entity&hybrid_search_source=Entity&hybrid_search_extra=%7B%22sourceType%22%3A%22answer%22%2C%22sourceId%22%3A254224088%7D)段被服務端接收,從而產生錯誤。**
PS:失效的連接請求:若客戶端向服務端發送的連接請求丟失,客戶端等待應答超時后就會再次發送連接請求,此時,上一個連接請求就是『失效的』。
若建立連接只需兩次握手,客戶端并沒有太大的變化,仍然需要獲得服務端的應答后才進入ESTABLISHED狀態,而服務端在收到連接請求后就進入ESTABLISHED狀態。此時如果網絡擁塞,客戶端發送的連接請求遲遲到不了服務端,客戶端便超時重發請求,如果服務端正確接收并確認應答,雙方便開始通信,通信結束后釋放連接。此時,如果那個失效的連接請求抵達了服務端,由于只有兩次握手,服務端收到請求就會進入ESTABLISHED狀態,等待發送數據或主動發送數據。但此時的客戶端早已進入CLOSED狀態,服務端將會一直等待下去,這樣浪費服務端連接資源。
### 四次揮手
- close_wait:服務端確認關閉后,還有數據傳輸的需要,此時服務端的狀態為`close_wait`;
## 什么是time_wait狀態
* TCP 連接中,**主動關閉連接**的一方出現的狀態;(收到 FIN 命令,進入 TIME\_WAIT 狀態,并返回 ACK 命令)
* 保持 2 個`MSL`時間,即,`4 分鐘`;(MSL 為 2 分鐘)
> 基于TCP建立連接時,都會占用一個本地端口。TCP 本地端口數量,上限為`65535`(6.5w),這是因為 TCP 頭部使用`16 bit`,存儲「**端口號**」,因此約束上限為`65535`。
### **time_wait 狀態**存在的`必要性`:
* **可靠的實現 TCP 全雙工連接的終止**:四次揮手關閉 TCP 連接過程中,最后的 ACK 是由「主動關閉連接」的一端發出的,如果這個 ACK 丟失,則,對方會重發 FIN 請求,因此,在「主動關閉連接」的一段,需要維護一個 time\_wait 狀態,處理對方重發的 FIN 請求;
* **處理延遲到達的報文**:由于路由器可能抖動,TCP 報文會延遲到達,為了避免「延遲到達的 TCP 報文」被誤認為是「新 TCP 連接」的數據,則,需要在允許新創建 TCP 連接之前,保持一個不可用的狀態,等待所有延遲報文的消失,一般設置為 2 倍的 MSL(報文的最大生存時間),解決「延遲達到的 TCP 報文」問題;
## 問題分析
> 大量的`TIME_WAIT`狀態 TCP 連接存在,其本質原因是什么?
* 大量的**短連接**存在
* 特別是 HTTP 請求中,如果`connection`頭部取值被設置為`close`時,基本都由「**服務端**」發起**主動關閉連接**
* 而`TCP 四次揮手`關閉連接機制中,為了保證`ACK 重發`和`丟棄延遲數據`,設置`time_wait`為 2 倍的`MSL`(報文最大存活時間)
### 大量time_wait有什么影響
Nginx 作為反向代理時,大量的短鏈接,可能導致 Nginx 上的 TCP 連接處于`time_wait`狀態:
* 每一個 time\_wait 狀態,都會占用一個「本地端口」,上限為`65535`(16 bit,2 Byte);
* 當大量的連接處于`time_wait`時,新建立 TCP 連接會出錯,**address already in use : connect**異常
### 解決辦法
解決上述`time_wait`狀態大量存在,導致新連接創建失敗的問題,一般解決辦法:
**客戶端**
HTTP 請求的頭部,connection 設置為 keep-alive,保持存活一段時間:現在的瀏覽器,一般都這么進行了。
**服務器端**
* 允許`time_wait`狀態的 socket 被**重用**
* 縮減`time_wait`時間,設置為`1 MSL`(即,2 mins)
## 總結
#### 1. **time\_wait 是「服務器端」的狀態?or 「客戶端」的狀態?**
1. RE:time\_wait 是「主動關閉 TCP 連接」一方的狀態,可能是「客服端」的,也可能是「服務器端」的
2. 一般情況下,都是「客戶端」所處的狀態;「服務器端」一般設置「不主動關閉連接」
#### 2. **服務器在對外服務時,是「客戶端」發起的斷開連接?還是「服務器」發起的斷開連接?**
1. 正常情況下,都是「客戶端」發起的斷開連接
2. 「服務器」一般設置為「不主動關閉連接」,服務器通常執行「被動關閉」
3. 但 HTTP 請求中,http 頭部 connection 參數,可能設置為 close,則,服務端處理完請求會主動關閉 TCP 連接
> 關于 Apache httpd 服務器的關聯配置,參考:[https://elf8848.iteye.com/blog/1739571](https://elf8848.iteye.com/blog/1739571)
#### **3. 關于 HTTP 請求中,設置的主動關閉 TCP 連接的機制:TIME\_WAIT的是主動斷開方才會出現的,所以主動斷開方是服務端?**
1. 答案是是的。在HTTP1.1協議中,有個 Connection 頭,Connection有兩個值,close和keep-alive,這個頭就相當于客戶端告訴服務端,服務端你執行完成請求之后,是關閉連接還是保持連接,保持連接就意味著在保持連接期間,只能由客戶端主動斷開連接。還有一個keep-alive的頭,設置的值就代表了服務端保持連接保持多久。
2. HTTP默認的Connection值為close,那么就意味著關閉請求的一方幾乎都會是由服務端這邊發起的。那么這個服務端產生TIME\_WAIT過多的情況就很正常了。
3. 雖然HTTP默認Connection值為close,但是,現在的瀏覽器發送請求的時候一般都會設置Connection為keep-alive了。所以,也有人說,現在沒有必要通過調整參數來使TIME\_WAIT降低了。
> 原文閱讀:https://www.cnblogs.com/soft-engineer/p/15175930.html
- 前言
- 第一部分 計算機網絡與操作系統
- 大量的 TIME_WAIT 狀態 TCP 連接,對業務有什么影響?怎么處理?
- 性能占用
- 第二部分 Java基礎
- 2-1 JVM
- JVM整體結構
- 方法區
- JVM的生命周期
- 堆對象結構
- 垃圾回收
- 調優案例
- 類加載機制
- 執行引擎
- 類文件結構
- 2-2 多線程
- 線程狀態
- 鎖與阻塞
- 悲觀鎖與樂觀鎖
- 阻塞隊列
- ConcurrentHashMap
- 線程池
- 線程框架
- 徹底搞懂AQS
- 2-3 Spring框架基礎
- Spring注解
- Spring IoC 和 AOP 的理解
- Spring工作原理
- 2-4 集合框架
- 死磕HashMap
- 第三部分 高級編程
- Socket與NIO
- 緩沖區
- Bybuffer
- BIO、NIO、AIO
- Netty的工作原理
- Netty高性能原因
- Rabbitmq
- mq消息可靠性是怎么保障的?
- 認證授權
- 第四部分 數據存儲
- 第1章 mysql篇
- MySQL主從一致性
- Mysql的數據組織方式
- Mysql性能優化
- 數據庫中的樂觀鎖與悲觀鎖
- 深度分頁
- 從一條SQL語句看Mysql的工作流程
- 第2章 Redis
- Redis緩存
- redis key過期策略
- 數據持久化
- 基于Redis分布式鎖的實現
- Redis高可用
- 第3章 Elasticsearch
- 全文查詢為什么快
- battle with mysql
- 第五部分 數據結構與算法
- 常見算法題
- 基于數組實現的一個隊列
- 第六部分 真實面試案例
- 初級開發面試材料
- 答案部分
- 現場編碼
- 第七部分 面試官角度
- 第八部分 計算機基礎
- 第九部分 微服務
- OpenFeign工作原理