1. 程式人生 > >網絡之傳輸層

網絡之傳輸層

產生 定制 inux 原因 data 分組 justify 互聯網 執行

傳輸層(transport):

傳輸層的作用是為會話層提供一個端到端的透明的數據傳輸服務。它是一個端到端的層次,為網絡體系結構中的關鍵一層。高層用戶合一直接利用傳輸層提供的服務進行端到端的數據傳輸。

在傳輸層有兩個重要的協議TCP協議和UDP協議,我們在學習中主要講解TCP協議,因為TCP協議可靠,很很多的機制保證我們的數據完整,但是UDP則不然,但是無論是那種協議,包括在上層和下層的協議,都不是安全的,在網絡中,沒有安全的協議。

我們可以看出來,在傳輸層TCP和UDP的是很重要的,雖然我們在傳輸數據的過程中,既可以使用TCP也可以使用UDP,但是,由於他們的功能不同,導致很多重要的數據傳輸都是用的TCP,而不是UDP,我們接下來要通過對他們的包頭進行分析,來說明這一點。現在,我們先看看他倆有什麽區別。

可靠性

有效性

連接方式

面向連接

非面向連接

協議

TCP

UDP

是否排序

用途

電子郵件

文件共享

下載

語音信息

視頻信息

現在我們對於TCP會詳細的介紹,對於UTP我們僅作為了解,因為,UDP的包頭信息真的很簡單。

TCP特性:

工作在傳輸層面向連接協議

全雙工協議

半關閉

錯誤檢查

將數據打包成段,排序

確認機制

數據恢復,重傳

流量控制,滑動窗口

擁塞控制,慢啟動和擁塞避免算法

接下來,我們從TCP的包頭信息開始說起:

技術分享

源端口、目標端口

計算機上的進程要和其他進程通信是要通過計算機端口的,而一個計算機端口某個時刻只能被一個進程占用,所以通過指定源端口和目標端口,就可以知道是哪兩個進程需要通信。源端口、目標端口是用16位表示的,可推算計算機的端口個數為2^16個。並且,客服端是隨機的,服務端針對於每種服務使固定的

序列號

表示本報文段所發送數據的第一個字節的編號。在TCP連接中所傳送的字節流的每一個字節都會按順序編號。由於序列號由32位表示,所以每2^32個字節,就會出現序列號回繞,再次從 0 開始。

此時就有一個問題,這樣的話,我們怎麽確定那個包在前,那個包在後呢?這就不得不說我們的選項位了,選項裏面有三種不同的功能,其中一種是時間戳,即便是我們的序號一樣,時間戳不一樣,我們包的順序就不一樣,就不會造成混亂。

確認號

表示接收方期望收到發送方下一個報文段的第一個字節數據的編號。也就是告訴發送發:我希望你(指發送方)下次發送的數據的第一個字節數據的編號是這個確認號。

數據偏移

表示TCP報文段的首部長度,共4位,由於TCP首部包含一個長度可變的選項部分,需要指定這個TCP報文段到底有多長。它指出 TCP 報文段的數據起始處距離 TCP 報文段的起始處有多遠。該字段的單位是32位(即4個字節為計算單位), 4位二進制最大表示15,所以數據偏移也就是TCP首部最大60字節。

URG

表示本報文段中發送的數據是否包含緊急數據。後面的緊急指針字段(urgent pointer)只有當URG=1時才有效

ACK:

表示是否前面的確認號字段是否有效。 ACK=1,表示有效。只有當ACK=1時,前面的確認號字段才有效。 TCP規定,連接建立後, ACK必須為1,帶ACK標誌的TCP報文段稱為確認報文段

PSH

提示接收端應用程序應該立即從TCP接收緩沖區中讀走數據,為接收後續數

據騰出空間。如果為1,則表示對方應當立即把數據提交給上層應用,而不是緩存起來,如果應用程序不將接收到的數據讀走,就會一直停留在TCP接收緩沖區中

RST

如果收到一個RST=1的報文,說明與主機的連接出現了嚴重錯誤(如主機崩

潰),必須釋放連接,然後再重新建立連接。或者說明上次發送給主機的數據有問題,主機拒絕響應,帶RST標誌的TCP報文段稱為復位報文段

SYN

在建立連接時使用,用來同步序號。當SYN=1, ACK=0時,表示這是一個請求建立連接的報文段;當SYN=1, ACK=1時,表示對方同意建立連接。 SYN=1

,說明這是一個請求建立連接或同意建立連接的報文。只有在前兩次握手中SYN才置為1,帶SYN標誌的TCP報文段稱為同步報文段

FIN

表示通知對方本端要關閉連接了,標記數據是否發送完畢。如果FIN=1,即

告訴對方:"我的數據已經發送完畢,你可以釋放連接了",帶FIN標誌的TCP報文段稱為結束報文段。

窗口大小

表示現在充許對方發送的數據量,也就是告訴對方,從本報文段的確認號開始允許對方發送的數據量

校驗和:提供額外的可靠性

緊急指針:標記緊急數據在數據字段中的位置

選項部分

其最大長度可根據TCP首部長度進行推算。 TCP首部長度用4位表示,選項部分最長為: (2^4-1)*4-20=40字節

常見選項:

最大報文段長度: Maxium Segment Size, MSS

窗口擴大: Windows Scaling

時間戳: Timestamps

1 最大報文段長度

指明自己期望對方發送TCP報文段時那個數據字段的長度。默認是536字節。數據字段的長度加上TCP首部的長度才等於整個TCP報文段的長度。 MSS不宜設的太大也不宜設的太小。若選擇太小,極端情況下, TCP報文段只含有1字節數據,在IP層傳輸的數據報的開銷至少有40字節(包括TCP報文段的首部和IP數據報的首部)。這樣,網絡的利用率就不會超過1/41。若TCP報文段非常長,那麽在IP層傳輸時就有可能要分解成多個短數據報片。在終點要把收到的各個短數據報片裝配成原來的TCP報文段。當傳輸出錯時還要進行重傳,這些也都會使開銷增大。因此MSS應盡可能大,只要在IP層傳輸時不需要再分片就行。在連接建立過程中,雙方都把自己能夠支持的MSS接入這一字段。 MSS只出現在SYN報文中。即: MSS出現在SYN=1的報文段中。

2 窗口擴大

為了擴大窗口,由於TCP首部的窗口大小字段長度是16位,所以其表示的最大數是65535。但是隨著時延和帶寬比較大的通信產生(如衛星通信),需要更大的窗口來滿足性能和吞吐率,所以產生了這個窗口擴大選項。

3 時間戳

可以用來計算RTT(往返時間),發送方發送TCP報文時,把當前的時間值放入時間戳字段,接收方收到後發送確認報文時,把這個時間戳字段的值復制到確認報文中,當發送方收到確認報文後即可計算出RTT。也可以用來防止回繞序號PAWS,也可以說可以用來區分相同序列號的不同報文。因為序列號用32為表示,每2^32個序列號就會產生回繞,那麽使用時間戳字段就很容易區分相同序列號的不同報文。

TCP協議的PORT

傳輸層通過port號,確定應用層協議

Port number:

tcp:傳輸控制協議,面向連接的協議;通信前需要建立虛擬鏈路;結束後

拆除鏈路(0-65535)

udp: User Datagram Protocol,無連接的協議(0-65535)

IANA:互聯網數字分配機構(負責域名,數字資源,協議分配)

0-1023:系統端口或特權端口(僅管理員可用) ,眾所周知,永久的分配給固定的系統應用使用, 22/tcp(ssh), 80/tcp(http), 443/tcp(https)

1024-49151:用戶端口或註冊端口,但要求並不嚴格,分配給程序註冊為某應用使用:

1433/tcp(SqlServer)

1521/tcp(oracle)

3306/tcp(mysql)

11211/tcp/udp (memcached)

49152-65535: 動態端口或私有端口,客戶端程序隨機使用的端口

其範圍的定義: /proc/sys/net/ipv4/ip_local_port_range

技術分享

TCP建立連接的三次握手

我們對TCP包頭有了了解,我們要TCP協議的作用是使用它來提供可靠地數據傳輸,那麽,如何傳輸的呢,我們接下來對TCP的連接過程和終斷過程進行學習。

技術分享

我們在傳輸或訪問過程只要使用到TCP,就會遇到上面的過程,客戶端一般情況下是關閉著的,但是服務器不是,因為服務器隨時都有可能有用戶去訪問,如果是關閉著,客戶端就有可能訪問不到。所以,服務器端就有一個監聽模式,不斷監聽是否有用戶訪問。

第一次握手

當我們訪問服務器時,客戶端會向指定的服務器發起請求,發送一個TCP報文,SYN為1和一個隨機數據包seq。

第二次握手

當我們的服務器收到TCP報文後,知道有人想要創建連接,就向源發送一個確認ACK=1,一個請求SYN=1,源數據包號+1,和一個隨機數據包seq=y。

第三次握手

當客戶機收到服務器發來的確認報文後,同樣會發送一個ACK=1,上次的隨機數據包seq+1,確認數據包y+1(服務器上次的發的seq=y),此時就算是正式建立連接,開始傳輸數據了。

一般是客戶端向服務器發起,因為客戶端端口號不是固定的,所以一般服務器不向客戶端發起連接。

惡意攻擊者發送第一次握手,而不發送第三次握手,導致服務器端不停地發送第二次握手的回應,導致資源大量消耗在惡意攻擊者上,最後可導致服務器宕機。

TCP斷開連接的四次握手

我們數據傳輸完成後就會斷開連接,TCP斷開連接的過程比建立連接的過程還要復雜,我們可以看一下他們是如何斷開連接的。

技術分享

我們以客戶端主動斷開連接為例說明四次握手的過程。

第一次握手

客戶端主動發起斷開請求FIN=1發送數據包序號u。

第二次握手

服務器端收到客戶端的斷開請求,發送確認包並繼續發送未發完的數據包v,希望收到下一個數據包u+1

第三次握手

服務器端發送確認斷開請求FIN=1,ACK=1,繼續發送數據包w

第四次握手

客戶端收到服務器端發來的確認斷開請求並發送數據包u+1,希望得到數據包W=1,然後,客戶端進入TIME-WAIT狀態,繼續接收未收完的數據包,然後關閉客戶端。

我們在上面看到,在創建連接或者斷開連接,客戶端和服務器都會有各種狀態,上面只是我們最常見的狀態,但並不是在創建或斷開連接時只有這些狀態,我們下面看看它還有其他什麽狀態。

有限狀態機

CLOSED 沒有任何連接狀態

LISTEN 偵聽狀態,等待來自遠方TCP端口的連接請求

SYN-SENT 在發送連接請求後,等待對方確認

SYN-RECEIVED 在收到和發送一個連接請求後,等待對方確認

ESTABLISHED 代表傳輸連接建立,雙方進入數據傳送狀態

FIN-WAIT-1 主動關閉,主機已發送關閉連接請求,等待對方確認

FIN-WAIT-2 主動關閉,主機已收到對方關閉傳輸連接確認,等待對方發送關閉傳輸連接請求

TIME-WAIT 完成雙向傳輸連接關閉,等待所有分組消失

CLOSE-WAIT 被動關閉,收到對方發來的關閉連接請求,並已確認

LAST-ACK 被動關閉,等待最後一個關閉傳輸連接確認,並等待所有分組消失

CLOSING 雙方同時嘗試關閉傳輸連接,等待對方確認

客戶端的典型狀態轉移

客戶端通過connect系統調用主動與服務器建立連接connect系統調用首先給服務器發送一個同步報文段,使連接轉移到SYN_SENT狀態。此後connect系統調用可能因為如下兩個原因失敗返回:

1、如果connect連接的目標端口不存在(未被任何進程監聽),或者該端口仍被處於TIME_WAIT狀態的連接所占用(見後文),則服務器將給客戶端發送一個復位報文段,connect調用失敗。

2、如果目標端口存在,但connect在超時時間內未收到服務器的確認報文段,則connect調用失敗。connect調用失敗將使連接立即返回到初始的CLOSED狀態。如果客戶端成功收到服務器的同步報文段和確認,則connect調用成功返回,連接轉移至ESTABLISHED狀態。

當客戶端執行主動關閉時,它將向服務器發送一個結束報文段,同時連接進入FIN_WAIT_1狀態。若此時客戶端收到服務器專門用於確認目的的確認報文段,則連接轉移至FIN_WAIT_2狀態。當客戶端處於FIN_WAIT_2狀態時,

服務器處於CLOSE_WAIT狀態,這一對狀態是可能發生半關閉的狀態。此時如果服務器也關閉連接(發送結束報文段),則客戶端將給予確認並進入TIME_WAIT狀態

客戶端從FIN_WAIT_1狀態可能直接進入TIME_WAIT狀態(不經過FIN_WAIT_2狀態),前提是處於FIN_WAIT_1狀態的服務器直接收到帶確認信息的結束報文段(而不是先收到確認報文段,再收到結束報文段)

處於FIN_WAIT_2狀態的客戶端需要等待服務器發送結束報文段,才能轉移至TIME_WAIT狀態,否則它將一直停留在這個狀態。如果不是為了在半關閉狀態下繼續接收數據,連接長時間地停留在FIN_WAIT_2狀態並無益處。連接停留在FIN_WAIT_2狀態的情況可能發生在:客戶端執行半關閉後,未等服務器關閉連接就強行退出了。此時客戶端連接由內核來接管,可稱之為孤兒連接(和孤兒進程類似)。

Linux為了防止孤兒連接長時間存留在內核中,定義了兩個內核參數:

/proc/sys/net/ipv4/tcp_max_orphans 指定內核能接管的孤兒連接數目

/proc/sys/net/ipv4/tcp_fin_timeout 指定孤兒連接在內核中生存的時間

客戶端先發送一個FIN給服務端,自己進入了FIN_WAIT_1狀態,這時等待接收服務端的報文,該報文會有三種可能:

只有服務端的ACK

只有服務端的FIN

基於服務端的ACK,又有FIN

1、只收到服務器的ACK,客戶端會進入FIN_WAIT_2狀態,後續當收到服務端的FIN時,回應發送一個ACK,會進入到TIME_WAIT狀態,這個狀態會持續2MSL(TCP報文段在網絡中的最大生存時間,RFC 1122標準的建議值是2min).客戶端等待2MSL,是為了當最後一個ACK丟失時,可以再發送一次。因為服務端在等待超時後會再發送一個FIN給客戶端,進而客戶端知道ACK已丟失

2、只有服務端的FIN時,回應一個ACK給服務端,進入CLOSING狀態,然後接收到服務端的ACK時,進入TIME_WAIT狀態

3、同時收到服務端的ACK和FIN,直接進入TIME_WAIT狀態

技術分享

TCP超時重傳

異常網絡狀況下(開始出現超時或丟包), TCP控制數據傳輸以保證其承諾的可靠服務

TCP服務必須能夠重傳超時時間內未收到確認的TCP報文段。為此, TCP模塊為每個TCP報文段都維護一個重傳定時器,該定時器在TCP報文段第一次被發送時啟動。如果超時時間內未收到接收方的應答, TCP模塊將重傳TCP報文段並重置定時器。至於下次重傳的超時時間如何選擇,以及最多執行多少次重傳,就是TCP的重傳策略。

與TCP超時重傳相關的兩個內核參數:

? /proc/sys/net/ipv4/tcp_retries1,指定在底層IP接管之前TCP最少執行的重傳次數,默認值是3

/proc/sys/net/ipv4/tcp_retries2,指定連接放棄前TCP最多可以執行的重傳次數,默認值15(一般對應13~30min)

什麽是窗口

我們在TCP報文中發現了一個窗口選項,那麽這個窗口是幹嘛用的呢,我們來說一下。當客戶端和服務器建立連接以後,我們開始傳輸數據,但是我們的數據如果是以下面的一種方式傳輸,即客戶端發一次數據,服務器確認一次,發一次,確認一次,這樣的話,效率是相當低的。此時的窗口數是1

技術分享

有沒有更好的數據傳輸方式呢,有,我們可以定制窗口的大小,讓客戶端一次發送很多次數據,讓服務器端確認一次,這樣就比窗口數為1的要好得多。

技術分享

但是,這樣還是有問題的,萬一我們此時的計算機資源本來就很緊張,一次發送很多的數據,在傳輸過程中發生問題該怎麽辦呢,或者我們的計算機資源很充足,但是用小窗口傳送數據就無法充分利用計算機資源,這是我們該如何呢?

其實,窗口的大小不是固定的,是根據計算機資源的具體情況變化的,計算機資源緊張時,窗口就會變得小一些,如果計算機資源充足,窗口就會開的大一些,提高效率。

技術分享

擁塞控制

TCP為提高網絡利用率,降低丟包率,並保證網絡資源對每條數據流的公平性。即所謂的擁塞控制

TCP擁塞控制的標準文檔是RFC 5681,其中詳細介紹了擁塞控制的四個部分:慢啟動(slow start)、擁塞避免(congestion avoidance)、快速重傳(fast retransmit)和快速恢復(fast recovery)。擁塞控制算法在Linux下有多種實現,比如reno算法、 vegas算法和cubic算法等。它們或者部分或者全部實現了上述四個部分

當前所使用的擁塞控制算法

/proc/sys/net/ipv4/tcp_congestion_control

TCP端口號

技術分享

TCP的序號與確認號

技術分享

UDP特性

對於UDP我們不做過多的說明,我們要知道的是UDP的一些特性:

工作在傳輸層

提供不可靠的網絡訪問

非面向連接協議

有限的錯誤檢查

傳輸性能高

無數據恢復特性

UDP包頭

技術分享

網絡之傳輸層