因?yàn)門CP是一個(gè)面向連接的協(xié)議充易。無(wú)論哪一方向另一方發(fā)送數(shù)據(jù)之前菇民,都必須先在雙方之間建立一條連接禁筏。
連接的建立與終止
TCP的三次握手
1持钉、請(qǐng)求端發(fā)送一個(gè)SYN段指明客戶打算連接的服務(wù)器的端口,以及初始序號(hào)(ISN篱昔,在這個(gè)例子中位1415531521)每强。這個(gè)SYN段為報(bào)文段1
2、服務(wù)器發(fā)回包含服務(wù)器的初始序號(hào)的SYN報(bào)文段(報(bào)文段2)作為應(yīng)答州刽,同時(shí)空执,將確認(rèn)序號(hào)設(shè)置為客戶的ISN加1以對(duì)客戶的SYN報(bào)文段進(jìn)行確認(rèn),一個(gè)SYN將占用一個(gè)序號(hào)
3穗椅、客戶必須將確認(rèn)序號(hào)設(shè)置為服務(wù)器的ISN加1以對(duì)服務(wù)器的SYN報(bào)文段進(jìn)行確認(rèn)(報(bào)文段3)
發(fā)送第一個(gè)SYN的一端將執(zhí)行主動(dòng)打開(active open)辨绊,接收這個(gè)SYN并發(fā)回下一個(gè)SYN的另一端執(zhí)行被動(dòng)打開(passive open)
當(dāng)一端為建立連接而發(fā)送它的SYN時(shí),它為連接選擇一個(gè)初始序號(hào)匹表,ISN隨時(shí)間而變化门坷,因此每個(gè)連接都將具有不同的ISN。
RFC 793指出 ISN可看作是一個(gè)32比特的計(jì)數(shù)器袍镀,每4ms加1默蚌,這樣選序號(hào)的目的在于防止在網(wǎng)絡(luò)中被延遲的分組在以后又被傳送,而導(dǎo)致某個(gè)連接的一方對(duì)它作錯(cuò)誤的解釋苇羡。
連接終止協(xié)議
終止一個(gè)連接需要經(jīng)過(guò)4次握手
由TCP的半關(guān)閉(half-close)造成的敏簿。以為一個(gè)TCP連接是全雙工(即數(shù)據(jù)在兩個(gè)方向上能同時(shí)傳遞),因此每個(gè)方向必須單獨(dú)地進(jì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ù)傳送绣硝。
收到一個(gè)FIN只意味著在這一方向上沒有數(shù)據(jù)流動(dòng),一個(gè)TCP連接在收到一個(gè)FIN后扔能發(fā)送數(shù)據(jù)撑刺。
正常關(guān)閉的流程:
首先進(jìn)行關(guān)閉的一方(即發(fā)送第一個(gè)FIN)將執(zhí)行主動(dòng)關(guān)閉鹉胖,而另一方(收到這個(gè)FIN)執(zhí)行被動(dòng)關(guān)閉,通常一方完成主動(dòng)關(guān)閉而另一方完成被動(dòng)關(guān)閉。
下圖是一個(gè)終止一個(gè)連接的典型握手順序
連接通常是由客戶端發(fā)起的甫菠,這樣第一個(gè)SYN從客戶傳到服務(wù)器挠铲,每一端都能主動(dòng)關(guān)閉這個(gè)連接(即首先發(fā)送FIN)。然而寂诱,一般由客戶端決定何時(shí)終止連接拂苹,因?yàn)榭蛻暨M(jìn)程通常由用戶交互控制,用戶會(huì)鍵入諸如“quit”一樣的命令來(lái)終止進(jìn)程痰洒。
連接建立的超時(shí)
1瓢棒、服務(wù)器主機(jī)沒有處于正常狀態(tài)。
大多數(shù)伯克利系統(tǒng)建立一個(gè)新連接的最長(zhǎng)時(shí)間限制為75秒丘喻。
最大報(bào)文長(zhǎng)度MSS
MSS 表示TCP傳往另一端的最大塊數(shù)據(jù)的長(zhǎng)度脯宿。
當(dāng)一個(gè)連接建立時(shí),連接的雙方都要通告各自的MSS泉粉。
我們已經(jīng)見過(guò)的MSS都是1024连霉,這導(dǎo)致IP數(shù)據(jù)報(bào)通常是40字節(jié)長(zhǎng):20字節(jié)的TCP首部和20字節(jié)的IP首部
MSS并不是任何條件下都可協(xié)商,當(dāng)建立一個(gè)連接時(shí)嗡靡,每一方都有用于通告它期望接收的MSS選項(xiàng)(MSS選項(xiàng)只能出現(xiàn)在SYN報(bào)文段中)跺撼。如果一方不接收來(lái)自另一方的MSS值,則MSS就定為默認(rèn)值536字節(jié)(這個(gè)默認(rèn)值允許20字節(jié)IP首部和20字節(jié)的TCP首部以適合576字節(jié)IP數(shù)據(jù)報(bào))叽躯。
如果目的IP地址為“非本地的(nonlocal)”(不是一個(gè)網(wǎng)段的)MSS通常的默認(rèn)值為536财边。
大多數(shù)TCP實(shí)現(xiàn)版都提供了一個(gè)配置選項(xiàng),讓系統(tǒng)管理員說(shuō)明不同的子網(wǎng)是屬于本地還是非本地点骑。這個(gè)選項(xiàng)的設(shè)置將確定MSS可以選擇盡可能的大(達(dá)到外出接口的MTU長(zhǎng)度)或是默認(rèn)值536酣难。
MSS讓主機(jī)限制另一端發(fā)送數(shù)據(jù)報(bào)的長(zhǎng)度,加上主機(jī)也能控制它發(fā)送數(shù)據(jù)報(bào)的長(zhǎng)度黑滴,這將使較小MTU連接到一個(gè)網(wǎng)絡(luò)上的主機(jī)避免分段憨募。
TCP的半關(guān)閉
TCP提供了連接的一端在結(jié)束它的發(fā)送后還能接受來(lái)自另一端數(shù)據(jù)的能力,這就是所謂的TCP 的半關(guān)閉袁辈。
為了使用這個(gè)特性菜谣,編程接口必須為應(yīng)用程序提供一種方式來(lái)說(shuō)明“我已經(jīng)完成了數(shù)據(jù)傳送,因此發(fā)送一個(gè)文件結(jié)束(FIN)給另一端晚缩,但我還想接收另一端發(fā)來(lái)的數(shù)據(jù)尾膊,直到它給我發(fā)來(lái)文件結(jié)束(FIN)”
TCP的狀態(tài)變遷圖
兩個(gè)導(dǎo)致進(jìn)入ESTABLISHED狀態(tài)的變遷對(duì)應(yīng)打開一個(gè)連接,而兩個(gè)導(dǎo)致從ESTABLISHED狀態(tài)離開的變遷對(duì)應(yīng)關(guān)閉一個(gè)連接荞彼。
ESTABLISHED狀態(tài)是連接雙方能夠進(jìn)行雙向數(shù)據(jù)傳遞的狀態(tài)冈敛。
2MSL等待狀態(tài)
TIME_WAIT狀態(tài)也稱為2MSL等待狀態(tài)。每個(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)門CP報(bào)文段以IP數(shù)據(jù)報(bào)在網(wǎng)絡(luò)內(nèi)傳輸,而IP數(shù)據(jù)報(bào)則有限制其生存時(shí)間的TTL字段癌压。
RFC 793指出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。這樣可讓TCP再次發(fā)送最后的ACK以防這個(gè)ACK丟失(另一端超時(shí)并重發(fā)最后的FIN)
這種2MSL等待的另一個(gè)結(jié)果是這個(gè)TCP連接在2MSL等待期間券犁,定義這個(gè)連接的插口(客戶的IP地址和端口號(hào)术健,服務(wù)器的IP地址和端口號(hào))不能再被使用。這個(gè)連接只能在2MSL結(jié)束后才能再被使用粘衬。
目前大多數(shù)TCP實(shí)現(xiàn)(如伯克利版)強(qiáng)加了更加嚴(yán)格的限制荞估,在2MSL等待期間,插口中使用的本地端口在默認(rèn)情況下不能再被使用稚新。
在連接處于2MSL等待時(shí)勘伺,任何遲到的報(bào)文段將被丟棄,因?yàn)樘幱?MSL等待的褂删、由該插口對(duì)(socket pair)定義的連接在這段時(shí)間內(nèi)不能被再用飞醉,因此當(dāng)要建立一個(gè)有效的連接時(shí),來(lái)自該連接的一個(gè)較早替身(incarnation)的遲到報(bào)文段作為新連接的一部分不可能不被曲解(一個(gè)連接由一個(gè)插口對(duì)來(lái)定義屯阀,一個(gè)連接的新的實(shí)例(instance)稱為該連接的替身)缅帘。
服務(wù)器通常執(zhí)行被動(dòng)關(guān)閉,不會(huì)進(jìn)入TIME_WAIT狀態(tài)难衰,這暗示如果我們終止一個(gè)客戶程序钦无,并立即重新啟動(dòng)這個(gè)客戶程序,則這個(gè)新客戶程序?qū)⒉荒苤赜孟嗤谋镜囟丝诟窍?duì)客戶雖然沒什么失暂,但是對(duì)于服務(wù)器來(lái)說(shuō),如果我們終止一個(gè)已經(jīng)建立連接的服務(wù)器程序鳄虱,并試圖立即重新啟動(dòng)這個(gè)服務(wù)器程序弟塞,服務(wù)器程序?qū)⒉荒馨阉倪@個(gè)熟知端口賦值給它的端點(diǎn),因?yàn)槟莻€(gè)端口是處于2MSL連接的一部分拙已。在重新啟動(dòng)服務(wù)器程序前宣肚,它需要在1 ~ 4分鐘。
TCP在重啟動(dòng)后的MSL秒內(nèi)不能建立任何連接悠栓,這就稱為平靜時(shí)間(quiet time)
FIN_WAIT_2狀態(tài)
復(fù)位報(bào)文段
TCP首部中RST比特是用于“復(fù)位”的霉涨。
無(wú)論何時(shí)一個(gè)報(bào)文段發(fā)往基準(zhǔn)的連接(referenced connection)出現(xiàn)錯(cuò)誤,TCP都會(huì)發(fā)出一個(gè)復(fù)位報(bào)文段(基準(zhǔn)連接指目的IP地址和目的端口號(hào)以及源IP和源端口號(hào)指明的連接)惭适。
到不存在的端口的連接請(qǐng)求
產(chǎn)生復(fù)位的一種情況是當(dāng)連接請(qǐng)求到達(dá)時(shí)笙瑟,目的端口沒有進(jìn)程正在聽,對(duì)于UDP的話癞志,會(huì)產(chǎn)生一個(gè)ICMP端口不可達(dá)的消息往枷,而TCP則使用復(fù)位。
異常終止一個(gè)連接
終止一個(gè)連接的正常方式是一方發(fā)送FIN凄杯,有時(shí)這也稱為有序釋放(orderly release)错洁,因?yàn)樵谒信抨?duì)數(shù)據(jù)都已發(fā)送之后才發(fā)送FIN,正常情況下沒有任何數(shù)據(jù)丟失戒突,但也有可能發(fā)送一個(gè)復(fù)位報(bào)文段而不是FIN來(lái)中途釋放一個(gè)連接屯碴。有時(shí)稱這為異常釋放(abortive release)
異常終止一個(gè)連接對(duì)應(yīng)用程序來(lái)說(shuō)有兩個(gè)優(yōu)點(diǎn):
1、丟棄任何待發(fā)數(shù)據(jù)并立即發(fā)送復(fù)位報(bào)文段
2膊存、RST的接收方會(huì)區(qū)分另一端執(zhí)行的是異常關(guān)閉還是正常關(guān)閉导而。應(yīng)用程序使用的API必須提供產(chǎn)生異常關(guān)閉而不是正常關(guān)閉的手段。
檢測(cè)半打開鏈接
如果一方已經(jīng)關(guān)閉或異常終止連接而另一方卻還不知道隔崎,我們將這樣的TCP連接成為半打開(Half-Open)的
只要不打算在半打開連接上傳輸數(shù)據(jù)今艺,扔處于連接狀態(tài)的一方就不會(huì)檢測(cè)另一方已經(jīng)出現(xiàn)異常。
1爵卒、任何一端的主機(jī)異常都可能導(dǎo)致發(fā)生這種情況
2虚缎、當(dāng)客戶主機(jī)突然掉電而不是正常的結(jié)束客戶應(yīng)用程序后再關(guān)機(jī)(可能發(fā)生在使用PC機(jī)作Telnet的客戶主機(jī)上)
同時(shí)打開
TCP對(duì)于同時(shí)打開,它僅建立一條連接而不是兩條連接
同時(shí)打開情況的狀態(tài)變遷圖:
兩端幾乎在同時(shí)發(fā)送SYN钓株,并進(jìn)入SYN_SENT狀態(tài)实牡,當(dāng)每一端收到SYN時(shí),狀態(tài)變?yōu)镾YN_RCVD享幽,同時(shí)它們都再發(fā)SYN并對(duì)收到的SYN進(jìn)行確認(rèn)铲掐,當(dāng)雙方都收到SYN及相應(yīng)的ACK時(shí),狀態(tài)都變遷為ESTABLISHED值桩。
一個(gè)同時(shí)打開的連接需要交換4個(gè)報(bào)文段摆霉,比正常的三次握手多一個(gè)。
相對(duì)應(yīng)的也有同時(shí)關(guān)閉奔坟。
TCP選項(xiàng)
TCP服務(wù)器
當(dāng)一個(gè)服務(wù)器進(jìn)程接受一來(lái)自客戶進(jìn)程的服務(wù)請(qǐng)求時(shí)是如何處理端口的携栋?如果多個(gè)連接請(qǐng)求幾乎同時(shí)到達(dá)會(huì)發(fā)生什么情況?
比如咳秉,我們看一個(gè)Telnet婉支,首先處于LISTEN狀態(tài),等待連接請(qǐng)求的到達(dá)澜建,之后主機(jī)啟動(dòng)一個(gè)Telnet客戶程序來(lái)連接這個(gè)Telnet服務(wù)器向挖,會(huì)發(fā)現(xiàn)狀態(tài)變成了ESTABLISHED狀態(tài)蝌以,但是處于LISTEN狀態(tài)的服務(wù)器進(jìn)程仍然存在,這個(gè)服務(wù)器進(jìn)程是當(dāng)前Telnet服務(wù)器用于接收其他的連接請(qǐng)求何之,當(dāng)傳入的連接請(qǐng)求到達(dá)并被接收時(shí)跟畅,系統(tǒng)內(nèi)核中的TCP模塊就創(chuàng)建一個(gè)處于ESTABLISHED狀態(tài)的進(jìn)程,另外處于ESTABLISHED狀態(tài)的連接的端口不會(huì)變化也是23溶推,與處與LISTEN狀態(tài)的進(jìn)程相同徊件。
我們?cè)偃ブ鳈C(jī)啟動(dòng)另一個(gè)Telnet客戶進(jìn)程,并仍與這個(gè)Telnet服務(wù)器進(jìn)行連接蒜危,那么現(xiàn)在有兩條從相同主機(jī)到相同服務(wù)器的處于ESTABLISHED的連接虱痕,它們的本地端口號(hào)均為23,由于它們的遠(yuǎn)端端口號(hào)不同辐赞,這不會(huì)造成沖突部翘,因?yàn)槊總€(gè)Telnet客戶進(jìn)程要是用一個(gè)外設(shè)端口,并且這個(gè)外設(shè)端口會(huì)選擇為主機(jī)當(dāng)前未曾使用的端口占拍,因此它們的端口號(hào)肯定不同略就。
呼入連接請(qǐng)求隊(duì)列
一個(gè)并發(fā)服務(wù)器調(diào)用一個(gè)新的進(jìn)程來(lái)處理每個(gè)客戶請(qǐng)求,因此處于被動(dòng)連接請(qǐng)求的服務(wù)器應(yīng)該始終準(zhǔn)備處理下一個(gè)呼入的連接請(qǐng)求晃酒,這正是使用并發(fā)服務(wù)器的根本原因表牢,但仍有可能出現(xiàn)當(dāng)服務(wù)器在創(chuàng)建一個(gè)新的進(jìn)程時(shí),或操作系統(tǒng)正忙于處理優(yōu)先級(jí)更高的進(jìn)程時(shí)贝次,到達(dá)多個(gè)連接請(qǐng)求崔兴,當(dāng)服務(wù)器正處于忙時(shí),TCP是如何處理這些呼入的連接請(qǐng)求蛔翅?