深入浅出TCP/UDP 原理-TCP篇(1)及完整MATLAB实现UDP通信
源代碼在這一篇文章里:
https://mp.csdn.net/editor/html/117086464
目錄
疑癥 1 :TCP 的三次握手、四次揮手
疑癥 2 : TCP 連接的初始化序列號能否固定
疑癥 3 : 初始化連接的 SYN 超時問題
疑癥 4 : TCP 的 Peer 兩端同時斷開連接
疑癥 5 : 四次揮手能不能變成三次揮手呢?
6. 疑癥 6 : TCP 的頭號疼癥 TIME_WAIT 狀態
原文鏈接:
https://cloud.tencent.com/developer/article/1004327
點擊查看更多通信與專業知識
說到TCP協議,相信大家都比較熟悉了,對于TCP協議總能說個一二三來,但是TCP協議又是一個非常復雜的協議,其中有不少細節點讓人頭疼點。本文就是來說說這些頭疼點的,淺談一些TCP的疑難雜癥。那么從哪說起呢?當然是從三次握手和四次揮手說起啦,可能大家都知道TCP是三次交互完成連接的建立,四次交互來斷開一個連接,那為什么是三次握手和四次揮手呢?反過來不行嗎?
?
疑癥 1 :TCP 的三次握手、四次揮手
?
下面兩圖大家再熟悉不過了,TCP的三次握手和四次揮手見下面左邊的”TCP建立連接”、”TCP數據傳送”、”TCP斷開連接”時序圖和右邊的”TCP協議狀態機”
?
TCP三次握手、四次揮手時序圖
?
TCP協議狀態機
?
要弄清TCP建立連接需要幾次交互才行,我們需要弄清建立連接進行初始化的目標是什么。TCP進行握手初始化一個連接的目標是:分配資源、初始化序列號(通知peer對端我的初始序列號是多少),知道初始化連接的目標,那么要達成這個目標的過程就簡單了,握手過程可以簡化為下面的四次交互:
?
1 ) clien 端首先發送一個 SYN 包告訴 Server 端我的初始序列號是 X。
2 ) Server 端收到 SYN 包后回復給 client 一個 ACK 確認包,告訴 client 說我收到了。
3 ) 接著 Server 端也需要告訴 client 端自己的初始序列號,于是 Server 也發送一個 SYN 包告訴 client 我的初始序列號是Y。
4 ) Client 收到后,回復 Server 一個 ACK 確認包說我知道了。
?
整個過程4次交互即可完成初始化,但是,細心的同學會發現兩個問題:
?
1. Server發送SYN包是作為發起連接的SYN包,還是作為響應發起者的SYN包呢?怎么區分?比較容易引起混淆
?
2.Server的ACK確認包和接下來的SYN包可以合成一個SYN ACK包一起發送的,沒必要分別單獨發送,這樣省了一次交互同時也解決了問題1. 這樣TCP建立一個連接,三次握手在進行最少次交互的情況下完成了Peer兩端的資源分配和初始化序列號的交換。
?
大部分情況下建立連接需要三次握手,也不一定都是三次,有可能出現四次握手來建立連接的。如下圖,當Peer兩端同時發起SYN來建立連接的時候,就出現了四次握手來建立連接(對于有些TCP/IP的實現,可能不支持這種同時打開的情況)。
?
在三次握手過程中,細心的同學可能會有以下疑問:
?
(2). 初始化序列號X、Y是可以是寫死固定的嗎,為什么不能呢?
?
(3). 假如Client發送一個SYN包給Server后就掛了或是不管了,這個時候這個連接處于什么狀態呢?會超時嗎?為什么呢?
?
TCP進行斷開連接的目標是:回收資源、終止數據傳輸。由于TCP是全雙工的,需要Peer兩端分別各自拆除自己通向Peer對端的方向的通信信道。這樣需要四次揮手來分別拆除通信信道,就比較清晰明了了。
?
1)Client 發送一個FIN包來告訴 Server 我已經沒數據需要發給 Server了。
2)Server 收到后回復一個 ACK 確認包說我知道了。
3)然后 server 在自己也沒數據發送給client后,Server 也發送一個 FIN 包給 Client 告訴 Client 我也已經沒數據發給client 了。
4)Client 收到后,就會回復一個 ACK 確認包說我知道了。
?
到此,四次揮手,這個TCP連接就可以完全拆除了。在四次揮手的過程中,細心的同學可能會有以下疑問:
?
(4). Client和Server同時發起斷開連接的FIN包會怎么樣呢,TCP狀態是怎么轉移的?
?
(5). 左側圖中的四次揮手過程中,Server端的ACK確認包能不能和接下來的FIN包合并成一個包呢,這樣四次揮手就變成三次揮手了。
?
(6). 四次揮手過程中,首先斷開連接的一端,在回復最后一個ACK后,為什么要進行TIME_WAIT呢(超時設置是 2*MSL,RFC793定義了MSL為2分鐘,Linux設置成了30s),在TIME_WAIT的時候又不能釋放資源,白白讓資源占用那么長時間,能不能省了TIME_WAIT呢,為什么?
?
疑癥 2 : TCP 連接的初始化序列號能否固定
?
如果初始化序列號(縮寫為ISN:Inital Sequence Number)可以固定,我們來看看會出現什么問題。假設ISN固定是1,Client和Server建立好一條TCP連接后,Client連續給Server發了10個包,這10個包不知怎么被鏈路上的路由器緩存了(路由器會毫無先兆地緩存或者丟棄任何的數據包),這個時候碰巧Client掛掉了,然后Client用同樣的端口號重新連上Server,Client又連續給Server發了幾個包,假設這個時候Client的序列號變成了5。接著,之前被路由器緩存的10個數據包全部被路由到Server端了,Server給Client回復確認號10,這個時候,Client整個都不好了,這是什么情況?我的序列號才到5,你怎么給我的確認號是10了,整個都亂了。
?
RFC793中,建議ISN和一個假的時鐘綁在一起,這個時鐘會在每4微秒對ISN做加一操作,直到超過2^32,又從0開始,這需要4小時才會產生ISN的回繞問題,這幾乎可以保證每個新連接的ISN不會和舊的連接的ISN產生沖突。這種遞增方式的ISN,很容易讓攻擊者猜測到TCP連接的ISN,現在的實現大多是在一個基準值的基礎上進行隨機的。
?
疑癥 3 : 初始化連接的 SYN 超時問題
Client發送SYN包給Server后掛了,Server回給Client的SYN-ACK一直沒收到Client的ACK確認,這個時候這個連接既沒建立起來,也不能算失敗。這就需要一個超時時間讓Server將這個連接斷開,否則這個連接就會一直占用Server的SYN連接隊列中的一個位置,大量這樣的連接就會將Server的SYN連接隊列耗盡,讓正常的連接無法得到處理。
?
目前,Linux下默認會進行5次重發SYN-ACK包,重試的間隔時間從1s開始,下次的重試間隔時間是前一次的雙倍,5次的重試時間間隔為1s, 2s, 4s, 8s, 16s,總共31s,第5次發出后還要等32s都知道第5次也超時了,所以,總共需要 1s + 2s + 4s+ 8s+ 16s + 32s = 63s,TCP才會把斷開這個連接。由于,SYN超時需要63秒,那么就給攻擊者一個攻擊服務器的機會,攻擊者在短時間內發送大量的SYN包給Server(俗稱 SYN flood 攻擊),用于耗盡Server的SYN隊列。對于應對SYN 過多的問題,linux提供了幾個TCP參數:tcp_syncookies、tcp_synack_retries、tcp_max_syn_backlog、tcp_abort_on_overflow 來調整應對。
?
疑癥 4 : TCP 的 Peer 兩端同時斷開連接
?
由上面的”TCP協議狀態機 “圖可以看出,TCP的Peer端在收到對端的FIN包前發出了FIN包,那么該Peer的狀態就變成了FIN_WAIT1,Peer在FIN_WAIT1狀態下收到對端Peer對自己FIN包的ACK包的話,那么Peer狀態就變成FIN_WAIT2,Peer在FIN_WAIT2下收到對端Peer的FIN包,在確認已經收到了對端Peer全部的Data數據包后,就響應一個ACK給對端Peer,然后自己進入TIME_WAIT狀態。
?
但是如果Peer在FIN_WAIT1狀態下首先收到對端Peer的FIN包的話,那么該Peer在確認已經收到了對端Peer全部的Data數據包后,就響應一個ACK給對端Peer,然后自己進入CLOSEING狀態,Peer在CLOSEING狀態下收到自己的FIN包的ACK包的話,那么就進入TIME WAIT 狀態。于是,TCP的Peer兩端同時發起FIN包進行斷開連接,那么兩端Peer可能出現完全一樣的狀態轉移 FIN_WAIT1——>CLOSEING——->TIME_WAIT,也就會Client和Server最后同時進入TIME_WAIT狀態。
?
同時關閉連接的狀態轉移如下圖所示:
?
疑癥 5 : 四次揮手能不能變成三次揮手呢?
?
答案是可能的。
?
TCP是全雙工通信,Cliet在自己已經不會在有新的數據要發送給Server后,可以發送FIN信號告知Server,這邊已經終止Client到對端Server那邊的數據傳輸。但是,這個時候對端Server可以繼續往Client這邊發送數據包。于是,兩端數據傳輸的終止在時序上是獨立并且可能會相隔比較長的時間,這個時候就必須最少需要2+2 = 4 次揮手來完全終止這個連接。但是,如果Server在收到Client的FIN包后,在也沒數據需要發送給Client了,那么對Client的ACK包和Server自己的FIN包就可以合并成為一個包發送過去,這樣四次揮手就可以變成三次了(似乎linux協議棧就是這樣實現的)
?
6. 疑癥 6 : TCP 的頭號疼癥 TIME_WAIT 狀態
?
要說明TIME_WAIT的問題,需要解答以下幾個問題:
?
- Peer兩端,哪一端會進入TIME_WAIT呢?為什么?相信大家都知道,TCP主動關閉連接的那一方會最后進入TIME_WAIT。那么怎么界定主動關閉方呢?是否主動關閉是由FIN包的先后決定的,就是在自己沒收到對端Peer的FIN包之前自己發出了FIN包,那么自己就是主動關閉連接的那一方。對于疑癥(4) 中描述的情況,那么Peer兩邊都是主動關閉的一方,兩邊都會進入TIME_WAIT。為什么是主動關閉的一方進行TIME_WAIT呢,被動關閉的進入TIME_WAIT可以不呢?我們來看看TCP四次揮手可以簡單分為下面三個過程過程一.主動關閉方發送FIN; 過程二.被動關閉方收到主動關閉方的FIN后發送該FIN的ACK,被動關閉方發送FIN; 過程三.主動關閉方收到被動關閉方的FIN后發送該FIN的ACK,被動關閉方等待自己FIN的ACK問題就在過程三中,據TCP協議規范,不對ACK進行ACK,如果主動關閉方不進入TIME_WAIT,那么主動關閉方在發送完ACK就走了的話,如果最后發送的ACK在路由過程中丟掉了,最后沒能到被動關閉方,這個時候被動關閉方沒收到自己FIN的ACK就不能關閉連接,接著被動關閉方會超時重發FIN包,但是這個時候已經沒有對端會給該FIN回ACK,被動關閉方就無法正常關閉連接了,所以主動關閉方需要進入TIME_WAIT以便能夠重發丟掉的被動關閉方FIN的ACK。
- TIME_WAIT狀態是用來解決或避免什么問題呢?
?
TIME_WAIT主要是用來解決以下幾個問題:
?
1)上面解釋為什么主動關閉方需要進入TIME_WAIT狀態中提到的: 主動關閉方需要進入TIME_WAIT以便能夠重發丟掉的被動關閉方FIN包的ACK。如果主動關閉方不進入TIME_WAIT,那么在主動關閉方對被動關閉方FIN包的ACK丟失了的時候,被動關閉方由于沒收到自己FIN的ACK,會進行重傳FIN包,這個FIN包到主動關閉方后,由于這個連接已經不存在于主動關閉方了,這個時候主動關閉方無法識別這個FIN包,協議棧會認為對方瘋了,都還沒建立連接你給我來個FIN包?,于是回復一個RST包給被動關閉方,被動關閉方就會收到一個錯誤(我們見的比較多的:connect reset by peer,這里順便說下 Broken pipe,在收到RST包的時候,還往這個連接寫數據,就會收到 Broken pipe錯誤了),原本應該正常關閉的連接,給我來個錯誤,很難讓人接受。2)防止已經斷開的連接1中在鏈路中殘留的FIN包終止掉新的連接2(重用了連接1的所有的5元素(源IP,目的IP,TCP,源端口,目的端口)),這個概率比較低,因為涉及到一個匹配問題,遲到的FIN分段的序列號必須落在連接2的一方的期望序列號范圍之內,雖然概率低,但是確實可能發生,因為初始序列號都是隨機產生的,并且這個序列號是32位的,會回繞。3)防止鏈路上已經關閉的連接的殘余數據包(a lost duplicate packet or a wandering duplicate packet) 干擾正常的數據包,造成數據流的不正常。這個問題和2)類似。
- TIME_WAIT會帶來哪些問題呢?
?
TIME_WAIT帶來的問題注意是源于:一個連接進入TIME_WAIT狀態后需要等待2*MSL(一般是1到4分鐘)那么長的時間才能斷開連接釋放連接占用的資源,會造成以下問題
?
1) 作為服務器,短時間內關閉了大量的Client連接,就會造成服務器上出現大量的TIME_WAIT連接,占據大量的tuple,嚴重消耗著服務器的資源。
?
2) 作為客戶端,短時間內大量的短連接,會大量消耗的Client機器的端口,畢竟端口只有65535個,端口被耗盡了,后續就無法在發起新的連接了。
( 由于上面兩個問題,作為客戶端需要連本機的一個服務的時候,首選UNIX域套接字而不是TCP )
?
TIME_WAIT很令人頭疼,很多問題是由TIME_WAIT造成的,但是TIME_WAIT又不是多余的不能簡單將TIME_WAIT去掉,那么怎么來解決或緩解TIME_WAIT問題呢?可以進行TIME_WAIT的快速回收和重用來緩解TIME_WAIT的問題。有沒一些清掉TIME_WAIT的技巧呢?
總結
以上是生活随笔為你收集整理的深入浅出TCP/UDP 原理-TCP篇(1)及完整MATLAB实现UDP通信的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: vba代码编程800例_VBA编程常用过
- 下一篇: q87主板支持cpu型号_网络上那些30