【網絡】第四章-傳輸層協議

傳輸層協議

  傳輸層負責端與端之間的數據傳輸,其中典型協議爲TCP協議和UDP協議。
  TCP協議是TCP/IP協議棧中的傳輸層的典型協議,叫傳輸控制協議,面向連接,可靠傳輸,提供字節流服務。
  UDP協議是TCP/IP協議棧中的傳輸層的典型協議,叫用戶數據報協議,無連接,不可靠,提供數據報傳輸服務。

UDP協議

協議字段

  協議字段都包含在UDP協議數據報的報頭中,每次發送數據都會將這些信息和數據一起發出。
  1、16位源端口:標識數據從哪個進程來。
  2、16位目的端口:描述數據到哪個進程去。
  3、16位校驗和:對數據的二進制反碼求和,用於校驗接收到的數據是否和發出的數據一致。
  4、16位數據報長度:由於數據報長度一共只有16位因此udp一個數據報的最大大小爲64k,加上udp數據報頭部信息一共64位還要佔8個字節,因此數據大小不能超過64k-8。並且sendto這個接口會將我們傳入的數據直接封裝報頭進行傳輸,所以我們在使用sendto接口的時候buf的長度最大不能超過64k-8
  如果我們要傳輸的數據大於64-8。則需要用戶在應用層進行分包傳輸。並且udp協議不保證安全也不保證包序,因此在接收數據後我們還需要在應用層進行整理。
  根據udp報文中的數據報長度大小,我們的udp協議能且僅能一次收發一條完整的udp報文,不會將報文拆分,報文的長度可以從報頭中獲得。

應用

  因爲udp無連接不可靠,因此udp不能應用於有安全性要求的傳輸,但是因爲報頭短,並且不需要保證安全性因此傳輸速度快,因此多應用於實時性要求高的場景。
  udp在協議棧層面實現了廣播機制,通過向一個地址發送信息可以向局域網內所有主機發送信息。
  在應用層基於udp協議實現的協議:DNS協議,DHCP協議。

TCP協議

協議字段

  1、16位源端口:標識數據從哪個進程來。
  2、16位目的端口:標識數據到哪個進程去。
  3、32位序號:用於進行包序管理。
  4、32位確認序號:用於進行包序管理。
  5、4位首部長度:用於描述頭部信息長度,單位是4個字節。tcp協議頭部長度最小爲20字節,最大爲當4位首部長度最大時即爲15 * 4 = 60字節
  6、6位保留:用於保留下來存儲新的屬性和數據。
  7、6位標誌位:用於標誌當前tcp信息的屬性和類型,常見的有URG/ACK/PSH/RST/SYN/FIN

CWR(Congestion Window Reduce):擁塞窗口減少標誌被髮送主機設置,用來表明它接收到了設置ECE標誌的TCP包,發送端通過降低發送窗口的大小來降低發送速率

ECE(ECN Echo):ECN響應標誌被用來在TCP3次握手時表明一個TCP端是具備ECN功能的,並且表明接收到的TCP包的IP頭部的ECN被設置爲11。更多信息請參考RFC793。

URG(Urgent):該標誌位置位表示緊急(The urgent pointer) 標誌有效。該標誌位目前已經很少使用參考後面流量控制和窗口管理部分的介紹。

ACK(Acknowledgment):取值1代表Acknowledgment Number字段有效,這是一個確認的TCP包,取值0則不是確認包。後續文章介紹中當ACK標誌位有效的時候我們稱呼這個包爲ACK包,使用大寫的ACK稱呼。

PSH(Push):該標誌置位時,一般是表示發送端緩存中已經沒有待發送的數據,接收端不將該數據進行隊列處理,而是儘可能快將數據轉由應用處理。在處理 telnet 或 rlogin 等交互模式的連接時,該標誌總是置位的。

RST(Reset):用於復位相應的TCP連接。通常在發生異常或者錯誤的時候會觸發復位TCP連接。

SYN(Synchronize):同步序列編號(Synchronize Sequence Numbers)有效。該標誌僅在三次握手建立TCP連接時有效。它提示TCP連接的服務端檢查序列編號,該序列編號爲TCP連接初始端(一般是客戶端)的初始序列編號。在這裏,可以把TCP序列編號看作是一個範圍從0到4294967295的32位計數器。通過TCP連接交換的數據中每一個字節都經過序列編號。在TCP報頭中的序列編號欄包括了TCP分段中第一個字節的序列編號。類似的後續文章介紹中當這個SYN標誌位有效的時候我們稱呼這個包爲SYN包。

FIN(Finish):帶有該標誌置位的數據包用來結束一個TCP會話,但對應端口仍處於開放狀態,準備接收後續數據。當FIN標誌有效的時候我們稱呼這個包爲FIN包。

  8、16位窗口大小:標誌傳輸窗口的大小,這塊牽扯到tcp協議的傳輸特性和機制,在後續進一步講解。
  9、16位校驗和:校驗數據一致性。
  10、16位緊急指針:帶外數據。
  11、40字節選項數據:有時沒有有時有,但長度必須是4的整數倍字節。
  除選項外其他頭信息一共20字節,這是最小頭信息長度。

TCP連接管理機制

三次握手建立連接

  首先客戶端處於SYN_SENT狀態服務端處於LISTEN狀態,客戶端會向服務端發送一個SYN包,服務端收到後會轉變狀態爲SYN_REVD狀態,成功後向客戶端發送SYN+ACK報文表示收到連接請求,之後客戶端會再次確認,轉換狀態爲ESTABLISHED並返回ACK包表示連接成功,服務端收到ACK後葉轉換狀態爲ESTABLISHED
三次握手

四次揮手斷開連接

  首先客戶端(假設客戶端先發起斷開連接請求)處於FIN_WAIT1狀態會發送FIN包請求關閉連接,服務端收到後切換狀態爲CLOSE_WAIT狀態,並且發送ACK包確認收到請求,客戶端收到數據包後會會轉變狀態爲FIN_WIAT2狀態。隨後服務端切換狀態爲LAST_ACK並且主動發送FIN包確認是否斷開連接,客戶端收到後切換狀態爲TIME_WAIT併發送ACK包給服務端,服務端收到後切換狀態爲CLOSED正式斷開連接,但是客戶端此時並不會真正斷開連接,處在TIME_WAIT狀態的客戶端會在此繼續等待一段時間,之後纔會改變狀態爲CLOSED
四次揮手

爲什麼?

  在tcp連接和斷開過程中有很多疑問,爲什麼要三次握手四次揮手?爲什麼客戶端斷開連接還要等待?
  爲什麼握手要三次才能建立連接?兩次行不行?四次呢?
  我們都注意到握手和揮手都需要客戶端和服務端雙方各發送一個SYN和收到一個ACK包才能算成功,這是爲了防止網絡傳輸過程中由於延遲,丟包等問題對數據造成丟失或延遲,因此客戶端和服務端此時都要確認對方此時是在線的是可以連接的。我們可以想象這樣的場景,在揮手過程中如果只有兩次的話,客戶端發送一個連接請求,服務端收到後就表示已經建立連接,這樣是十分不安全的,如果這個連接請求因爲延遲被耽誤了很久,服務端接收到報文的時候客戶端此時已經關閉了,那麼這個連接就是失敗的,但是服務端卻表示成功了,那着肯定是不合理不安全的。因此服務端也必須要發送一個SYN包確認此時客戶端在線,需要服務端回覆後才能確認連接建立成功,因此兩次握手是不安全的,但是四次連接也是沒有必要的,服務端在建立連接時確認客戶端在線的SYN報文是可以和回覆報文一起發送的,因此三次即可。
  爲什麼揮手要四次,三次可以麼?
  和建立連接同理,服務端和客戶端需要各發送一次SYN進行一次確認,但是這裏爲什麼不可以將服務端的SYNACK放在一起發送?因爲在連接關閉後,連同socket及其相應的緩衝區都會關閉,清空數據,那麼如果此時緩衝區中還有信息的話信息也會一起丟失,因此客戶端發送FIN包後服務端不能將回應ACK和確認FIN一起發送,服務端會在用戶接收緩衝區中所有數據後纔會發送SYN確認關閉請求,然後關閉套接字。
  TIME_WAIT有什麼用,爲什麼客戶端關閉連接後要等待?要等待多久?
  如果沒有TIME_WAIT,客戶端在發送最後一次ACK後就直接關閉的情況下,如果此時最後一次ACK包文丟失,服務端很久並沒有收到包文,它會再次重發最後一次FIN包,如果此時客戶端又重啓了一個同端口的客戶端準備再次建立連接,此時卻接收到服務端發來的FIN包就會陷入混亂,因此客戶端在此時需要等待。至於等待多久此時需要2個MSL的時間,一個MSL時間表示數據包在網絡中的生命週期,它最久只能在網絡中存活這麼久,如果還沒有成功則會銷燬這個包,也就是丟包,等待兩個MSL時間是爲了讓客戶端自己發送的包以及以及服務端如果重發FIN包都能夠要麼接收到,要麼都消亡在網絡中不對後續連接造成影響,等待2個MSL時間已經足夠保守極大減少之前連接會影響到之後連接情況。

保活機制

  默認情況下,通信雙方7200s沒有數據往來,每隔75s向對方發送一個保活探測數據包,要求對方進行相應,若是得到相應則認爲連接正常,若是連續9次沒有得到相應,則認爲連接斷開,將socket狀態置爲CLOSE_WAIT

確認應答機制

  TCP給發送的所有數據中的每一字節都進行了編號,假如說客戶端首先給服務端發送了1-1000號數據,服務端如果接收到所有數據會返回1001ACK表示前1000字節的數據都已經收到了,但是如果返回的不是1001或者壓根沒有返回,客戶端就會知道中間有丟包,於是爲了可靠傳輸則會重傳,於是有了超時重傳機制。

超時重傳機制

  如果在客戶端發送了1-1000號數據後服務端返回了1001ACK,此時客戶端發送了1001-2000號數據,但是服務端給我們遲遲不見發送ACK確認,於是客戶端就會認爲1001-2000號數據丟包則會重新發送1001-2000號數據。
  當然客戶端收不到ACK也有可能是ACK丟包,因此客戶端會依然重發數據,此時服務端可能會存在大量重複數據,服務端會根據編號來辨別重複數據,並且如果客戶端之後發送了2001-3000號數據,並且收到了3001號ACK則客戶端雖然沒有收到2001號ACK也會認爲之前所有的數據都已經收到。
  至於多久纔算超時,Linux是以500ms作爲一個單位以整數倍作爲超時時間,以指數形式增長,當重傳到一定次數後還收不到ACK則認爲網絡出現異常,強制關閉連接。

確認數據有序和無誤

  超時重傳機制和確認應答機制只能保證所有的包都不丟包,但是並不能保證包有序到達並且數據無誤,這就要用到TCP頭信息中的序號/確認序號來對包文進行排序,如果服務端先收到了2001-3000號包但沒有收到1001-2000號數據服務端則暫時不會向用戶表示自己收到了2001-3000號數據,會在此等待收到1001-2000號數據爲止,並且收到數據後對包文進行排序和整理,以及通過校驗和對包文數據進行校驗。

滑動窗口機制

  我們之前討論都是在一次傳輸一次應答的情況下討論的,但是這種通信方式過於緩慢,於是TCP是可以同時發送多個數據傳輸而不用非要每一條傳輸都接收ACK纔會發送下一條數據,這樣大大可以提高性能。但是如果發送過快,而網絡狀況不好,全部丟包,服務端一條都收不到或者只能零零散散接收到一些片段,則會導致客戶端大量的重傳包文,十分影響效率,因此引入滑動窗口機制
  在介紹滑動窗口之前,我們還要知道一個概念即MSS,最大數據長度,這個大小往往是在三次握手期間就已經確定下來的,標識一個包文最大隻能傳輸這麼大的數據。知道這個概念後,我們就要開始思考一個問題,TCP允許同時發送多個數據包,但是爲了防止大量丟包該怎麼做呢?沒錯,呢就是限制能夠同時發送的數據包的數量,這就是滑動窗口成立的基礎。
  滑動窗口大小決定了能夠無需等待確認應答的情況下可以繼續發送數據的最大值。假如說我們在三次握手期間確定MSS=1024,滑動窗口win=4096,則表示客戶端向服務端可以暫時不等待確認應答最多可以發送4096/1024=4個數據包,假設一開始客戶端窗口框住的數據區域爲1-4096,並且同時發送4個數據包完畢後,客戶端就必須停下來等待確認應答。如果我們的第一個包1-1024號數據已經成功發送並且收到了響應ACK1025則表示一號包中數據發送成功,則客戶端的窗口會向後滑動一個MSS大小框住接下來的數據1025-5120(假設滑動窗口大小並沒有改變),然後繼續向服務端發送窗口滑動後出現的新的一個數據包,以此類推,直到發出所有數據包並接受所有響應,此時滑動窗口應該已經滑倒了數據末尾。窗口大小越大,網絡吞吐量越高。

快速重傳機制

  因爲滑動窗口機制我們知道了TCP允許同時發送多個數據包,但是如果其中發生丟包會怎樣呢?假設窗口還是win=4096MSS=1024我們的第一個包1-1024已經成功接收到了ACK,說明服務端接收到了1-1024的數據,但是服務端此時遲遲收不到1025-2048的數據,儘管後面的數據都已經收到,此時服務端會連續不斷髮送1025ACK提醒客戶端我想要的是1025-2048數據。一旦客戶端連續三次收到相同的ACK則會知道對應的數據包丟失重新發送對應的數據包,這個機制叫做快速重傳機制

流量控制

  同時得益於滑動窗口,我們可以控制傳輸數據的速度和大小,控制網絡吞吐量。但是滑動窗口的大小並不是一成不變的,服務端在接收數據過程中如果緩衝區滿了無法繼續接收數據則會造成大量丟包情況,於是此時服務端可以通過改變窗口大小控制數據傳輸大小。服務端可以將自己緩衝區的大小放進TCP頭部信息的窗口大小字段中,告訴客戶端自己緩衝區大小,當緩衝區逐漸變滿的過程中服務端每次發送ACK都會重新調整滑動窗口大小,告訴客戶端你傳滿點我收不下了,當大小爲0時表示緩衝區已滿,只有等用戶從緩衝區提取信息後服務端纔會加大窗口大小繼續接收數據,由此做到流量控制。

擁塞控制

  滑動窗口的大小並不是一開始就是最大的,因爲要考慮到網絡狀況,一開始滑動窗口的大小隻有1,用於試探網絡狀況,當確認網絡狀況沒有問題的時候纔會逐漸以指數級別增長速度繼續增長,但是到達一個閾值後停止繼續增長。如果傳輸過程中出現丟包需要重傳情況則會將閾值縮小爲原先的一半,並將滑動窗口置回1。這個過程用一句話形容就是慢啓動,快增長,客戶端想盡快將數據發送給對方,但是又要避免網絡造成太大壓力的折中方案。

延遲應答機制

  客戶端將數據發送給服務端後,服務端收到數據後並不會立刻回覆ACK,因爲剛接收完數據存儲在緩衝區中用戶還沒有取走數據,因爲流量控制機制此時必然要調小窗口大小,這樣會影響傳輸速度,影響網絡吞吐量,於是服務端會稍微在此等待片刻再做應答,這個時間一般是200ms左右,操作系統不同時間也不同,而操作系統也許處理數據速度很快10ms就取走數據,此時緩衝區數據被取走緩衝區又恢復到無數據狀態,也就不用調小窗口大小控制傳輸速度了,這樣可以加大網絡吞吐量,提高傳輸效率。

捎帶應答機制

  不光客戶端可以給服務端發送數據,服務端也會給客戶端發送數據,假如客戶端發送數據你好呀,服務端想要回復我還行,服務端爲了傳輸效率則會將客戶端數據的ACK和要發送的我還行數據一起發送給客戶端,這就是捎帶應答機制。

TCP面向字節流

提供字節流服務

  一個TCP套接字在被創建後,同時在內核中創建一個發送緩衝區和一個接收緩衝區,既可以讀數據也可以寫數據,這樣的模式被稱爲全雙工。在調用send發送數據後會將數據先讀取到緩衝區中,操作系統會將數據積累到合適的長度在合適的時機進行發送。在接收數據時也是從接收緩衝區中讀取數據。正因爲緩衝區的存在,使得TCP的數據傳輸十分自由,寫一個數據可以一次寫完也可以分段多次寫,讀也一樣,正因爲字節流這樣靈活的服務導致了接下來的問題——粘包問題

TCP粘包問題

  首先要明確一點UDP不會粘包,因爲在UDP頭信息中就已經存儲了數據大小信息,根據這個信息可以分辨出這個包的邊界,因此不會出現取數據時取出下一個報文的數據的情況。但是由於TCP面向字節流傳輸靈活因此無法明確兩個包之間的邊界。
  如何解決呢?明確兩個包之間的邊界
  要想解決粘包問題我們只能通過在應用層對數據的處理來明確數據邊界了。有以下幾種做法。
  1、如果數據是定長的,例如傳輸的是結構或者類這樣的塊狀結構,只需要每次都保證讀取定長的數據即可。
  2、如果是邊長的數據,可以約定在數據頭部添加一個字段,表示這個包數據的長度,獲取這個包的話只讀取這麼長的數據即可。
  3、也可以在不影響的數據下在數據末尾添加分割符,標識這個包數據的結束。
  以上這些方法都可以區分包與包之間數據的邊界,從而解決粘包問題。

TCP總結

  以上講了TCP這麼多的機制,我們可以將其分類總結,TCP之所以複雜的原因是它爲了保證可靠性但又儘可能的在提升效率。

可靠性

  1、校驗和。
  2、序列號。
  3、確認應答機制。
  4、超時重傳機制。
  5、流量控制。
  6、擁塞控制。
  7、連接管理。

提高性能

  1、快速重傳。
  2、滑動窗口。
  3、延遲應答機制。
  4、捎帶應答機制。

TCP應用

  基於TCP的應用層協議:HTTP/HTTPS/SSH/FTP/SMTP

TCP與UDP的對比

  通過對TCP與UDP的介紹,我們現在已經很清楚TCP與UDP各自的特點及各自的優勢。
  對於TCP協議,適用於要求安全性高,傳輸可靠的情況下,例如文件傳輸,重要狀態更新等情況。
  對於UDP協議,適用於實時性高,速度要求快的情況下,例如視頻傳輸,通信領域等。

如何用UDP實現可靠傳輸

  UDP本身是不可靠的,可能會有大量丟包的情況發生,因此我們想要UDP實現可靠傳輸可以參考TCP在應用層實現TCP的一些可靠機制,例如在應用層利用序號確定包序,引入確認應答和超時重傳保證不丟包等。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章