建立一個(gè) TCP 連接TCP 是一個(gè)面向連接的協(xié)議,無(wú)論哪一方向另一方發(fā)送數(shù)據(jù)之前,都必須先在雙方之間建立一條連接。本節(jié)將詳細(xì)討論一個(gè)TCP 連接是如何建立的以及通信結(jié)束后是如何終止的。
TCP使用三次握手 ( three-way handshake ) 協(xié)議來(lái)建立連接,圖 3-10 描述了三次握手的報(bào)文序列。這三次握手為:
請(qǐng)求端(通常稱為客戶)發(fā)送一個(gè) SYN 報(bào)文段( SYN 為 1 )指明客戶打算連接的服務(wù)器的端口,以及初始順序號(hào)( ISN )。
服務(wù)器發(fā)回包含服務(wù)器的初始順序號(hào)的 SYN 報(bào)文段( SYN 為 1 )作為應(yīng)答。同時(shí),將確認(rèn)號(hào)設(shè)置為客戶的 ISN 加 1 以對(duì)客戶的 SYN 報(bào)文段進(jìn)行確認(rèn)( ACK 也為 1 )。
客戶必須將確認(rèn)號(hào)設(shè)置為服務(wù)器的 ISN 加 1 以對(duì)服務(wù)器的 SYN 報(bào)文段進(jìn)行確認(rèn)( ACK 為 1 ),該報(bào)文通知目的主機(jī)雙方已完成連接建立。
三次握手協(xié)議是連接兩端正確同步的充要條件。因?yàn)?TCP 建立在不可靠的分組傳輸服務(wù)之上,報(bào)文可能丟失、延遲、重復(fù)和亂序,因此協(xié)議必須使用超時(shí)和重傳機(jī)制。如果重傳的連接請(qǐng)求和原先的連接請(qǐng)求在連接正在建立時(shí)到達(dá),或者當(dāng)一個(gè)連接已經(jīng)建立、使用和結(jié)束之后,某個(gè)延遲的連接請(qǐng)求才到達(dá),就會(huì)出現(xiàn)問(wèn)題。采用三次握手協(xié)議(加上這樣的規(guī)則:在連接建立之后 TCP 就不再理睬又一次的連接請(qǐng)求)就可以解決這些問(wèn)題。
三次握手協(xié)議可以完成兩個(gè)重要功能:它確保連接雙方做好傳輸準(zhǔn)備,并使雙方統(tǒng)一了初始順序號(hào)。初始順序號(hào)是在握手期間傳輸順序號(hào)并獲得確認(rèn):當(dāng)一端為建立連接而發(fā)送它的 SYN 時(shí),它為連接選擇一個(gè)初始順序號(hào);每個(gè)報(bào)文段都包括了順序號(hào)字段和確認(rèn)號(hào)字段,這使得兩臺(tái)機(jī)器僅僅使用三個(gè)握手報(bào)文就能協(xié)商好各自的數(shù)據(jù)流的順序號(hào)。一般來(lái)說(shuō), ISN 隨時(shí)間而變化,因此每個(gè)連接都將具有不同的ISN 。
關(guān)閉一個(gè) TCP 連接
TCP 連接建立起來(lái)后,就可以在兩個(gè)方向傳送數(shù)據(jù)流。當(dāng) TCP 的應(yīng)用進(jìn)程再?zèng)]有數(shù)據(jù)需要發(fā)送時(shí),就發(fā)關(guān)閉命令。 TCP 通過(guò)發(fā)送控制位 FIN=1 的數(shù)據(jù)片來(lái)關(guān)閉本方數(shù)據(jù)流,但還可以繼續(xù)接收數(shù)據(jù),直到對(duì)方關(guān)閉那個(gè)方向的數(shù)據(jù)流,連接就關(guān)閉。
TCP 協(xié)議使用修改的三次握手協(xié)議來(lái)關(guān)閉連接, 如圖 3-11 所示,即終止一個(gè)連接要經(jīng)過(guò) 4 次握手。這是因?yàn)?TCP 的半關(guān)閉( half-close )造成的。由于一個(gè) TCP 連接是全雙工(即數(shù)據(jù)在兩個(gè)方向上能同時(shí)傳遞),因此每個(gè)方向必須單獨(dú)地進(jìn)行關(guān)閉。關(guān)閉的原則就是當(dāng)一方完成它的數(shù)據(jù)發(fā)送任務(wù)后就能發(fā)送一個(gè) FIN 來(lái)終止這個(gè)方向連接。當(dāng)一端收到一個(gè) FIN ,它必須通知應(yīng)用層另一端已經(jīng)終止了那個(gè)方向的數(shù)據(jù)傳送。發(fā)送 FIN 通常是應(yīng)用層進(jìn)行關(guān)閉的結(jié)果。

從一方的 TCP 來(lái)說(shuō),連接的關(guān)閉有三種情況:
本方啟動(dòng)關(guān)閉
收到本方應(yīng)用進(jìn)程的關(guān)閉命令后, TCP 在發(fā)送完尚未處理的報(bào)文段后,發(fā) FIN = 1 的報(bào)文段給對(duì)方,且 TCP 不再受理本方應(yīng)用進(jìn)程的數(shù)據(jù)發(fā)送。在 FIN 以前發(fā)送的數(shù)據(jù)字節(jié),包括 FIN ,都需要對(duì)方確認(rèn),否則要重傳。注意 FIN 也占一個(gè)順序號(hào)。一旦收到對(duì)方對(duì) FIN 的確認(rèn)以及對(duì)方的 FIN 報(bào)文段,本方 TCP 就對(duì)該 FIN 進(jìn)行確認(rèn),在等待一段時(shí)間,然后關(guān)閉連接。等待是為了防止本方的確認(rèn)報(bào)文丟失,避免對(duì)方的重傳報(bào)文干擾新的連接。
對(duì)方啟動(dòng)關(guān)閉
TCP 收到對(duì)方發(fā)來(lái)的 FIN 報(bào)文時(shí),發(fā) ACK 確認(rèn)此 FIN 報(bào)文,并通知應(yīng)用進(jìn)程連接正在關(guān)閉。應(yīng)用進(jìn)程將以關(guān)閉命令響 應(yīng)。 TCP 在發(fā)送完尚未處理的報(bào)文段后,發(fā)一個(gè) FIN 報(bào)文給對(duì)方 TCP ,然后等待對(duì)方對(duì) FIN 的確認(rèn),收到確認(rèn)后關(guān)閉連接。若對(duì)方的確認(rèn)未及時(shí)到達(dá),在等待一段時(shí)間后也關(guān)閉連接。
雙方同時(shí)啟動(dòng)關(guān)閉
連接雙方的應(yīng)用進(jìn)程同時(shí)發(fā)關(guān)閉命令,則雙方 TCP 在發(fā)送完尚未處理的報(bào)文段后,發(fā)送 FIN 報(bào)文。各方 TCP 在 FIN 前所發(fā)報(bào)文都得到確認(rèn)后,發(fā) ACK 確認(rèn)它收到的 FIN 。各方在收到對(duì)方對(duì) FIN 的確認(rèn)后,同樣等待一段時(shí)間再關(guān)閉連接。這稱之為同時(shí)關(guān)閉( simultaneous close )。
TCP 狀態(tài)機(jī)
TCP 協(xié)議的操作可以使用一個(gè)具有 11 種狀態(tài)的有限狀態(tài)機(jī)( Finite State Machine )來(lái)表示,圖 3-12 描述了 TCP 的有限狀態(tài)機(jī),圖中的圓角矩形表示狀態(tài),箭頭表示狀態(tài)之間的轉(zhuǎn)換,各狀態(tài)的描述如表 3-2 所示。圖中用粗線表示客戶端主動(dòng)和被動(dòng)的服務(wù)器端建立連接的正常過(guò)程:客戶端的狀態(tài)變遷用粗實(shí)線,服務(wù)器端的狀態(tài)變遷用粗虛線。細(xì)線用于不常見的序列,如復(fù)位、同時(shí)打開、同時(shí)關(guān)閉等。圖中的每條狀態(tài)變換線上均標(biāo)有“事件/動(dòng)作”:事件是指用戶執(zhí)行了系統(tǒng)調(diào)用( CONNECT 、 LISTEN 、 SEND 或 CLOSE )、收到一個(gè)報(bào)文段( SYN 、 FIN 、 ACK 或 RST )、或者是出現(xiàn)了超過(guò)兩倍最大的分組生命期的情況;動(dòng)作是指發(fā)送一個(gè)報(bào)文段( SYN 、 FIN 或 ACK )或什么也沒(méi)有(用“-”表示)。

圖 3-12 TCP 有限狀態(tài)機(jī)。粗實(shí)線表示客戶的正常路徑;
粗虛線表示服務(wù)器的正常路徑;細(xì)線表示不常見的事件。
每個(gè)連接均開始于CLOSED 狀態(tài)。當(dāng)一方執(zhí)行了被動(dòng)的連接原語(yǔ)LISTEN或主動(dòng)的連接原語(yǔ)CONNECT時(shí),它便會(huì)脫離CLOSED狀態(tài)。如果此時(shí)另一方執(zhí)行了相對(duì)應(yīng)的連接原語(yǔ),連接便建立了,并且狀態(tài)變?yōu)镋STABLISHED 。任何一方均可以首先請(qǐng)求釋放連接,當(dāng)連接被釋放后,狀態(tài)又回到了CLOSED 。
表 3-2 TCP 狀態(tài)表
狀 態(tài) | 描 述 |
CLOSED | 關(guān)閉狀態(tài),沒(méi)有連接活動(dòng)或正在進(jìn)行 |
LISTEN | 監(jiān)聽狀態(tài),服務(wù)器正在等待連接進(jìn)入 |
SYN RCVD | 收到一個(gè)連接請(qǐng)求,尚未確認(rèn) |
SYN SENT | 已經(jīng)發(fā)出連接請(qǐng)求,等待確認(rèn) |
ESTABLISHED | 連接建立,正常數(shù)據(jù)傳輸狀態(tài) |
FIN WAIT 1 | (主動(dòng)關(guān)閉)已經(jīng)發(fā)送關(guān)閉請(qǐng)求,等待確認(rèn) |
FIN WAIT 2 | (主動(dòng)關(guān)閉)收到對(duì)方關(guān)閉確認(rèn),等待對(duì)方關(guān)閉請(qǐng)求 |
TIMED WAIT | 完成雙向關(guān)閉,等待所有分組死掉 |
CLOSING | 雙方同時(shí)嘗試關(guān)閉,等待對(duì)方確認(rèn) |
CLOSE WAIT | (被動(dòng)關(guān)閉)收到對(duì)方關(guān)閉請(qǐng)求,已經(jīng)確認(rèn) |
LAST ACK | (被動(dòng)關(guān)閉)等待最后一個(gè)關(guān)閉確認(rèn),并等待所有分組死掉 |
1. 正常狀態(tài)轉(zhuǎn)換
我們用圖3-13 來(lái)顯示在正常的TCP 連接的建立與終止過(guò)程中,客戶與服務(wù)器所經(jīng)歷的不同狀態(tài)。讀者可以對(duì)照?qǐng)D 3-12 來(lái)閱讀,使用圖3-12 的狀態(tài)圖來(lái)跟蹤圖3-13 的狀態(tài)變化過(guò)程,以便明白每個(gè)狀態(tài)的變化:
服務(wù)器端首先執(zhí)行LISTEN 原語(yǔ)進(jìn)入被動(dòng)打開狀態(tài)(LISTEN),等待客戶端連接;
當(dāng)客戶端的一個(gè)應(yīng)用程序發(fā)出CONNECT命令后,本地的TCP 實(shí)體為其創(chuàng)建一個(gè)連接記錄并標(biāo)記為SYN SENT 狀態(tài),然后給服務(wù)器發(fā)送一個(gè)SYN 報(bào)文段;
服務(wù)器收到一個(gè) SYN 報(bào)文段,其TCP 實(shí)體給客戶端發(fā)送確認(rèn) ACK 報(bào)文段同時(shí)發(fā)送一個(gè) SYN 信號(hào),進(jìn)入 SYN RCVD 狀態(tài);
客戶端收到SYN + ACK 報(bào)文段,其TCP 實(shí)體給服務(wù)器端發(fā)送出三次握手的最后一個(gè) ACK 報(bào)文段,并轉(zhuǎn)換為ESTABLISHED狀態(tài);
服務(wù)器端收到確認(rèn)的ACK報(bào)文段,完成了三次握手,于是也進(jìn)入ESTABLISHED 狀態(tài)。
在此狀態(tài)下,雙方可以自由傳輸數(shù)據(jù)。當(dāng)一個(gè)應(yīng)用程序完成數(shù)據(jù)傳輸任務(wù)后,它需要關(guān)閉TCP連接。假設(shè)仍由客戶端發(fā)起主動(dòng)關(guān)閉連接。
客戶端執(zhí)行CLOSE原語(yǔ),本地的TCP 實(shí)體發(fā)送一個(gè)FIN 報(bào)文段并等待響應(yīng)的確認(rèn)進(jìn)入狀態(tài)FIN WAIT 1 ;
服務(wù)器收到一個(gè) FIN 報(bào)文段,它確認(rèn)客戶端的請(qǐng)求發(fā)回一個(gè) ACK 報(bào)文段,進(jìn)入CLOSE WAIT狀態(tài);
客戶端收到確認(rèn)ACK報(bào)文段,就轉(zhuǎn)移到FIN WAIT 2狀態(tài),此時(shí)連接在一個(gè)方向上就斷開了;
服務(wù)器端應(yīng)用得到通告后,也執(zhí)行CLOSE原語(yǔ)關(guān)閉另一個(gè)方向的連接,其本地TCP 實(shí)體向客戶端發(fā)送一個(gè) FIN 報(bào)文段,并進(jìn)入LAST ACK 狀態(tài),等待最后一個(gè) ACK 確認(rèn)報(bào)文段;
客戶端收到FIN報(bào)文段并確認(rèn),進(jìn)入TIMED WAIT 狀態(tài),此時(shí)雙方連接均已經(jīng)斷開,但TCP 要等待一個(gè)2倍報(bào)文段最大生存時(shí)間MSL( Maximum Segment Lifetime ),確保該連接的所有分組全部消失,以防止出現(xiàn)確認(rèn)丟失的情況。當(dāng)定時(shí)器超時(shí)后,TCP 刪除該連接記錄,返回到初始狀態(tài)(CLOSED)。
服務(wù)器收到最后一個(gè)確認(rèn) ACK 報(bào)文段,其TCP 實(shí)體便釋放該連接,并刪除連接記錄,返回到初始狀態(tài)( CLOSED )。

2. 同時(shí)打開:
盡管發(fā)生的可能性極小,兩個(gè)應(yīng)用程序同時(shí)彼此執(zhí)行主動(dòng)打開的情況還是可能的。每一方必須發(fā)送一個(gè) SYN ,且這些 SYN 必須傳遞給對(duì)方。這需要每一方使用一個(gè)對(duì)方周知的端口作為本地端口。例如,主機(jī) A 中的一個(gè)應(yīng)用程序使用本地端口 7777 ,并與主機(jī) B 的端口 8888 執(zhí)行主動(dòng)打開。主機(jī) B 中的應(yīng)用程序則使用本地端口 8888 ,并與主機(jī) A 的端口 7777 執(zhí)行主動(dòng)打開。 TCP 是特意設(shè)計(jì)為了可以處理同時(shí)打開,對(duì)于同時(shí)打開它僅建立一條連接而不是兩條連接(其他的協(xié)議族,最突出的是 OSI 傳輸層,在這種情況下將建立兩條連接而不是一條連接)。
當(dāng)出現(xiàn)同時(shí)打開的情況時(shí),狀態(tài)變遷與圖 3-13 所示的不同。兩端幾乎在同時(shí)發(fā)送 SYN ,并進(jìn)入 SYN_SENT 狀態(tài)。當(dāng)每一端收到 SYN 時(shí),狀態(tài)變?yōu)?SYN_RCVD ,同時(shí)它們都再發(fā) SYN 并對(duì)收到的 SYN 進(jìn)行確認(rèn)。當(dāng)雙方都收到 SYN 及相應(yīng)的 ACK 時(shí),狀態(tài)都變遷為 ESTABLISHED 。圖 3-14 顯示了這些狀態(tài)變遷過(guò)程。
圖 3-14 同時(shí)打開期間報(bào)文段的交換
一個(gè)同時(shí)打開的連接需要交換 4 個(gè)報(bào)文段,比正常的三次握手多一個(gè)。此外,要注意的是我們沒(méi)有將任何一端稱為客戶或服務(wù)器,因?yàn)槊恳欢思仁强蛻粲质欠?wù)器。
3. 同時(shí)關(guān)閉:
正常情況下都是由一方(通常但不總是客戶方)發(fā)送第一個(gè) FIN 執(zhí)行主動(dòng)關(guān)閉,但雙方都執(zhí)行主動(dòng)關(guān)閉也是可能的, TCP 協(xié)議也允許這樣的同時(shí)關(guān)閉。
在圖3-12 中,當(dāng)兩端應(yīng)用層同時(shí)發(fā)出關(guān)閉命令時(shí),兩端均從ESTABLISHED 變?yōu)镕IN_WAIT_1 。這將導(dǎo)致雙方各發(fā)送一個(gè)FIN ,兩個(gè)FIN 經(jīng)過(guò)網(wǎng)絡(luò)傳送后分別到達(dá)另一端。收到FIN 后,狀態(tài)由FIN_WAIT_1變遷到 CLOSING ,并發(fā)送最后的ACK 。當(dāng)收到最后的ACK 時(shí),狀態(tài)變化為TIME_WAIT 。圖3-15 總結(jié)了這些狀態(tài)的變化,從圖中可以看出同時(shí)關(guān)閉與正常關(guān)閉使用的報(bào)文段交換數(shù)目相同。
圖 3-15 同時(shí)關(guān)閉期間的報(bào)文段交換
4. 其它情況:
服務(wù)方打開:從LISTEN到SYN_SENT的變遷是正確的,它由服務(wù)器端主動(dòng)發(fā)出 SYN 報(bào)文段,但 Berkeley 版的 TCP 軟件并不支持它。
重置連接(復(fù)位):只有當(dāng) SYN_RCVD 狀態(tài)是從LISTEN 狀態(tài)(正常情況)進(jìn)入,而不是從SYN_SENT狀態(tài)(同時(shí)打開)進(jìn)入時(shí),從SYN_RCVD 回到LISTEN 的狀態(tài)變遷才是有效的。這意味著如果我們執(zhí)行被動(dòng)打開(進(jìn)入 LISTEN ),收到一個(gè) SYN ,發(fā)送一個(gè)帶ACK 的SYN(進(jìn)入 SYN_RCVD ),然后收到一個(gè) RST ,而不是一個(gè) ACK ,便又回到 LISTEN 狀態(tài)并等待另一個(gè)連接請(qǐng)求的到來(lái)。
快速關(guān)閉:在主動(dòng)關(guān)閉后的FIN_WAIT_1狀態(tài),如果收到的報(bào)文段不僅是ACK ,而且還包括對(duì)方的 FIN 信號(hào),則直接進(jìn)入 TIME_WAIT 狀態(tài),給對(duì)方發(fā)送 ACK 報(bào)文段,然后等待超時(shí)。
另外, TIME_WAIT 狀態(tài)的等待超時(shí)需要再詳細(xì)解釋一下,因?yàn)樗苯佑绊懙骄W(wǎng)絡(luò)應(yīng)用程序的表現(xiàn)。
每個(gè)具體 TCP 實(shí)現(xiàn)必須選擇一個(gè)報(bào)文段最大生存時(shí)間 MSL ( Maximum Segment Lifetime ),它是任何報(bào)文段被丟棄前在網(wǎng)絡(luò)內(nèi)的最長(zhǎng)時(shí)間。我們知道這個(gè)時(shí)間是有限的,因?yàn)?TCP 報(bào)文段以 IP 數(shù)據(jù)報(bào)在網(wǎng)絡(luò)內(nèi)傳輸,而 IP 數(shù)據(jù)報(bào)有限制其生存時(shí)間的 TTL 字段。 RFC 793 [Postel 1981c ] 指出 MSL 為 2 分鐘。然而,實(shí)現(xiàn)中的常用值是 30 秒、 1 分鐘、或 2 分鐘。
對(duì)一個(gè)具體實(shí)現(xiàn)所給定的 MSL 值,處理的原則是:當(dāng) TCP 執(zhí)行一個(gè)主動(dòng)關(guān)閉,并發(fā)回最后一個(gè) ACK ,該連接必須在 TIME_WAIT 狀態(tài)停留的時(shí)間為 2 倍的 MSL ,因此 TIME_WAIT 狀態(tài)也稱為 2MSL 等待狀態(tài)。在這段時(shí)間內(nèi),如果最后的 ACK 丟失,對(duì)方會(huì)超時(shí)并重發(fā)最后的 FIN ,這樣本地 TCP 可以再次發(fā)送 ACK 報(bào)文段(這也是它唯一可以發(fā)送的報(bào)文,并重置 2MSL 定時(shí)器)。
這種 2MSL 等待的另一個(gè)結(jié)果是這個(gè) TCP 連接在 2MSL 等待期間,定義這個(gè)連接的套接字( socket ,客戶的 IP 地址和端口號(hào),服務(wù)器的 IP 地址和端口號(hào))不能再被使用。這個(gè)連接只能在 2MSL 結(jié)束后才能再被使用。在連接處于 2MSL 等待時(shí),任何遲到的報(bào)文段將被丟棄。
我們假設(shè)圖 3-12 中是客戶執(zhí)行主動(dòng)關(guān)閉并進(jìn)入 TIME_WAIT ,這是正常的情況,因?yàn)榉?wù)器通常執(zhí)行被動(dòng)關(guān)閉,不會(huì)進(jìn)入 TIME_WAIT 狀態(tài)。這暗示如果我們終止一個(gè)客戶程序,并立即重新啟動(dòng)這個(gè)客戶程序,則這個(gè)新客戶程序?qū)⒉荒苤赜孟嗤谋镜囟丝?。這不會(huì)帶來(lái)什么問(wèn)題,因?yàn)榭蛻羰褂帽镜囟丝冢⒉魂P(guān)心這個(gè)端口號(hào)是什么。然而,對(duì)于服務(wù)器,情況就有所不同,因?yàn)榉?wù)器使用周知端口。如果我們終止一個(gè)已經(jīng)建立連接的服務(wù)器程序,并試圖立即重新啟動(dòng)這個(gè)服務(wù)器程序,服務(wù)器程序?qū)⒉荒馨阉倪@個(gè)周知端口賦值給它的端點(diǎn),因?yàn)槟莻€(gè)端口是處于 2MSL 連接的一部分。在重新啟動(dòng)服務(wù)器程序前,它需要在 1~4 分鐘。這就是很多網(wǎng)絡(luò)服務(wù)器程序被殺死后不能夠馬上重新啟動(dòng)的原因(錯(cuò)誤提示為“ Address already in use ”)。

轉(zhuǎn)自:
http://blog.163.com/tyw_andy/blog/static/1167902120099293916894/