說明:1).本文以TCP的發展歷程解析容易引起混淆,誤會的方方面面
2).本文不會貼大量的源碼,大多數是以文字形式描述,我相信文字看起來是要比代碼更輕松的
3).針對對象:對TCP已經有了全面了解的人。因為本文不會解析TCP頭裡面的每一個字段或者3次握手的細節,也不會解釋慢啟動和快速重傳的定義
4).除了《TCP/IP詳解》(卷一,卷二)以及《Unix網絡編程》以及Linux源代碼之外,絡更好的資源是RFC
5).本文給出一個提綱,如果想了解細節,請直接查閱RFC
6).翻來覆去,終於找到了這篇備忘,本文基於這篇備忘文檔修改。
1.網絡協議設計ISO提出了OSI分層網絡模型,這種分層模型是理論上的,TCP/IP最終實現了一個分層的協議模型,每一個層次對應一組網絡協議完成一組特定的功能,該組網絡協議被其下的層次復用和解復用。這就是分層模型的本質,最終所有的邏輯被編碼到線纜或者電磁波。
分層模型是很好理解的,然而對於每一層的協議設計卻不是那麼容易。TCP/IP的漂亮之處在於:協議越往上層越復雜。我們把網絡定義為互相連接在一起的設備,網絡的本質作用還是“端到端”的通信,然而希望互相通信的設備並不一定要“直接”連接在一起,因此必然需要一些中間的設備負責轉發數據,因此就把連接這些中間設備的線纜上跑的協議定義為鏈路層協議,實際上所謂鏈路其實就是始發與一個設備,通過一根線,終止於另一個設備。我們把一條鏈路稱為“一跳”。因此一個端到端的網絡包含了“很多跳”。
2.TCP和IP協議終止於IP協議,我們已經可以完成一個端到端的通信,為何還需要TCP協議?這是一個問題,理解了這個問題,我們就能理解TCP協議為何成了現在這個樣子,為何如此“復雜”,為何又如此簡單。
正如其名字所展示的那樣,TCP的作用是傳輸控制,也就是控制端到端的傳輸,那為何這種控制不在IP協議中實現的。答案很簡單,那就是這會增加IP協議的復雜性,而IP協議需要的就是簡單。這是什麼原因造成的呢?
首先我們認識一下為何IP協議是沙漏的細腰部分。它的下層是繁多的鏈路層協議,這些鏈路提供了相互截然不同且相差很遠的語義,為了互聯這些異構的網絡,我們需要一個網絡層協議起碼要提供一些適配的功能,另外它必然不能提供太多的“保證性服務”,因為上層的保證性依賴下層的約束性更強的保證性,你永遠無法在一個100M吞吐量的鏈路之上實現的IP協議保證1000M的吞吐量...
IP協議設計為分組轉發協議,每一跳都要經過一個中間節點,路由的設計是TCP/IP網絡的另一大創舉,這樣,IP協議就無需方向性,路由信息和協議本身不再強關聯,它們僅僅通過IP地址來關聯,因此,IP協議更加簡單。路由器作為中間節點也不能太復雜,這涉及到成本問題,因此路由器只負責選路以及轉發數據包。
因此傳輸控制協議必然需要在端點實現。在我們詳談TCP協議之前,首先要看一下它不能做什麼,由於IP協議不提供保證,TCP也不能提供依賴於IP下層鏈路的這種保證,比如帶寬,比如時延,這些都是鏈路層決定的,既然IP協議無法修補,TCP也不能,然而它卻能修正始於IP層的一些“不可保證性質”,這些性質包括IP層的不可靠,IP層的不按順序,IP層的無方向/無連接。
將該小節總結一下,TCP/IP模型從下往上,功能增加,需要實現的設備減少,然而設備的復雜性卻在增加,這樣保證了成本的最小化,至於性能或者因素,靠軟件來調節吧,TCP協議就是這樣的軟件,實際上最開始的時候,TCP並不考慮性能,效率,公平性,正是考慮了這些,TCP協議才復雜了起來。
3.TCP協議這是一個純軟件協議,為何將其設計上兩個端點,參見上一小節,本節詳述TCP協議,中間也穿插一些簡短的論述。
3.1.TCP協議確切的說,TCP協議有兩重身份,作為網絡協議,它彌補了IP協議盡力而為服務的不足,實現了有連接,可靠傳輸,報文按序到達。作為一個主機軟件,它和UDP以及左右的傳輸層協議隔離了主機服務和網絡,它們可以被看做是一個多路復用/解復用器,將諸多的主機進程數據復用/解復用到IP層。可以看出,不管從哪個角度,TCP都作為一個接口存在,作為網絡協議,它和對端的TCP接口,實現TCP的控制邏輯,作為多路復用/解復用器,它和下層IP協議接口,實現協議棧的功能,而這正是分層網絡協議模型的基本定義(兩類接口,一類和下層接口,另一類和對等層接口)。
我們習慣於將TCP作為協議棧的最頂端,而不把應用層協議當成協議棧的一部分,這部分是因為應用層被TCP/UDP解復用了之後,呈現出了一種太復雜的局面,應用層協議用一種不同截然不同的方式被解釋,應用層協議習慣於用類似ASN.1標准來封裝,這正體現了TCP協議作為多路復用/解復用器的重要性,由於直接和應用接口,它可以很容易直接被應用控制,實現不同的傳輸控制策略,這也是TCP被設計到離應用不太遠的地方的原因之一。
總之,TCP要點有四,一曰有連接,二曰可靠傳輸,三曰數據按照到達,四曰端到端流量控制。注意,TCP被設計時只保證這四點,此時它雖然也有些問題,然而很簡單,然而更大的問題很快呈現出來,使之不得不考慮和IP網絡相關的東西,比如公平性,效率,因此增加了擁塞控制,這樣TCP就成了現在這個樣子。
3.2.有連接,可靠傳輸,數據按序到達的TCPIP協議是沒有方向的,數據報傳輸能到達對端全靠路由,因此它是一跳一跳地到達對端的,只要有一跳沒有到達對端的路由,那麼數據傳輸將失敗,其實路由也是互聯網的核心之一,實際上IP層提供的核心基本功能有兩點,第一點是地址管理,第二點就是路由選路。TCP利用了IP路由這個簡單的功能,因此TCP不必考慮選路,這又一個它被設計成端到端協議的原因。
既然IP已經能盡力讓單獨的數據報到達對端,那麼TCP就可以在這種盡力而為的網絡上實現其它的更加嚴格的控制功能。TCP給無連接的IP網絡通信增加了連接性,確認了已經發送出去的數據的狀態,並且保證了數據的順序。
3.2.1.有連接這是TCP的基本,因為後續的傳輸的可靠性以及數據順序性都依賴於一條連接,這是最簡單的實現方式,因此TCP被設計成一種基於流的協議,既然TCP需要事先建立連接,之後傳輸多少數據就無所謂了,只要是同一連接的數據能識別出來即可。
疑難雜症1:3次握手和4次揮手
TCP使用3次握手建立一條連接,該握手初始化了傳輸可靠性以及數據順序性必要的信息,這些信息包括兩個方向的初始序列號,確認號由初始序列號生成,使用3次握手是因為3次握手已經准備好了傳輸可靠性以及數據順序性所必要的信息,該握手的第3次實際上並不是需要單獨傳輸的,完全可以和數據一起傳輸。
TCP使用4次揮手拆除一條連接,為何需要4次呢?因為TCP是一個全雙工協議,必須單獨拆除每一條信道。注意,4次揮手和3次握手的意義是不同的,很多人都會問為何建立連接是3次握手,而拆除連接是4次揮手。3次握手的目的很簡單,就是分配資源,初始化序列號,這時還不涉及數據傳輸,3次就足夠做到這個了,而4次揮手的目的是終止數據傳輸,並回收資源,此時兩個端點兩個方向的序列號已經沒有了任何關系,必須等待兩方向都沒有數據傳輸時才能拆除虛鏈路,不像初始化時那麼簡單,發現SYN標志就初始化一個序列號並確認SYN的序列號。因此必須單獨分別在一個方向上終止該方向的數據傳輸。
疑難雜症2:TIME_WAIT狀態
為何要有這個狀態,原因很簡單,那就是每次建立連接的時候序列號都是隨機產生的,並且這個序列號是32位的,會回繞。現在我來解釋這和TIME_WAIT有什麼關系。
任何的TCP分段都要在盡力而為的IP網絡上傳輸,中間的路由器可能會隨意的緩存任何的IP數據報,它並不管這個IP數據報上被承載的是什麼數據,然而根據經驗和互聯網的大小,一個IP數據報最多存活MSL(這是根據地球表面積,電磁波在各種介質中的傳輸速率以及IP協議的TTL等綜合推算出來的,如果在火星上,這個MSL會大得多...)。
現在我們考慮終止連接時的被動方發送了一個FIN,然後主動方回復了一個ACK,然而這個ACK可能會丟失,這會造成被動方重發FIN,這個FIN可能會在互聯網上存活MSL。
如果沒有TIME_WAIT的話,假設連接1已經斷開,然而其被動方最後重發的那個FIN(或者FIN之前發送的任何TCP分段)還在網絡上,然而連接2重用了連接1的所有的5元素(源IP,目的IP,TCP,源端口,目的端口),剛剛將建立好連接,連接1遲到的FIN到達了,這個FIN將以比較低但是確實可能的概率終止掉連接2.
為何說是概率比較低呢?這涉及到一個匹配問題,遲到的FIN分段的序列號必須落在連接2的一方的期望序列號范圍之內。雖然這種巧合很少發生,但確實會發生,畢竟初始序列號是隨機產生了。因此終止連接的主動方必須在接受了被動方且回復了ACK之後等待2*MSL時間才能進入CLOSE狀態,之所以乘以2是因為這是保守的算法,最壞情況下,針對被動方的ACK在以最長路線(經歷一個MSL)經過互聯網馬上到達被動方時丟失。
為了應對這個問題,RFC793對初始序列號的生成有個建議,那就是設定一個基准,在這個基准之上搞隨機,這個基准就是時間,我們知道時間是單調遞增的。然而這仍然有問題,那