任何一方都可以關閉一個TCP連接,要求雙方發送一個FIN信號關閉自己的通訊頻道。一方可以在另 一方之前關閉,或者雙方同時關閉TCP連接。因此,當一方發送一個FIN信號時,另一方可發送“FIN+ACK”,開始關閉自己一方的通信並且確認收到了 第一個FIN信號。發送第一個FIN信號的人接下來再發送一個“FIN+ACK”信息,確認收到第二個FIN信號。另一方就知道這個連接已經關閉了,並且 關閉了自己的連接。發送第一個FIN的人沒有辦法收到最後一個ACK信號的確認信息。這時它會進入“TIME_WAIT”(等待時間)狀態並啓動一個定時 器,防止另一方沒有收到ACK信息並且認爲連接仍是打開的。一般來說,這個狀態會持續1至2分鐘。
現在,我們來討論第一個問題。如果有人(假如一 個黑客)在你的Web服務器上留下一個半開或者半關的連接,那就是一個壞消息。每一個連接都要消耗內存,打開數千個虛假的TCP連接可能導致服務器癱瘓。 當然,你實際上不可能在不影響TCP正常工作的情況下調整TCP定時器。如果你聽說過TCP SYN 攻擊的話,那就是這個意思。爲了防止出現這種情況,大多數操作系統都要限制半開連接的數量。例如,Linux默認的限制一般是256個。
關於持續 流控制問題,現在我們就來討論這個問題。TCP中實現它的機制是TCP滑動窗口機制。TCP協議使用“重新發送與正向ACK”來保證數據傳輸的可靠性。發 送方將等待一段時間,如果沒有收到其發送的數據包的ACK確認信息,發送方就要重新發送。順便說一下,TCP協議中有許多定時器。這只是其中一個定時器。 ACK的概念對於流控制是非常重要的,因爲TCP滑動窗口協議使TCP的往復確認變得更有效率。如果TCP要發送一個數據包並且等待每一個ACK確認信 息,它實際上就把數據吞吐量削減了一半。理想的情況是,我們能夠一次發送許多數據包,然後等待收到一個確認收到全部數據包的ACK信息,而不用對
方發來更多的數據。但是,我們如何知道發送了多少個數據包呢?TCP窗口尺寸可以控制在“已發送但是沒有確認”的狀態下能夠容納多少個數據包。(也就是說,只要自己也就是發送方的窗口足夠大,不用管對方是否發送確認ack過來,先提前將數據包放到發送窗口中即可) 如果這個窗 口尺寸很大,我們不必等待ACK信息就可以發送大量的數據包。這實際上就是流控制。
關於TCP流控制的問題,我們不能不提一下Nagle算法。如果我們在一個telnet連接上使用一 個大的TCP窗口會發生什麼事情呢?你會輸入一個指令(例如敲了一個字母),然後一直等待迴應它卻遲遲不出現在終端回顯上。這對於實時通信來說是一個大問 題。而且,telnet也會增加網絡的阻塞度,因爲一個字節的數據(例如我們的一次擊鍵)需要40個字節的包頭。於是RFC 896定義這個Nagle算法,用以消除小的數據包。這個思路是我們應該在數據發送之前給先把小數據集中起來然後一次性發送,以便提高效率。爲了更有效 率,它還限定只允許存在一個未經確認的數據段,你在得到確認信息之前不能發送更多的數據。Telnet和互動SSH連接使用TCP_NODELAY套接口 選項啓用這個功能,這樣當你按下一個按鍵的時候,你能夠立即得到一個迴應。
當然,我們仍是忽略了有關TCP協議的許多事情。然而,通過這兩篇文章的瞭解,你應該能夠理解其它一些更專業的TCP著作。阻塞控制與流控制不同,本文沒有討論。如果你真的對了解TCP協議的全部工作原理感興趣,你可以詳細閱讀TCP RFC。
小結
TCP 協議非常善於解決流控制問題,因此非常適應於許多應用程序。TCP協議中的流控制的含義是:“在收到對發送的數據的確認信息這前,我可以發送多少數據?” 這就是TCP窗口。學習阻塞控制的問題可以留作讀者的練習。需要指出的是,在TCP協議之下連接速度開始很慢,然後速度逐漸加快。這個做法並不總是最理想 的。