小白都能看懂的tcp三次握手

格蘭芬多曹大仙發表於2022-03-04

       眾所周知,TCP在建立連線時需要經過三次握手。許多初學者經常對這個過程感到混亂:SYN是幹什麼的,怎麼一會兒是1一會兒是0?怎麼既有大寫的ACK又有小寫的ack?為什麼ACK在第二次握手才開始出現?初始序列號isn有什麼講究?isn和seq有什麼關係?ack的值到底是什麼?

      別慌,彆著急,看完這篇文章,我相信上述問題對你來說就會迎刃而解。

      我將TCP三次握手所涉及到的具體操作,總結為“設標誌位,發序列號”。這裡先告訴你一下,一般來說標誌位的名稱全部大寫,序列號的縮寫名稱全部小寫

      在開始講解之前,我們先來看一下TCP段頭的結構:

 

     

       是不是感覺頭昏眼花,這麼多英文,這麼多組成!其實,三次握手的過程只涉及到兩個序列號(Sequence、Acknowledgement Sequence)和兩個標誌位(ACK、SYN)。注意我這裡的大小寫結構,與上圖是完全對應的。下面我將分別講解這些欄位的含義。

       先來說序列號。序列號包括seq和ack,seq是傳送序列號,ack是確認序列號。

       Sequence,縮寫為seq,中文名為序列號,定義如下:The Sequence number indicates the position in the byte stream of the first byte in the TCP Data field. For example, if the Initial Sequence number is 1,000 and this is the first segment, then the Sequence number is 1,000. If the segment is 500 bytes long, then the sequence number in the next segment will be 1,500 and so on. 所以我們可以把seq理解為傳送序列號。注意,在上述的定義中提到了Initial Sequence number(縮寫isn,中文名初始序列號),你可能會感到疑惑,這和seq有什麼關係?其實,這也是seq,只不過是第一次握手時用的序列號,所以叫初始序列號。傳送端(客戶端)和接收端(伺服器端)的初試序列號都是隨機生成的。初試序列號必須隨機生成,計算機網路的設計者並不是隨隨便便地就制定了這項原則,隨機是有其道理的。

       為了瞭解isn的設計思想,我們首先需要知道TCP的“身份證”是什麼。A TCP connection is uniquely identified by five pieces of information in the TCP and IP headers. The IP source and destination addresses uniquely identify the end points, and the IP Protocol ID for TCP tells us the connection is TCP. The TCP source and destination ports identify they application processes on the end hosts. Together, at any instant, all 5 fields uniquely identify the TCP connection Internet-wide. 即,一個TCP連線的ID是由源埠地址、目的埠地址、源IP地址、目的IP地址共同組成的。

       假設有兩臺主機A和B建立了一個TCP連線,A向B傳送了一些資料後,連線斷開了。我們繼續假設在連線斷開前,有一些報文沒有正常到達B ,而是一直處於傳輸網路中(這是有可能的,因為路由器會毫無先兆地快取或者丟棄任何的資料包,並且如果路由器轉發表出現了錯誤,資料包也會在網路裡進行不必要的兜兜轉轉)。 之後,AB之間又建立了一個連線且ID四元組跟上次的連線一樣。此時,如果上一個連線的報文姍姍來遲,到達了B,且序列號在B的合理區間內,那麼B 會以為這個報文是本次連線的正常報文。這不就出錯了嘛,資料發生了混亂!所以,我們需要隨機的isn。

       如果你還是沒看懂,不妨集思廣益。下面是網友們的解答,可以結合著看一看:

(甲)ISN不同是為了防止老連線的包/偽造包乾擾新連線。舉個例子來說1,2兩個連線先後建立,並且四元組相同,在此情況下如果ISN固定的話,很有可能連線1某個丟失的包在連線1關閉,連線2已經建立的情況下突然被網路轉回來了,因為四元組相同,此時協議棧會以為該包是連線2的包,輕則會導致通訊雙方重傳,重則導致資料錯亂或連線不正常關閉,所以需要開隨機的ISN,理論上該方法也只能大大降低問題出現的概率,而不能完全避免,tcp連線主動關閉時time_wait時間也是為了儘量避免上述情況。

(乙)舉個例子,如果初始化序列號isn是固定的,我們來看看會出現什麼問題。假設isn固定是1,Client和Server建立好一條TCP連線後,Client連續給Server發了10個包,這10個包不知怎麼被鏈路上的路由器快取了(路由器會毫無先兆地快取或者丟棄任何的資料包,而且如果路由器轉發表出現了錯誤,包也會在網路裡進行不必要的兜兜轉轉),這個時候碰巧Client掛掉了,然後Client用同樣的埠號重新連上Server,Client又連續給Server發了幾個包,假設這個時候Client的序列號變成了5。接著,之前被路由器快取的10個資料包全部被路由到Server端了,Server給Client回覆確認號10,這個時候,Client整個都不好了,這是什麼情況?我的序列號才到5,你怎麼給我的確認號是10了,整個都亂了。所以現在的方法是在一個基準值得上產生一個隨機值。

(丙)如果TCP在建立連線時每次都選擇相同的、固定的初始序號,那麼設想以下的情況:   (1)假定主機A和B頻繁地建立連線,傳送一些TCP報文段後,再釋放連線,然後又不斷地建立新的連線、傳送報文段和釋放連線。   (2)假定每一次建立連線時,主機A都選擇相同的、固定的初始序號,例如,選擇1。   (3)假定主機A傳送出的某些TCP報文段在網路中會滯留較長的時間,以致造成主機A超時重傳這些TCP報文段。   (4)假定有一些在網路中滯留時間較長的TCP報文段最後終於到達了主機B,但這時傳送該報文段的那個連線早已釋放了.而在到達主機B時的TCP連線是一條新的TCP連線。   這樣,工作在新的TCP連線下的主機B就有可能會接受在舊的連線傳送的、已經沒有意義的、過時的TCP報文段(因為這個TCP報文段的序號有可能正好處在現在新的連線所使用的序號範圍之中)。結果產生錯誤。   因此,必須使得遲到的TCP報文段的序號不處在新的連線中所使用的序號範圍之中。   這樣,TCP在建立新的連線時所選擇的初始序號一定要和前面的一些連線所使用過的序號不一樣。因此,不同的TCP連線不能使用相同的初始序號。

(丁)(源IP,源埠,目的IP,目的埠,協議號),這五個要素組成一個五元組。協議號都是TCP的話說是四元也行。不同的五元組,它們的SN完全是獨立的空間,互相不干擾,所以是多少都是可以的。但是這裡有個安全問題,如果你使用固定的其實,那麼一個攻擊者就有可能猜出你的SN號序列,然後插入到你的TCP連線當中。我們知道IP協議並不安全,一個傳送者可以很容易偽造自己的源IP地址來傳送資料包,即使他沒有辦法在你和目標中間建立偵聽,他也可以通過推測你們兩者的ISN來傳送可以混進TCP連線的資料,造成很嚴重的安全問題。所以現代作業系統會根據不同的五元組,建立獨立的ISN空間,同一時間建立的五元組不同的連線,會使用不同的ISN,這是為了安全。

       Acknowledgement Sequence,縮寫為ack,中文名為確認序列號,定義如下:The Acknowledgment sequence number tells the other end which byte we are expecting next. It also says that we have successfully receivedevery byte up until the one before this byte number. For example, if the Acknowledgment Sequence number is 751, it means we have received every byte up to and including byte 750. Notice that there are sequence numbers for both directions in every segment. This way, TCP piggybacks acknowledgments on the data segments traveling in the other direction. 也就是說,ack的值用於通知另一端“我接下來想要的資料”,同時還告訴另一端“我已經收到了你剛剛發過來的所有資料”。舉個不是很恰當的例子,這就像一個不太正常的人去吃自助餐,唸叨著“剛剛拿來的五花肉、肥牛和雞米花我都吃完了,接下來我想繼續吃點水果”。

     

       再來說標誌位,在TCP段頭中有六種標誌位(Flag),我們通常將他們的名字全部大寫。注意,這很重要,標誌位的名稱全部大寫!所以,你在網上看到的許多三次握手示意圖裡,SYN和ACK都是標誌位。

       ACK的定義:The ACK flag tells us that the Acknowledgement sequence number is valid and we are acknowledging all of the data up until this point. 從中我們可以知道,只有當ACK這個標誌位為1的時候,ack序列號值才有效。你可以這樣理解:只要有ack的地方,都需要設定ACK=1,ACK就像是ack的開關一樣。

       SYN的定義: The SYN flag tells us that we are signalling a synchronize, which is part of the 3way handshake to set up the connection. SYN是synchronize(同步)的縮寫,在第一次握手和第二次握手時,SYN=1,在第三次握手時,SYN=0。這就像是say hi,兩個人在路上遇到,彼此打了個招呼後,就不再打招呼了,不然,兩人每說一句話,都先來一句hi,這也太奇怪了!

 

       最後,我們再來看一下三次握手示意圖,幾張示意圖結合起來看,我懶得自己畫了:

 

 

 

 

 

 

 

 

       再配一個TCP三次握手過程的文字描述:

       第一次握手:客戶端將標誌位SYN置為1,隨機產生一個值序列號seq=x,並將該資料包傳送給服務端,客戶端 進入syn_sent狀態,等待服務端確認。

       第二次握手:服務端收到資料包後由標誌位SYN=1知道客戶端請求建立連線,服務端將標誌位SYN和 ACK都置為1,ack=x+1,隨機產生一個值seq=y,並將該資料包傳送給客戶端以確認連線請求,服務端進入syn_rcvd狀態。

       第三次握手:客戶端收到確認後檢查,如果正確則將標誌位ACK為1,ack=y+1,並將該資料包傳送給服務端,服務端進行檢查如果正確則連線建立成功,客戶端和服務端進入established狀態,完成三次握手,隨後客戶端和服務端之間可以開始傳輸資料了。

 

       怎麼樣,是不是特別清楚了!如果不是特別清楚的話,建議再讀一遍。如果讀了還是不清楚,那你就洗洗睡吧。

 

注:本文參考了

https://www.cnblogs.com/dormant/p/5422124.html

https://www.zhihu.com/question/49794331

https://www.zhihu.com/question/53658729/answer/255221757

TCP協議中的三次握手和四次揮手(圖解)_Simple life-CSDN部落格_三次握手

TCP三次握手中SYN,ACK,seq ack的含義 - study_goup - 部落格園 (cnblogs.com)

https://github.com/khanhnamle1994/computer-networking/blob/master/Unit2-Transport/2-1-tcp-service-model-notes.pdf

http://interviewtop.top/#/list

相關文章