??TCP是一個面向連接的協(xié)議平酿。無論哪一方向另一方發(fā)送數(shù)據(jù)之前尔艇,都必須現(xiàn)在雙方之間建立一條連接桨仿。
??連接的建立與終止
??在第1行中,字段1415531521:1415531521(0)表示分組的序號是1415531521锡足,而報文段中數(shù)據(jù)字節(jié)數(shù)為0.
??tcpdump顯示這個字段的格式是開始的序號波丰、一個冒號、隱含的結尾序號及圓括號內(nèi)的數(shù)據(jù)字節(jié)數(shù)舶得。顯示序號和隱含結尾序號的優(yōu)點是便于了解數(shù)據(jù)字節(jié)數(shù)大于0時的隱含結尾序號掰烟。這個字段只有在滿足條件(1)報文段中至少包含一個數(shù)據(jù)字節(jié);或者(2)SYN沐批、FIN或RST被設置為1時才顯示纫骑。圖18-1中的第1、2九孩、4和6行是因為標志比特被置為1而顯示這個字段的先馆,在這個例子中通信雙方?jīng)]有交換任何數(shù)據(jù)。
??在第2行中躺彬,字段ack1415531522表示確認序號煤墙。它只有在首部中的ACK標志比特被設置1時才顯示梅惯。
??每行顯示的字段win4096表示發(fā)端通告的窗口大小。在這些例子中仿野,我們沒有交換任何數(shù)據(jù)铣减,窗口大小就維持默認情況下的4096(我們將在20.4節(jié)中討論TCP窗口大小)脚作。
??圖18-1中的最后一個字段<mss1024>表示由發(fā)端指明的最大報文段長度選項葫哗。發(fā)端將不接收超過這個長度的TCP報文段。這通常是為了避免分段球涛。
建立連接協(xié)議
??三次握手過程:
- 1. 請求端(通常稱為客戶)發(fā)送一個SYN段指明客戶打算連接的服務器的端口劣针,以及初始序號(ISN,在這個例子中為1415531521)亿扁。這個SYN段為報文段1捺典。
- 2. 服務器發(fā)回包含服務器的初始序號的SYN報文段(報文段2)作為應答。同時魏烫,將確認序號設置為客戶的ISN加1以對客戶的SYN報文段進行確認辣苏。一個SYN將占用一個序號。
- 3. 客戶必須將確認序號設置為服務器的ISN加1以對服務器的SYN報文段進行確認(報文段3)哄褒。
??發(fā)送第一個SYN的一端將執(zhí)行主動打開(active open)。接收這個SYN并發(fā)回下一個SYN的另一端執(zhí)行被動打開(passive open)煌张。
??當一端為建立連接而發(fā)送它的SYN時呐赡,它為連接選擇一個初始序號。ISN隨時間而變化骏融,因此每個連接都將具有不同的ISN链嘀。
連接終止協(xié)議
??4次揮手過程:
??原因: 這由TCP的半關閉(halfclose)造成的。既然一個TCP連接是全雙工(即數(shù)據(jù)在兩個方向上能同時傳遞)档玻,因此每個方向必須單獨地進行關閉怀泊。這原則就是當一方完成它的數(shù)據(jù)發(fā)送任務后就能發(fā)送一個FIN來終止這個方向連接。當一端收到一個FIN误趴,它必須通知應用層另一端幾經(jīng)終止了那個方向的數(shù)據(jù)傳送霹琼。發(fā)送FIN通常是應用層進行關閉的結果。
??收到一個FIN只意味著在這一方向上沒有數(shù)據(jù)流動凉当。一個TCP連接在收到一個FIN后仍能發(fā)送數(shù)據(jù)枣申。而這對利用半關閉的應用來說是可能的,盡管在實際應用中只有很少的TCP應用程序這樣做看杭。
首先進行關閉的一方(即發(fā)送第一個FIN)將執(zhí)行關閉,而另一方(收到這個FIN)執(zhí)行被動關閉.通常一方完成主動關閉而另一方完成被動關閉
當服務器收到這個FIN,它發(fā)回一個ACK,確認序號為收到的序號加1.和SYN一樣,一個FIN將占用一個序號.同時TCP服務器還向應用程序(即丟棄服務器)傳送一個文件結束符.接著這個服務程序就關閉它的連接,導致它的TCP端發(fā)送一個FIN,客戶端必須發(fā)回一個確認,并將確認序號設置為收到序號加1.
連接建立的超時
??有很多情況導致無法建立連接忠藤。一種情況是服務器主機沒有處于正常狀態(tài)。為了模擬這種情況楼雹,我們斷開服務器主機的電纜線模孩,然后向它發(fā)出telnet命令尖阔。圖18-6顯示了tcpdump的輸出。
??在這個輸出中有趣的一點是客戶間隔多長時間發(fā)送一個SYN榨咐,試圖建立連接介却。第2個SYN與第1個的間隔是5.8秒,而第3個與第2個的間隔是24秒祭芦。
最大報文端長度
??最大報文段長度(MSS)表示TCP傳往另一端的最大塊數(shù)據(jù)的長度.當一個連接建立時,連接的雙方都要通告各自的MSS.我們已經(jīng)見過MSS都是1024. 這導致IP數(shù)據(jù)報通常是40字節(jié):20字節(jié)的TCP首部和20字節(jié)的IP首部.
??當建立一個連接時,每一方都有用于通告它期望接受的MSS選項(MSS選項只能出現(xiàn)在SYN報文段中).如果一方不接受來自另一方的MSS值.則MSS就定位默認值536字節(jié)(這個默認值允許20字節(jié)的IP首部和20字節(jié)的TCP首部以適合576字節(jié)IP數(shù)據(jù)報)
TCP的半關閉
??半關閉: TCP提供了連接的一端在結束它的發(fā)送后還能接收來自另一端數(shù)據(jù)的能力筷笨。
??為了使用這個特性,編程接口必須為應用程序提供一種方式來說明“我已經(jīng)完成了數(shù)據(jù)傳送龟劲,因此發(fā)送一個文件結束(FIN)給另一端胃夏,但我還想接收另一端發(fā)來的數(shù)據(jù),直到它給我發(fā)來文件結束(FIN)”昌跌。
??如果應用程序不調(diào)用close而調(diào)用shutdown仰禀,且第2個參數(shù)值為1,則插口的API支持半關閉蚕愤。然而答恶,大多數(shù)的應用程序通過調(diào)用close終止兩個方向的連接。
??圖18-10顯示了一個半關閉的典型例子萍诱。
讓左方的客戶端開始半關閉悬嗓,當然也可以由另一端開始。
初始端發(fā)出的FIN裕坊,接著是另一端對這個FIN的ACK報文段
接收半關閉的一方仍能發(fā)送數(shù)據(jù)包竹。我們只顯示一個數(shù)據(jù)報文段和一個ACK報文段,但可能發(fā)送了許多數(shù)據(jù)報文段
當收到半關閉的一端在完成它的數(shù)據(jù)傳送后籍凝,將發(fā)送一個FIN關閉這個方向的連接周瞎,這將傳送一個文件結束符給發(fā)起這個半關閉的應用進程
當對第二個FIN進行確認后,這個連接便徹底關閉了饵蒂。
為什么要有半關閉?
??沒有半關閉,需要其他的一些技術讓客戶通知服務器,客戶端已經(jīng)完成了它的數(shù)據(jù)傳送,但仍要接受來自服務端的數(shù)據(jù).使用兩個TCP連接也可作為一個選擇,但使用半關閉的單連接更好.
TCP的狀態(tài)變遷圖
2MSL等待狀態(tài)
??TIME_WAIT狀態(tài)也稱為2MSL等待狀態(tài)声诸。每個具體TCP實現(xiàn)必須選擇一個報文段最大生存時間MSL(Maximum Segment Lifetime)。它是任何報文段被丟棄前在網(wǎng)絡內(nèi)的最長時間退盯。我們知道這個時間是有限的彼乌,因為TCP報文段以IP數(shù)據(jù)報在網(wǎng)絡內(nèi)傳輸,而IP數(shù)據(jù)報則有限制其生存時間的TTL字段得问。
??對一個具體實現(xiàn)所給定的MSL值囤攀,處理的原則是:當TCP執(zhí)行一個主動關閉,并發(fā)回最后一個ACK宫纬,該連接必須在TIME_WAIT狀態(tài)停留的時間為2倍的MSL焚挠。這樣可讓TCP再次發(fā)送最后的ACK以防這個ACK丟失(另一端超時并重發(fā)最后的FIN)。
?這種2MSL等待的另一個結果是這個TCP連接在2MSL等待期間,定義這個連接的插口(客戶的IP地址和端口號,服務器的IP地址和端口)不能再被使用.這個連接只能在2MSL結束后才能再被使用.
??客戶執(zhí)行主動關閉并進入TIME_WAIT是正常的漓骚。服務器通常執(zhí)行被動關閉蝌衔,不會進入TIME_WAIT狀態(tài)榛泛。這暗示如果我們終止一個客戶程序,并立即重新啟動這個客戶程序噩斟,則這個新客戶程序將不能重用相同的本地端口曹锨。這不會帶來什么問題,因為客戶使用本地端口剃允,而并不關心這個端口號是什么沛简。
??然而,對于服務器斥废,情況就有所不同椒楣,因為服務器使用熟知端口。如果我們終止一個已經(jīng)建立連接的服務器程序牡肉,并試圖立即重新啟動這個服務器程序捧灰,服務器程序將不能把它的這個熟知端口賦值給它的端點,因為那個端口是處于2MSL連接的一部分统锤。在重新啟動服務器程序前毛俏,它需要在1~4分鐘.
FIN_WAIT_2狀態(tài)
??在FIN_WAIT_2狀態(tài)我們已經(jīng)發(fā)出了FIN,并且另一端也已對它進行確認饲窿。除非我們在實行半關閉煌寇,否則將等待另一端的應用層意識到它已收到一個文件結束符說明,并向我們發(fā)一個FIN來關閉另一方向的連接逾雄。只有當另一端的進程完成這個關閉唧席,我們這端才會從FIN_WAIT_2狀態(tài)進入TIME_WAIT狀態(tài)。
這意味著我們這端可能永遠保持這個狀態(tài)嘲驾。另一端也將處于CLOSE_WAIT狀態(tài),并一直保持這個狀態(tài)直到應用層決定進行關閉迹卢。
許多伯克利實現(xiàn)采用如下方式來防止這種在FIN_WAIT_2狀態(tài)的無限等待辽故。如果執(zhí)行主動關閉的應用層將進行全關閉,而不是半關閉來說明它還想接收數(shù)據(jù)腐碱,就設置一個定時器誊垢。如果這個連接空閑10分鐘75秒,TCP將進入CLOSED狀態(tài)症见。在實現(xiàn)代碼的注釋中確認這個實現(xiàn)代碼違背協(xié)議的規(guī)范喂走。
復位報文段
??TCP首部中的RST比特是用于"復位"的.一般說來,無論何時一個報文段發(fā)往基準的連接出現(xiàn)錯誤,TCP都會出現(xiàn)一個復位報文段.
出現(xiàn)情況:
-
到不存在的端口的連接請求
?產(chǎn)生復位的一種常見情況是當連接請求到達時,目的端口沒有進程正在聽.當一個數(shù)據(jù)報到達目的端口時,該端口沒在使用,TCP使用復位
- 異常終止一個連接
?終止一個連接的正常方式是一方發(fā)送FIN.有時這也稱為有序釋放(orderly release),因為在所有排隊數(shù)據(jù)都已發(fā)送之后才發(fā)送FIN,正常情況下沒有任何數(shù)據(jù)丟失.但也有可能發(fā)送一個復位報文段而不是FIN來中途釋放一個連接.這被稱為異常釋放
?異常終止一個連接對應用程序來說有兩個優(yōu)點:(1)丟棄任何待發(fā)數(shù)據(jù)并立即發(fā)送復位報文段;(2)RST的接收方會區(qū)分另一端執(zhí)行的是異常關閉還是正常關閉.應用程序使用的API必須提供產(chǎn)生異常關閉而不是正常關閉的手段.
半打開連接
?如果一方已經(jīng)關閉或異常終止連接而另一方卻還不知道,我們將這樣的TCP連接稱為半打開(Half-Open)的.任何一端的主機異常都可能導致發(fā)送這種情況.只要不打算在半打開連接上傳輸數(shù)據(jù)谋作,仍處于連接狀態(tài)的一方就不會檢測另一方已經(jīng)出現(xiàn)異常芋肠。
?半打開連接的另一個常見原因是當客戶主機突然掉電而不是正常的結束客戶應用程序后再關機。這可能發(fā)生在使用PC機作為Telnet的客戶主機上遵蚜,例如帖池,用戶在一天工作結束時關閉PC機的電源奈惑。當關閉PC機電源時,如果已不再有要向服務器發(fā)送的數(shù)據(jù)睡汹,服務器將永遠不知道客戶程序已經(jīng)消失了肴甸。當用戶在第二天到來時,打開PC機囚巴,并啟動新的Telnet客戶程序原在,在服務器主機上會啟動一個新的服務器程序。這樣會導致服務器主機中產(chǎn)生許多半打開的TCP連接
同時打開
??TCP特意設計可以處理同時打開,對于同時打開它僅建立一條連接而不是兩條連接;
??一個同時打開的連接需要交換4個報文段,比正常的三次握手多一個.此外,我們沒有將任何一端稱為客戶或服務器,因為每一端既是客戶又是服務器.
同時關閉
??當應用層發(fā)出關閉命令時彤叉,兩端均從ESTABLISHED變?yōu)镕IN_WAIT_1庶柿。這將導致雙方各發(fā)送一個FIN,兩個FIN經(jīng)過網(wǎng)絡傳送后分別到達另一端姆坚。收到FIN后澳泵,狀態(tài)由FIN_WAIT_1變遷到CLOSING,并發(fā)送最后的ACK兼呵。當收到最后的ACK時兔辅,狀態(tài)變化為TIME_WAIT。
??同時關閉與正常關閉使用的段交換數(shù)目相同击喂。