傳輸層的重要功能是爲運行在不一樣的主機上的進程提供通訊服務,事實上這是基於網絡層爲不一樣的主機提供通訊服務的基礎之上實現的。緩存
同時,某些傳輸層協議,例如TCP,則致力於解決在易於丟失和損壞數據的傳輸介質之上實現可靠的數據傳輸,以及如何控制傳輸速度以防止網絡擁塞或者從中恢復。安全
在發送端,傳輸層從應用層獲取數據並將它們分塊,爲每一個數據塊添加一個頭部造成一個Segment。以後將Segment遞交給網絡層,網絡層對它進行封裝並將其發送到接收端,在中間的傳輸過程當中並不會碰Segment中的內容。最後接收端的傳輸層獲取Segment並將其中的數據遞交給應用層。服務器
傳輸層是基於網絡層實現的,所以傳輸層能提供的服務每每會受到網絡層能提供的功能的限制。好比,網絡層不能保證傳輸數據的帶寬和延遲,那麼傳輸層一樣不能保證傳輸數據的帶寬和延遲。可是傳輸層也能提供網絡層所不具有的功能,例如可靠的數據傳輸以及對傳輸數據進行加密。其實,對於沒法實現的功能是網絡自己的問題引發的,和分層無關,而傳輸層實現網絡層不具有的功能,也只是由於這些功能在傳輸層實現更合適而已。網絡
擁塞控制不是爲調用TCP的應用提供的服務而是一個有益於全局的服務。擁塞控制防止了單個鏈接用巨大的流量淹沒其流經的鏈路和路由器,從而能讓全部鏈接都共享擁塞的鏈路,這是經過控制發送端的發送速度實現的。可是UDP並無該限制,使用UDP的應用能夠用任何速度發送流量而且能夠持續任意長的時間。less
端口是傳輸層在Demultiplexing Segment時做爲依據的標示,端口號從0~65535,其中0~1023是衆所周知的端口。通常服務器端須要本身綁定端口而客戶端則系統會默認分配一個端口。性能
對於UDP,一個Socket由(目標IP,目標端口)這樣的二元組進行標示,只要兩個Segment的目標IP和目標端口相等,即便源IP和源端口不相同,也會被髮往目標IP所在主機的同一目標進程。加密
對於TCP,一個Socket由(源IP,源端口,目標IP,目標端口)這樣的四元組進行標示。所以對於兩個源IP或源端口不一樣的TCP Segment,它們會被髮往不一樣的Socket(除了最開始攜帶創建鏈接請求的TCP Segment之外)。操作系統
TCP Socket建立的過程以下所示:設計
1)當Server首先會建立一個「Welcoming Socket」,例如在端口12000等待來自Client的創建鏈接請求進程
2)Client建立Socket而且向Server發送一個創建鏈接請求,而創建鏈接請求無非是一個Header中的Connection-Establishment Bit設置爲true的TCP Segment
3)Server端的操做系統接收到2)中建立的創建鏈接請求,根據目的端口,將請求轉發至Server,Server根據四元組建立一個Socket,以後全部包含該四元組的Segment都會發往該Socket
UDP是最爲簡單的傳輸層協議,除了Multiplexing/Demultiplexing以及一些Error checking以外,它並無在IP之上添加任何其餘功能,值得注意的是UDP在發送Segment以前,UDP的接收方和發送方並無握手的過程,所以UDP是Connectionless。既然TCP能提供可靠的數據傳輸服務,爲何還會有應用使用UDP?由於有的應用更適合UDP,緣由以下:
1)能夠在應用層面對發送什麼數據以及什麼時候發送有更好的控制,在傳輸層使用UDP而且在應用裏實現其餘必要的功能
2)沒有鏈接創建的過程,由於創建鏈接會引入很大的延時,這也是DNS使用UDP而不是TCP的緣由
3)UDP不須要維護鏈接狀態,所以一樣的Server,UDP可以支持更多的Client
4)更小的頭部Overhead,TCP的頭部是20字節,UDP的頭部是8字節
多媒體應用對於TCP的擁塞控制機制並不適應,所以通常更傾向於使用UDP。可是隨着丟包率變低以及一些組織由於安全緣由屏蔽UDP流量時,對於多媒體應用TCP將變得更有吸引力。
基於UDP的多媒體應用是一直存在爭議的,由於沒有擁塞控制的UDP協議可能致使很是高的丟包率而且對於有擁塞控制的TCP是不公平的。
UDP的頭部僅包含四個字段:源端口,目標端口,長度(包括頭部和負載部分)以及校驗碼。
雖然有的二層協議(包括最流行的Ethernet)會提供校驗,可是IP協議可能運行於任何二層協議之上,所以UDP依舊須要提供校驗。
可靠的數據傳輸事實上是指將數據無損地,不丟失地,按序地從發送發傳輸到接收方,爲了達到這一目的,事實上不只要傳輸有效負載,還應該傳輸一系列的控制數據。
此時咱們的傳輸協議須要三種能力:
1. Error Detection:經過校驗碼檢驗數據在傳輸過程當中是否發生錯誤
2. Receiver Feedback:接收方須要給發送方反饋是否正確地收到信息,對於當前狀況,只需返回接收成功(ACK)或者接收不成功(NAK)
3. Retransmission:對於接收方發現錯誤的數據,發送方要可以重傳
可是咱們忽略了接收方放回的ACK和NAK也存在損壞的可能,對於這一點,咱們能夠引入校驗碼並從中恢復出正確的信息,另外咱們也能夠在接收到NAK或者損壞的反饋信息時都進行重傳。可是若是接收方返回的是ACK,可是損壞了,而發送方又進行重傳了,則接收方會收到重複的信息。此時咱們就須要爲發送數據進行編號,從而防止重複。
事實上,咱們徹底不須要NAK,咱們只須要在ACK以後增長一個編號便可,若接收方接收到符合要求的數據,則ACK對應數據的序號,不然返回上一次正確接收到的數據的序號便可。
經過超時重傳便可解決該問題,可是超時的時間難以肯定,通常該時間至少爲Round-Trip Delay,不過這實際上也是一個在動態變化的數字,發送者一般須要本身對超時時間進行預估,雖然在預估的時間到達之後仍是不能保證發送的數據或者對應的ACK已經丟失了。
若是基於發送數據並等待ACK以後再發送的Stop-And-Wait方式,則數據的傳輸效率會很低,事實上徹底能夠並行發送多個數據從而提高效率。但此時數據的Range of Sequence Numbers須要增長,同時發送者和接收者須要對數據進行緩存,特別是接收者的緩存會和具體的策略有關。
GBN其實就是一個滑動窗口協議,窗口的大小爲N,而流量控制和擁塞控制是限制窗口大小的緣由,對於發送方來講有如下事件須要處理:
1. 上層須要發送數據:若是窗口未滿,則直接發送數據,不然緩存數據或者利用同步機制在窗口有空閒時通知上層
2. 接收到ACK:GBN採用累計確認機制,當收到數據N的ACK時,則表示N以前的數據都已被確認
3. 超時:GBN使用一個統一的時鐘,若發生超時,則從新發送全部已發送而未ACK的數據,若收到了一個ACK,則重啓時鐘,若沒有已發送而未ACK的數據,則時鐘中止
接收方若收到序號爲N的數據,若是以前的數據都已被接收,則將數據傳輸至上層並返回ACK,若N以前有數據未收到,則直接丟棄序號爲N的數據,由於N以前的數據若是丟失了,根據GBN,丟失數據以後的數據都會被重傳。可是這樣作的缺點是,重傳的數據也可能丟失從而再次須要重傳。總之,這種已經收到正確傳輸的數據可是又丟棄的行爲是一種浪費。
在GBN中,若是N和帶寬都很大,那麼將有不少數據存在於信道中,可是其設計機制決定了可能由於一個數據的錯誤致使大量的重傳,從而可能產生性能問題。
爲了不GBN的性能問題,SR會對正確但提早收到的數據進行緩存並返回相應的ACK,發送方根據接收到的ACK對相應的數據進行確認,當確認的數據可以鏈接在一塊兒時就對窗口進行滑動。在SR中一樣具備超時機制,可是時鐘是和每一個數據匹配的,一個時鐘超時只會重傳對應的數據。
因爲數據編號的範圍是有限的,當滑動窗口的大小大於數據編號的範圍時,接收方就會沒法區分收到的數據是新的數據仍是重傳的數據。例如數據編號的範圍爲0,1,2,3,窗口大小爲3,開始發送0,1,2三個數據,且接收方全都收到並分別做出應答,若發送方成功接收ACK,則會繼續發送3,0,1,若發送方未接收到ACK,則重傳0,1,2,由此接收方徹底沒法判斷,以後發來的數據0和數據1未新發的數據仍是重傳的數據。
若窗口大小小於數據編號的二分之一,則在最極端的狀況下,接收方指望接收的數據範圍和重傳的數據範圍也永遠不會重合。
窗口大小問題對於GBN一樣存在,可是它只要求窗口大小不和數據編號的範圍相等便可。若是二者相等且接收方的ACK所有丟失,發送方會不斷重傳,可是接收方仍然可以所有按序接收。
TCP是一個面向鏈接的協議,所謂的面向鏈接就是指兩個位於不一樣主機的進程在通訊以前先要進行「握手」 ,而所謂的握手是指在傳輸數據以前先進行一系列的交互用於創建確保傳輸正常進行的參數。做爲創建鏈接的一部分,鏈接的兩端都會初始化一些TCP狀態參數。
TCP鏈接的每一端都包含發送緩存和接收緩存以及一系列的參數,TCP從緩存中獲取數據,構建一個TCP Segment,每一個Segment中應用數據的大小不能超過Maximum Segment Size(MSS),MSS是由MTU決定的,以太網的MTU爲1500,減去IP和TCP的頭部各20,最終MSS爲1460。
TCP的頭部由以下幾部分構成:
1. 16位的源端口和目的端口
2. 32位的Sequence Number和Acknowledgment Number,做爲字節流的編號,用於可靠的數據傳輸
3. 4位的頭部長度,TCP的頭部理論上有可擴展的部分,可是通常都不用,正常狀況下都是20字節
4. Flag字段:ACK位設置,代表Acknowledgment Number字段是有效的,說明有相應的Segment被確認
RST,SYN,FIN用於鏈接的創建和關閉
CWR和ECE主要用於顯式的擁塞控制,PSH代表接收方須要馬上將數據交給上層,URG表示發送方的應用層將數據標記爲"Urgent",緊急數據的起始位置由16位的Urgent Data Pointer字段決定。不過通常PSH,URG以及Urgent Data Pointer都不會用到。
TCP對字節流中的每一個字節而非每一個Segment進行編號,其中Sequence Number是傳輸的字節塊中第一個字節的編號,而Acknowledgment Number則是發送方指望從接收方獲取的下一個字節的編號。例如,B從A獲取到了字節0~574的Segment,則其中的Sequence Number爲0,而A發往B的ACK中的Acknowledgment Number則爲575.
TCP採用累計確認的方式,例如A已經收到從B發來的0~535以及900~1000,所以A還在等待來自B的536~899,因此A發往B的下一個Segment的Acknowledgment Number爲536。同時對於Out-Of-Order的Segment,TCP的RFC並無明肯定義處理方式,通常爲了節省帶寬,將它緩存起來是更好的選擇。
事實上,鏈接雙方對於初始的Sequence Number都是隨機選擇的,從而避免以前徹底相同的鏈接(一樣的IP和端口)在網絡中遺留的Segment對現有鏈接的影響。
TCP並不會對每一個Segment的RTT進行測量,它通常每一個RTT就只會測量一個Segment,並且對於重傳的Segment也會不會測量。通常對於RTT的預測公式爲:
EstimatedRTT = (1 - a) * EstimatedRTT + a * SampleRTT
上述公式代表,TCP更關注當前的SampleRTT,而非以往的Sample。
DevRTT用來測量RTT的波動狀況,其生成公式以下:
DevRTT = (1 - b) * DevRTT + b * | SampleRTT - EstimatedRTT |
所以,重傳的超時時間爲:
TimeoutInterval = EstimatedRTT + 4 * DevRTT
TimeoutInterval初始化爲1s,在第一次超時發生時會進行翻倍從而防止過早的超時,由於下一個Segment可能很快就會被ACK。當接收到第一個Segment的時候,EstimatedRTT就會更新,TimeoutInterval就會根據上述公式計算。
爲每個傳輸但還未ACK的Segment配置一個時鐘理論上是可行的,可是實際上會形成極大的Overhead,所以TCP僅會使用單個的時鐘 ,通常該時鐘和Oldest Unacknowledged Segment相綁定。
TCP的接收方收到失序的Segment會將它緩存而且ACK第一個按序未被接收的Sequence Number,若是接收者收到三個及以上的Oldest Unacknowledged Sequence Number的ACK,即便還未超時,也要從新發送Oldest Unacknowledged Sequence Number。事實上,當TCP的接收者在收到一個按序的Segment以後,並不會當即發送相應的ACK,而是會等待500msec以檢測可以進行累計確認。
TCP鏈接的兩端都有一個Receive Buffer用於緩存接收到的數據,可是因爲應用不必定會及時處理這些數據,所以咱們須要有一種機制來匹配Receive Buffer的空閒狀況和發送者的發送速率。
Flow Control會讓TCP的發送方維護一個Receive Window,該窗口表示了接收方可用緩存的大小,通常該值都是經過接收者發送給發送者的Segment中的Receive Window字段決定的。
可能出現的一種狀況是,接收方的緩存已滿,此時發送方獲取的Receive WIndow大小爲0,發送者將永遠再也不向接收者發送數據,可是接收方早晚會清空緩存,而接收者以後並不會通知發送者Receive Window已經不爲0了,此時雙方的通訊將永遠陷入停滯。所以TCP要求當發送者即便在Receive Window爲0的狀況下,也要持續向接收者發送Segment,其中包含一個字節,從而在接收者的緩存不爲0時得以通知發送者。
UDP中並無這種機制,它以後將數據簡單地放到緩存中,應用程序會一次性讀取緩存中的數據,若是讀取不及時,則緩存就會溢出,就會有Segment被丟棄。
TCP創建鏈接的過程分爲如下三步:
1. Client向Server發送一個Segment,其中Flag中的SYN設置爲1,隨機選取一個Client Sequence Number,咱們將這個Segment稱爲SYN Segment
---> Client從"CLOSED"狀態轉換爲"SYN_SENT"狀態
2. Server獲取SYN Segment,爲鏈接分配緩存以及初始化一系列的參數(這是形成SYN Flooding攻擊的緣由),再發送給Client一個Segment,SYN和ACK設置爲true,隨機選擇一個Server Sequence Number,ACK Number設置爲Client Sequence Number加一,咱們將這個Segment稱爲SYN-ACK Segment
---> Server開始監聽時,狀態從"CLOSED"轉換爲"LISTEN",在步驟2以後,則切換爲"SYN_RCVD"
3. Client收到SYN-ACK Segment,一樣爲鏈接分配緩存以及初始化一系列的參數,以後Client發送給Server一個Segment,ACK設置爲true,ACK Number爲Server Sequence Number + 1,SYN設置爲false,能夠攜帶數據。由此完成三次握手,鏈接創建
---> Client從"SYN_SENT"狀態轉換爲"ESTABLISHED"狀態,Server從"SYN_RCVD"切換爲"ESTABLISHED"
最後,鏈接雙方均可以結束鏈接,假設Client發起斷開鏈接則:
1. Client向Server發送一個特殊的Segment,它的FIN設置爲true
---> Client狀態從"ESTABLISHED"切換爲"FIN_WAIT_1"
2. Server收到該Segment回覆一個ACK Segment
---> Server從"ESTABLISHED"切換爲"CLOSE_WAIT",Client狀態從"FIN_WAIT_1"切換爲"FIN_WAIT_2"
3. Server也會向Client發送FIN Segment
---> Server從"CLOSE_WAIT"切換爲"LAST_ACK"
4. Client收到Segment並ACK,鏈接結束
---> Client狀態從"FIN_WAIT_2"切換爲"TIME_WAIT"狀態,該狀態因實現而已,會持續30s,1min或者2min,從而容許在最後一個ACK丟失以後進行重傳,Server從"LAST_ACK"切換爲"CLOSED"
若是Server並無在相應的端口進行監聽,則Server在接收到Client的SYN Segment以後會直接返回一個特殊的Segment,它的RST位設置爲true。對於UDP的話,則會返回一個特殊的ICMP Datagram
SYN Cookie能夠用於解決SYN Flood Attack,當Server收到SYN Segment時不會當即進行分配內存等初始化操做,而是基於源,目的IP和端口以及Server獨有的Secret建立初始的Server Sequence Number,若以後獲取到的對應的ACK的ACK Number可以經過驗證(驗證方法即用一樣的方法基於源,目的IP和端口生成一個Number,判斷該Number是否爲ACK Number - 1),則Server再分配緩存以及進行相應的初始化工做。