TCP連接的建立與終止過程

我們知道,網絡間的通信簡單的從應用層來看,就是同一或者不同主機上的進程之間進行信息傳送和/或接收。在信息傳輸的過程中我們需要遵循一定的規則。
舉個簡單例子,假如我想傳送一個信息A給你,那麼按照特定的規則我發送的A在傳輸的過程中實際上是以A++的形式存在的。但是你收到的信息還是A。說明在傳輸過程中我的信息被“規則化”了以後又被“規則化”回來了。這種“規則”,就是我們平時所說的協議。即,要按照它的格調辦事。
本次所討論的對象TCP協議就是這些規則的一種。也就是說,如果我們在網絡上通信時遵循TCP協議的話,就得按照它的格調來。
TCP協議是一個面向連接的協議,無論是在哪一方向另一方發送數據之前,都必須在雙方之間建立一條連接,按照上面所說的,“必須建立連接”就是TCP格調的一部分。
利用TCP協議通信的最簡單三個步驟是:

1.雙方之間建立一條連接
2.信息傳送與接收
3.斷開這條連接

注意:本文經常會提到“通信”一詞,這裏就簡單的指進程之間通過網絡進行信息交流與傳遞。
這裏有個疑問,是不是建立好連接就能進行通信,通信完成後就關閉或者斷開這條連接呢?是的。正常情況下本來就是這麼簡單方便容易理解記憶加操作!但是,很多時候,情況它不正常。那麼不正常指的是什麼呢?我們先把問題放在這裏,來看一下最正常的情況吧。
這裏寫圖片描述
在分析這張簡單的圖之前我們先來看一些概念:

SYN:同步序號,是TCP建立連接時使用的握手信號
ack:接收端給發送端的一種傳輸類控制字符,表示發來的數據已確認接收無誤
FIN:TCP終止時使用的揮手信號

以下,我們就用“服務器”和“客戶端”這兩個名詞來代表我們所討論的通信兩端。

TCP連接時,
服務器端通過socket、bind、listen這三個系統調用函數來完成接收外來連接的準備階段。
當客戶端需要向服務器發送連接請求之時,要創建好套接字。
兩端都準備好了以後就可以完成如圖1所示的TCP正常連接了。

就像打電話時雙方都需要手機和電話號碼一樣,TCP連接需要一個套接字對。一個TCP套接字對是一個定義該連接的兩個端點的四元組。本地IP地址、本地TCP端口號、外地IP地址、外地TCP端口號。每個套接字對唯一標識一個網絡上的一個TCP連接。

標識每個端點的兩個值(IP地址和端口號)通常稱一個套接字。
圖1又叫三次握手,過程如下:
(1)客戶端通過調用系統調用函數connect函數發起主動打開,這導致客戶端TCP發送一個SYN給服務器端,表示我要連你。
(2)服務器接收到請求以後,發送一個ack告訴客戶端說我知道了,同時發送一個SYN給客戶端說你已經連在我這裏了,準備發數據吧。
(3)客戶端收到以後發一個ack給服務器說我知道我能發數據了,發不發看心情。

具體連接的細節我沒有介紹,我主要簡單介紹一下大致過程以及這其中比較特別的一些情況。

連接好以後雙方就可以互相發送和接收數據了,入圖3所示

圖2又叫四次揮手,如圖所示其過程如下:
(1)客戶端首先系統調用函數close,(當然也可以是服務器先調用先關閉,這裏講比較常見的情況)主動關閉連接,客戶端TCP發送一個FIN給服務器端說我不需要連接了咱們斷連吧。
(2)服務器端TCP確認客戶端的FIN並且發送ack給客戶端說我知道了,知道你不會再發數據給我了。
(3)過了一會兒,服務器調用close關閉客戶端在這邊連接的那個套接字,同時給客戶端發送一個FIN說我把在我上面的連接關了。
(4)客戶端收到FIN以後給服務器發送一個ack表示知道自己跟服務器那邊沒關係了,然後安心的死了。

好了,以上就是TCP連接傳送數據最理想最簡單的情況,我上面也提到,通常是沒有這麼簡單的,因爲它老是發生“意外”,有好的也有不好的,我們把這些“意外”統稱爲“特殊情況”。
下面,我們來看一下這些情況:####

TCP的半關閉:

先上圖:
這裏寫圖片描述

先注意:發送一個FIN就表示,我不會再給你發送數據了。
TCP提供了連接的一端在結束它的發送後(發了一個FIN),還能接收來自另一端數據 的能力。直到對方發送過來一個FIN就徹底斷開不再(喪失)接收。

我們看着圖4中的六個箭頭來一步一步分析:
(1)客戶端給服務器發送一個FIN,說我想死,不會再給你發數據了。
(2)服務器給客戶端發送一個ack,表示我知道你不會再給我發了。
但是,服務器這邊還有數據要發給客戶端呀,而且剛好客戶端現在的狀 態是能接收,所以,
(3)服務器端把數據發送給客戶端。
(4)客戶端就很不矜持的接收了並且發給服務器一個ack,說我收到你的數據啦。
當服務器一看沒啥好發的了,你不是想斷嘛,那就把你斷了吧。
(5)服務器把客戶端在它上面的連接斷了,發給客戶端一個FIN說我已經把你斷了不會再給你發了。
(6)客戶端收到FIN以後給服務器發送一個ack表示知道自己跟服務器那邊沒關係了,然後安心的死了。

爲了能使用TCP的半關閉特性,編程接口必須爲應用程序提供一種方式來說明。
方式就是,圖4中客戶端在發FIN的時候不再調用close而改調用shutdown,且第二個參數置爲1,此時插口的API支持半關閉。

然而,雖然TCP有這種能力但是用得少,一般就是把數據接收完了再發送FIN想死也來得及嘛。不過人家也確實有派的上用場的時候,只是情況比較少而已,應用程序(例圖4客戶端),一般都是通過調用close來終止兩個方向的連接。

RST:表示要求對方重新建立連接

在某些情況下,一端會發送給對方一個RST要求重新連接,然後如果雙方本來就沒有連接就算了,如果原來已有連接的話就斷開自身這端的連接。

我在網上看到一篇博文兒介紹了RST可能出現的幾種情況感覺很詳細這裏貼一個超鏈吧,感興趣的可以自己去看:幾種TCP連接中出現RST的情況該文裏面有一些小問題,比如裏面講請求超時連接時,應該把“主機27很不友好”改爲“主機89很不友好”。

在這裏我還是簡單根據上述博文的內容提一下幾種TCP連接中出現RST的情況:

(1)服務器程序端口未打開而客戶端來連接:此時有些操作系統上的主機會迴應一個RST給客戶端,而有些操作系統上的主機則不予理會。
(2)請求超時:例如,客戶端發送一個SYN給服務器端,服務器端在超時之前沒有給客戶端迴應ack或/和SYN,則客戶端會發送一個SYN給服務器端拒絕進一步連接。
(3)提前關閉:例如,你給我發5個數據,我只接三個還有兩個不接,然後我關閉連接,給你發一個RST說我不接了,咱重連吧。
(4)在一個已關閉的socket上收到數據,就會給對方發送一個RST。

簡單地說就是,不管是不接收連接還是不接收數據,只要我不接收你,我就給你發送一個RST。不管以前有沒有連接過,都斷了,給我重連。

說了這麼多,下面來考慮一種情況檢測檢測:服務器主機崩潰重啓後,會給發送數據過來的客戶端發送一個RST,屬於那種情況呢?
沒錯,屬於不接收數據的情況。
因爲當服務器崩潰後重啓時,它的TCP丟失了崩潰前所有的連接信息,TCP連接是安全可靠的,我都不認識你當然不會接收你的數據,給你一個RST,你重新來連我吧。

其實說到這裏下面我就應該上TCP狀態轉移圖了,那麼我就上個圖吧。(這是在一篇博文裏找的圖本來準備附鏈接但沒附,關鍵是要理解圖的含義)
這裏寫圖片描述
感覺我上面把狀態大致過程都說得差不多了,結合到圖中就是每發送或接收一次信息就變一個狀態。這裏我只對上圖中的三個狀態加以描述。

(1)同時打開:同時打開就是兩端同時執行主動打開,這種可能性極小但是存在。爲了處理同時打開TCP還特意設計了一番,對於這種同時打開,TCP建立一條連接而不是兩條(其他好多協議組建立兩條)。
兩端幾乎同時發送SYN並進入SYN_SENT狀態,當每一端收到SYN時,狀態變爲SYN_RCVD狀態。同時它們都再發送SYN(本來服務器該乾的事)並對收到的SYN(本來服務器第二步時客戶端第三步時該乾的事)進行確認。當雙方都收到SYN及相應ACK時,狀態都變遷爲ESTABLISHED。需要注意的是:這裏的兩端每一端都既是服務器又是客戶端。
這裏寫圖片描述
(2)ESTABLISHED:這個狀態時表示三次握手已經完成可以傳送數據啦。
(3)TIME_WAIT:也稱等待2MSL狀態。每個具體TCP實現必須選擇一個報文段最大生存時間MSL,它是任何報文段(數據)被丟棄前在網絡內的最長時間。
其實我一開始特別不理解,爲什麼要有TIME_WAIT這個狀態呢,明明都已經死了啊!那就是沒狀態了嘛還給它時間等啥,直接到CLOSED就好了啊。
後來,我找到了它存在的兩個理由,概述如下:
a.可靠地實現TCP全雙工連接的終止:維護連接一定時間以防最後一個確認ask需要重發。這也就解釋了爲什麼TIME_WAIT 端總是執行主動關閉的那一端。因爲可能不得不重傳最終那個ack的就是那一端。
b.允許老的重複報文段在網絡中消逝:例,我們關閉一個連接A,過一段時間後在相同的IP地址和端口號之間建立一個新連接B,因爲A和B的IP地址和端口號都相同,如果老連接A斷開的時間還不到MSL,B連接就能傳送數據了,此時假如連接A再傳送數據則很有可能會被連接B誤當成是自己連接中的數據來傳送。爲了解決這個問題,TCP將不給TIME_WAIT狀態的連接(IP地址和端口號)發起新的連接,等過了TIME_WAIT這個狀態後已經過去2MSL時間了,連接中的數據早就在網絡中消逝了,此時再發起的新的連接就不會收到老的重複數據了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章