## TCP報文格式

```
上圖中有幾個字段需要重點介紹下:
(1)序號:Seq序號,占32位,用來標識從TCP源端向目的端發送
的字節流,發起方發送數據時對此進行標記。
(2)確認序號:Ack序號,占32位,只有ACK標志位為1時,確認
序號字段才有效,Ack=Seq+1。
(3)標志位:共6個,即URG、ACK、PSH、RST、SYN、FIN
等,具體含義如下:
(A)URG:緊急指針(urgent pointer)有效。
(B)ACK:確認序號有效。
(C)PSH:接收方應該盡快將這個報文交給應用層。
(D)RST:重置連接。
(E)SYN:發起一個新連接。
(F)FIN:釋放一個連接。
需要注意的是:
(A)不要將確認序號Ack與標志位中的ACK搞混了。
(B)確認方Ack=發起方Req+1,兩端配對。
```

## 三次握手
所謂三次握手(Three-Way Handshake)即建立TCP連接,就是指建立一個TCP連接時,需要客戶端和服務端總共發送3個包以確認連接的建立。在socket編程中,這一過程由客戶端執行connect來觸發,整個流程如下圖所示:


```
(1)第一次握手:
Client將標志位SYN置為1,隨機產生一個值seq=J,并將該數據包
發送給Server,Client進入SYN_SEND狀態,等待Server確認。
(2)第二次握手:
Server收到數據包后由標志位SYN=1知道Client請求建立連接,
Server將標志位SYN和ACK都置為1,ack=J+1,隨機產生一個
值seq=K,并將該數據包發送給Client以確認連接請求,Server進
入SYN_RCVD狀態。
(3)第三次握手:
Client收到確認后,檢查ack是否為J+1,ACK是否為1,如果正確則
將標志位ACK置為1,ack=K+1,并將該數據包發送給Server,
Server檢查ack是否為K+1,ACK是否為1,如果正確則連接建立
成功,Client和Server進入ESTABLISHED(TCP連接成功)狀態,
完成三次握手,
隨后Client與Server之間可以開始傳輸數據了。
```
## SYN攻擊
```
在三次握手過程中,Server發送SYN-ACK之后,收到Client的ACK
之前的TCP連接稱為半連接(half-open connect),此時Server處
于SYN\_RCVD狀態,當收到ACK后,Server轉入ESTABLISHED
狀態。SYN攻擊就是Client在短時間內偽造大量不存在的IP地址,
并向Server不斷地發送SYN包,Server回復確認包,并等待Client
的確認,由于源地址是不存在的,因此,Server需要不斷重發直
至超時,這些偽造的SYN包將產時間占用未連接隊列,導致正常
的SYN請求因為隊列滿而被丟棄,從而引起網絡堵塞甚至系統癱
瘓。SYN攻擊時一種典型的DDOS攻擊,檢測SYN攻擊的方式非
常簡單,即當Server上有大量半連接狀態且源IP地址是隨機的,
則可以斷定遭到SYN攻擊了,使用如下命令可以讓之現行:
`netstat -nap | grep SYN\_RECV`
```
## 四次揮手

第一次揮手:客戶端發送一個FIN=M,用來關閉客戶端到服務器端的數據傳送,客戶端進入FIN\_WAIT\_1狀態。意思是說"我客戶端沒有數據要發給你了",但是如果你服務器端還有數據沒有發送完成,則不必急著關閉連接,可以繼續發送數據。
第二次揮手:服務器端收到FIN后,先發送ack=M+1,告訴客戶端,你的請求我收到了,但是我還沒準備好,請繼續你等我的消息。這個時候客戶端就進入FIN\_WAIT\_2 狀態,繼續等待服務器端的FIN報文。
第三次揮手:當服務器端確定數據已發送完成,則向客戶端發送FIN=N報文,告訴客戶端,好了,我這邊數據發完了,準備好關閉連接了。服務器端進入LAST\_ACK狀態。
第四次揮手:客戶端收到FIN=N報文后,就知道可以關閉連接了,但是他還是不相信網絡,怕服務器端不知道要關閉,所以發送ack=N+1后進入TIME\_WAIT狀態,如果Server端沒有收到ACK則可以重傳。服務器端收到ACK后,就知道可以斷開連接了。客戶端等待了2MSL后依然沒有收到回復,則證明服務器端已正常關閉,那好,我客戶端也可以關閉連接了。最終完成了四次握手。
?常見面試題
【問題1】為什么連接的時候是三次握手,關閉的時候卻是四次握手?
答:因為當Server端收到Client端的SYN連接請求報文后,可以直接發送SYN+ACK報文。其中ACK報文是用來應答的,SYN報文是用來同步的。但是關閉連接時,當Server端收到FIN報文時,很可能并不會立即關閉SOCKET,所以只能先回復一個ACK報文,告訴Client端,"你發的FIN報文我收到了"。只有等到我Server端所有的報文都發送完了,我才能發送FIN報文,因此不能一起發送。故需要四步握手。
【問題2】為什么TIME_WAIT狀態需要經過2MSL(最大報文段生存時間)才能返回到CLOSE狀態?
答:雖然按道理,四個報文都發送完畢,我們可以直接進入CLOSE狀態了,但是我們必須假象網絡是不可靠的,有可以最后一個ACK丟失。所以TIME_WAIT狀態就是用來重發可能丟失的ACK報文。在Client發送出最后的ACK回復,但該ACK可能丟失。Server如果沒有收到ACK,將不斷重復發送FIN片段。所以Client不能立即關閉,它必須確認Server接收到了該ACK。Client會在發送出ACK之后進入到TIME_WAIT狀態。Client會設置一個計時器,等待2MSL的時間。如果在該時間內再次收到FIN,那么Client會重發ACK并再次等待2MSL。所謂的2MSL是兩倍的MSL(Maximum Segment Lifetime)。MSL指一個片段在網絡中最大的存活時間,2MSL就是一個發送和一個回復所需的最大時間。如果直到2MSL,Client都沒有再次收到FIN,那么Client推斷ACK已經被成功接收,則結束TCP連接。
【問題3】為什么不能用兩次握手進行連接?
答:3次握手完成兩個重要的功能,既要雙方做好發送數據的準備工作(雙方都知道彼此已準備好),也要允許雙方就初始序列號進行協商,這個序列號在握手過程中被發送和確認。
???????現在把三次握手改成僅需要兩次握手,死鎖是可能發生的。作為例子,考慮計算機S和C之間的通信,假定C給S發送一個連接請求分組,S收到了這個分組,并發 送了確認應答分組。按照兩次握手的協定,S認為連接已經成功地建立了,可以開始發送數據分組。可是,C在S的應答分組在傳輸中被丟失的情況下,將不知道S 是否已準備好,不知道S建立什么樣的序列號,C甚至懷疑S是否收到自己的連接請求分組。在這種情況下,C認為連接還未建立成功,將忽略S發來的任何數據分 組,只等待連接確認應答分組。而S在發出的分組超時后,重復發送同樣的分組。這樣就形成了死鎖。
【問題4】如果已經建立了連接,但是客戶端突然出現故障了怎么辦?
TCP還設有一個保活計時器,顯然,客戶端如果出現故障,服務器不能一直等下去,白白浪費資源。服務器每收到一次客戶端的請求后都會重新復位這個計時器,時間通常是設置為2小時,若兩小時還沒有收到客戶端的任何數據,服務器就會發送一個探測報文段,以后每隔75秒鐘發送一次。若一連發送10個探測報文仍然沒反應,服務器就認為客戶端出了故障,接著就關閉連接。
- git-第一天
- Git-第二天
- git-第三天
- http-基礎
- HTTP構成和狀態碼
- 瀏覽器輸入URL,經歷的過程
- TCP/IP 詳解三次握手 四次揮手
- http-DNS系統
- http與https之間的區別
- HTTPS握手和HTTP握手
- HTTP小試牛刀
- Tcp初探
- TCP報文格式
- HTML5
- HTML基礎
- Mock
- css 選擇器
- css 動畫
- css 定位
- position/display/float/z-index第一課時
- 行內、塊、脫標 三種狀態下的元素如何實現、水平、垂直居中
- clientHeight/offsetHeight/scrollHeight
- js 數據類型
- 變量提升
- 堆棧關系