從本節開始,我們開始學習最重要的傳輸層。傳輸層位于OSI七層模型的第四層(從下往上)。顧名思義,傳輸層的作用是實現應用程序間的通信。網絡層的作用是保證數據在不同數據鏈路上傳輸的可達性,至于如何傳輸則是由傳輸層負責。
一、傳輸層協議簡介
常見的傳輸層協議主要有TCP和UDP協議。
- TCP,TCP是面向有連接的協議,也就是說在使用TCP協議傳輸數據之前,一定要在發送發和接收方建立連接。一般情況下建立連接需要三步,關閉連接需要四步。
建立TCP連接后,由于有數據重傳,流量控制等功能,TCP能夠正確處理丟包問題,保證接收方能夠收到數據,與此同時還能有效利用帶寬。然而TCP中定義了很多復雜的規范,因此效率不如UDP,所以不適合實時的視頻、音頻傳輸。 - UDP
- 無連接,UDP是面向無連接的協議。不需要建立連接。
- 盡最大努力交付,它只會把數據發送給接收端,但是不關心接收端是否收到數據。但是這種特性反而適合多播,實時的視頻和音頻傳輸,因為個別數據包的丟失并不會影響視頻和音頻的整體效果。
- 面向報文,對應用層的數據既不合并,也不拆分,而是直接在應用層數據基礎上直接拼接udp首部,傳給網絡層(ip所在層)。
- 關鍵要素
IP協議中的兩大關鍵要素是源IP地址和目標IP地址。而傳輸層我們剛剛說過,作用是實現應用程序間的通信。因此傳輸層的協議中新增三個要素:源端口號、目標端口號和協議號。通過這五個要素可以唯一識別一個通信。
不同的端口號,用于區分同一主機上的不同應用程序。
協議號用于區分使用的是TCP還是UDP。
用一句話概括,“源IP地址、目標IP地址、源端口號、目標端口號、協議號”這五個信息只要有一個不同,就被認為是不同的通信。
二、UDP首部
UDP協議最大的特點就是簡單,UDP首部如圖:
- 包長度,表示UDP首部和UDP數據長度之和。
-
檢驗和用于判斷數據在傳輸過程中是否有損壞。計算這個校驗和的時候,不僅要考慮源端口號、目標端口號,還要考慮IP首部中的源IP地址、目標IP地址、協議號(這些又稱為UDP偽首部)。這是因為以上五個要素是識別通信時缺一不可,如果校驗和只考慮端口號,那么另外三個要素在受到破壞時,應用就無法得知。這可能導致不該收到包的應用收到了包,該收到包的應用沒有收到。
這個概念同樣適合于下面的TCP首部。
三、TCP首部
和UDP首部相比,TCP首部要復雜的多。解析這個首部的時間也會相應的增加,這也是TCP連接的效率低于UDP的原因之一。
- 序列號,它表示發送數據的位置,假設當前的序列號為s,發送的數據長度為l,則下次發送數據時的序列號為s+l。在建立連接時,通常由計算機隨機生成個數作為序列號的初始值。
- 確認應答號,它等于下一次應該收到的數據的序列號。比如發送端的序列號為s,數據長度為l,則接收端返回的確認應答號也應為s+l。發送端接收到這個確認應答后,可以認為這個位置以前的所有數據已經被正常接收。
- 數據偏移,TCP首部的長度,單位為4字節。如果沒有可選字段,那么這個值就是5,表示TCP首部的長度為20字節。
- 控制位,該字段有8比特,分別有8個控制標志。依次是 CWR,ECE,URG,ACK,PSH,RST,SYN 和 FIN。在后續的文章中會接觸到其中的幾個。
- 窗口大小,用于表示從應答號開始能夠接受多少個8位字節。如果窗口大小為0,可以發送窗口探測。
- 緊急指針,僅在URG控制位是1時有效。表示緊急數據的末尾在TCP數據部分中的位置。通常在暫時中斷通信時使用。
四、TCP握手
TCP是面向有連接的協議,連接在每次通信前被建立,通信結束后被關閉。了解連接建立和關閉的過程通常是考察的重點。連接的建立和關閉可以用一張圖來表示:
通常情況下我們認為客戶端首先發起連接請求。
1. 三次握手
1.發送端發送一個SYN=1,ACK=0標志的數據包給接收端,請求進行連接,這是第一次握手;
2.接收端收到請求并且允許連接的話,就會發送一個SYN=1,ACK=1標志的數據包給發送端,告訴它,可以通訊了,并且讓發送端發送一個確認數據包,這是第二次握手;
3.最后,發送端發送一個SYN=0,ACK=1的數據包給接收端,告訴它連接已被確認,這就是第三次握手。之后,一個TCP連接建立,開始通訊。
*SYN:同步標志
同步序列編號(Synchronize Sequence Numbers)欄有效。該標志僅在三次握手建立TCP連接時有效。它提示TCP連接的服務端檢查序列編號,該序列編號為TCP連接初始端(一般是客戶端)的初始序列編號。在這里,可以把TCP序列編號看作是一個范圍從0到4,294,967,295的32位計數器。通過TCP連接交換的數據中每一個字節都經過序列編號。在TCP報頭中的序列編號欄包括了TCP分段中第一個字節的序列編號。
*ACK:確認標志
確認編號(Acknowledgement Number)欄有效。大多數情況下該標志位是置位的。TCP報頭內的確認編號欄內包含的確認編號(w+1,Figure-1)為下一個預期的序列編號,同時提示遠端系統已經成功接收所有數據。
*RST:復位標志
復位標志有效。用于復位相應的TCP連接。
*URG:緊急標志
緊急(The urgent pointer) 標志有效。緊急標志置位,
*PSH:推標志
該標志置位時,接收端不將該數據進行隊列處理,而是盡可能快將數據轉由應用處理。在處理 telnet 或 rlogin 等交互模式的連接時,該標志總是置位的。
*FIN:結束標志
帶有該標志置位的數據包用來結束一個TCP回話,但對應端口仍處于開放狀態,準備接收后續數據
2.為什么是三次握手
根據一般思路,我們認為第三次是多余的,TCP協議為什么還要增加第三次的握手呢?
這是因為在網絡請求的時候,我們應該時刻記住“網絡是不安全的,數據包是可能丟失的”。假設沒有第三次確認,客戶端向服務端發送了SYN包,請求建立連接。由于網絡原因,服務器沒有及時收到這個包,于是客戶端重新發送了SYN包。正常建立了連接。此時超時的那個確認包到達了服務端,如果是兩次握手此連接就建立了,服務端就建立了一個空連接,白白浪費資源。如果是三次,客戶端判斷這個確認包是無效的,就丟棄了。
3. 第三次握手的ACK包丟失
三次握手實際其實解決了第二步丟包問題。那么第三步的ACK包丟失了,TCP協議是如何處理的呢?
按照TCP協議處理丟包問題的一般方法,服務器會重新向客戶端發送確認包,知道ACK確認為止。但實際上這種做法有可能遭到SYN泛洪攻擊。所謂的泛洪攻擊,是指發送方偽造多個IP地址,模擬三次握手的過程。當服務器返回ACK后,攻擊方故意不確認,從而使服務器不斷重發ACK。由于服務器長時間處于半連接狀態,最后消耗過多的CUP和內存資源導致死機。
所以服務端采用的是這種方法,發送RST數據包,進入close狀態,這個RST數據包中的TCP首部中的控制位中的RST位被置為1。這表示連接信息全部被初始化,原有的TCP通信不能繼續??蛻舳巳绻€想建立TCP連接,需要從第一步握手重新開始。
五、四次揮手關閉連接
(1)客戶端A發送一個FIN,用來關閉客戶A到服務器B的數據傳送(報文段4)。
(2)服務器B收到這個FIN,它發回一個ACK,確認序號為收到的序號加1(報文段5)。和SYN一樣,一個FIN將占用一個序號。
(3)服務器B關閉與客戶端A的連接,發送一個FIN給客戶端A(報文段6)。
(4)客戶端A發回ACK報文確認,并將確認序號設置為收到序號加1(報文段7)。
- 關閉連接的最后一個 ACK 丟失怎么辦?
實際上,在第三步中,客戶端收到 FIN 包時,它會設置一個計時器,等待相當長的一段時間。如果客戶端返回的 ACK 丟失,那么服務端還會重發 FIN 并重置計時器。假設在計時器失效前服務器重發的 FIN 包沒有到達客戶端,客戶端就會進入 CLOSE 狀態,從而導致服務端永遠無法收到 ACK 確認,也就無法關閉連接。 - 為什么TIME_WAIT狀態需要經過2MSL(最大報文段生存時間)才能返回到CLOSE狀態?
這樣能夠保證第四次揮手不管在正常情況下還是丟包情況下都不會出現問題。
正常情況下,客戶端A把第四次揮手的ACK報文發送給客戶端B,這個報文在一個MSL內能夠成功到達。盡管A會多等一個MSL的時間也不會有什么問題。
如果出現第四次丟包的情況,也就是A發送ACK包給B以后,正常情況下在一個MSL的時間內B是肯定能收到的,如果B沒有收到,B會重新發送FIN包,這個重新發送的FIN包在MSL時間內也是能夠到達的。
所以2MLS時間之后,就確保了當前連接上沒有任何報文了,可以正常關閉了。