本章面對的兩個計算機網基礎性問題
1、 兩個實體如何才能在一種會丟失或損壞數據的媒體上可靠地通信
2、 如何控制運輸層實體的傳輸速率以避免網絡擁塞,或從擁塞中恢復過來
運輸層協議是在端系統中而不是在路由器中實現的。因為只有端系統中才有完整的因特網五層協議,路由器只實現了物理層、數據鏈路層和網絡層。
因此,路由器也不會(或者說做不到)檢查封裝在數據報的運輸層報文段的字段。
運輸層和網絡層的關系
- 運輸層(TCP或UDP)為運行在不同主機上的進程之間提供邏輯通信
- 網絡層(IP協議)提供了主機之間的邏輯通信
IP協議的服務模型
盡最大努力交付服務。但它并不做任何確保:
1、 不確保報文段的交付
2、 不確保報文段的按序交付
3、 不確保報文段中數據的完整性
所以IP被稱為不可靠服務。
UDP和TCP協議的服務模型
將兩個端系統間IP的交付服務擴展為運行在端系統上的兩個進程之間的交付服務。這種服務被稱為運輸層的多路復用與多路分解。
UDP和TCP可以通過在其報文段首部中包括差錯檢查字段而提供完整性檢查。進程到進程的數據交付和差錯檢查是兩種最低限度的運輸層服務,也是UDP所能提供的僅有的兩種服務。
所以,與IP一樣,UDP也是一種不可靠的服務,不能保證一個進程所發送的數據能夠完整無缺地到達目的進程。
(聽起來好像上面兩句話有矛盾,提供完整性檢查,為什么又說是不可靠的呢?其實不矛盾,完整性檢查的對象是一個分組——一個報文段的完整性,而非一個進程發送的所有數據作為一個整體的完整性)
TCP為應用程序提供了幾種附加服務
1、提供可靠數據傳輸。通過使用流量控制、序號、確認和定時器來確保
2、提供擁塞控制
TCP提供更多服務,必然會比UDP復雜。
多路復用和多路分解
考慮一個具體的場景,一臺計算機正在運行4個進程:2個Telnet進程、一個FTP進程和一個HTTP進程。這臺計算機的運輸層從底層的網絡層接收數據時,它需要將所接收到的數據定向到這4個進程中的一個。
一個進程會有一個或多個套接字,它相當于從網絡向進程傳遞數據和從進程向網絡傳遞數據的門戶。由于在任一時刻,在接收主機上可能有不止一個套接字,所以每個套接字都會有唯一的標識符。(同時,標識符的格式取決于它是UDP還是TCP套接字。)
所以,問題轉換成接收主機怎樣將一個到達的運輸層報文段定向到適當的套接字。為了完成這個任務,每個運輸層報文段中會有幾個字段。在接收端,運輸層檢查這些字段,標識出接收套接字,進而將報文段定向到該套接字。
- 多路分解:將運輸層報文段中的數據交付到正確的套接字的工作
- 多路復用:在源主機從不同套接字中收集數據塊,并為每個數據塊封裝上首部信息(用于在以后分解)從而生成報文段,然后將報文段傳遞到網絡層的工作
可以看出,分解和復用是兩個相反的操作
- 復用像是導游為旅游團登記酒店入住時收集所有游客身份證件,再交給酒店的行為。
- 分解像是導游給酒店完成登記后,重新把身份證件發回各位手中的行為。這個過程需要身份證件與人的名字相貌等資料匹配。
</br>
端口號
套接字的標識符其實就是端口號
端口號是一個16比特的數,其大小在065535之間。其中,01023范圍的端口號是周知端口號,是受限制的,它們保留給諸如HTTP(端口號80)和FTP(端口號21)之類的周知應用層協議來使用。
常用服務及其端口號
當我們開發一個新的應用程序時,必須為其分配一個端口號。
(思考:那么應用程序這么多,端口號是否容易產生沖突呢?如果產生沖突,如何解決?)
不同主機中具有相同端口號的應用程序向同一個服務器進行通信的例子:
主機A向Web服務器B發起一個HTTP會話,主機C向服務器B發起兩個HTTP會話。主機A、主機C和服務器B都有自己唯一的IP地址,它們分別是A、C、B。主機C為其兩個HTTP連接分配了兩個不同的源端口號(26145和7532)。主機A選擇源端口號時與主機C是互不相干的,因此它也可以將源端口號26145分配給其HTTP連接。
這沒有任何問題,服務器B仍然能夠正確地分解這兩個具有相同源端口號的連接,因為這兩條連接有不同的源IP地址。
UDP的多路復用和多路分解
如果應用程序開發者所編寫的代碼實現的是一個“周知協議”的服務器端,那么開發者就必須為其分配一個相應地周知端口號。通常,應用程序的客戶端讓運輸層自動地(并且是透明地)分配端口號,而服務器端則分配一個特定的端口號。
一個UDP套接字由一個二元組來全面標識,該二元組包含一個目的IP地址和一個目的端口號。
因此,如果兩個UDP報文段有不同的源IP地址和/或源端口號,但具有相同的目的IP地址和目的端口號,那么這兩個報文段將通過相同的目的套接字被定向到相同的目的進程。
TCP的多路復用和多路分解
一個TCP套接字由一個四元組來標識。包含源IP地址,源端口號,目的IP地址,目的端口號。
因此,當一個TCP報文段從網絡到達一臺主機時,該主機使用全部4個值來將報文段定向(分解)到相應地套接字。
特別與UDP不同的是,具有不同源IP地址或源端口號,但具有相同目的IP地址或目的端口號的兩個到達TCP報文段將被定向到兩個不同的套接字;除非TCP報文段攜帶了初始創建連接的請求。
- 首先解釋分號前一句。因為TCP是面向連接的,需要經過“三次握手”建立連接。服務器每收到一個客戶進程的連接請求,都要建立一個新的連接——包括創建一個新的服務器進程與客戶進程通信,與一個相應的套接字。
更具體地說,客戶進程1與服務器的TCP連接,客戶進程2與服務器的TCP連接是不同的兩個TCP連接。客戶進程1會與服務器進程1進行TCP連接,客戶進程2會與服務器進程2進行TCP連接,這2個服務器進程也需要有2個不同的套接字。有n個客戶進程就至少需要n個套接字。所以源不同,就會被定向到不同的套接字。 - 分號后一句。服務器進程在TCP連接中,會有一個“歡迎套接字”用于建立與客戶進程之間的TCP連接。這個“歡迎套接字”是唯一的,僅在“三次握手”時會收到報文。所以如果當兩個客戶進程同時發出請求與服務器進程建立連接的報文段,它們會被定向到同一個套接字,即“歡迎套接字”。
事實上,當今高性能的Web服務器通常只使用一個進程,但是會為每個新的客戶連接創建一個具有新連接套接字的新線程。
</br>
選擇使用UDP的原因
1、 關于何時、發送什么數據的應用層控制更為精細
2、無需連接建立
3、無連接狀態
4、分組首部開銷小
流行的因特網應用及其所使用的運輸層協議
</br>
雖然UDP無法實現可靠數據傳輸,但要說明的一點是,使用UDP的應用也可以實現可靠數據傳輸。這可以在應用程序自身中建立可靠性機制來完成(如增加確認與重傳機制)。但這不容易,它會使應用開發人員長時間忙于調試。
UDP報文段結構
簡要說一下檢驗和:假設數據有3個16比特的字,即3個16比特的數字相加,(有溢出時,就回卷。回卷就是把溢出的高位部分,加到最低位。)最后求反碼,就得到了檢驗和。
在接收方,我們再度把著3個16比特的數字跟檢驗和相加,如果得到全1的數字,證明在傳輸過程沒有差錯。反之,若有某些位為0,說明有差錯。
UDP提供檢驗和進行差錯檢測的原因是不能保證源和目的之間的所有鏈路都提供差錯檢測。
但是,雖然UDP提供差錯檢測,但它對差錯恢復無能為力。UDP的某種實現只是丟棄受損的報文段;其他實現是將受損的報文段交給應用程序并給出警告。
</br>
構造可靠的數據傳輸協議
version1: rdt1.0 (假設情況:經完全可靠信道的可靠數據傳輸)
這個版本的傳輸協議幾乎不用做任何事情,因為下層已經是可靠的。
version2: rdt2.0(假設情況:經具有比特差錯信道的可靠數據傳輸)
增加自動重傳請求(Automatic Repeat reQuest, ARQ)協議。接收方會在報文中添加肯定確認(ACK)和否定確認(NAK)標識來識別哪些被正確接收,哪些因為有比特差錯要重新傳送。
需要使用到ARQ協議的三種功能
1、 差錯檢測(在報文段中添加額外的比特,如使用像UDP中的檢驗和等機制)
2、 接收方反饋(發送回復報文“ACK”和NAK)
3、 重傳(發送方重傳有差錯的分組)
version3: rdt2.1(假設情況:同上,且考慮ACK和NAK報文也會出現比特差錯的情況)
為數據分組添加一個新字段,讓發送方對其數據分組編號,即將發送數據分組的序號放在該字段。接收方只需要檢查序號即可確定收到的分組是否是一次重傳。
現在的情況是這樣的,當接收到正確的分組時,接收方對所接收的分組發送一個ACK;如果收到受損的分組,則接收方將發送一個NAK。
這樣好像很完美很合理了。但是細想,我們實際上只用ACK就夠了。
因為每個分組都有序號,如果發送方發送了一個分組0,接收方收到正確的分組,發送ACK0給發送方,發送方就知道發送成功了。如果發送方發送下一個分組1,接收方收到受損的分組,還是發送ACK0給發送方(因為之前已經接收過ACK0,所以這個ACK0是冗余ACK),發送方就知道分組受損了,準備重傳。所以只需要ACK就能完成這個任務。rdt2.2就是對此進行了優化,只保留ACK標識。
version4: rdt2.2(假設情況:同上,且考慮ACK和NAK報文也會出現比特差錯的情況)
在有比特差錯信道上實現一個無NAK的可靠數據傳輸協議,與rdt2.1的差異也在上一段描述了。
final version: rdt3.0(假設情況:經具有比特差錯的丟包信道的可靠數據傳輸)
比特差錯的問題已經在rdt2.2中得到解決。rdt3.0主要關注丟包問題。
如何判斷一個分組已經丟失?顯然發送方至少需要等待這樣長的時間:發送方與接收方之間的一個往返時延(可能還包括中間路由器的緩沖時延)加上接收方處理一個分組所需的時間。
當然最壞情況下的最大時延很難估算,假設我們可以明智地選擇一個恰當的時間值,以判定可能發生了丟包(盡管不能確保)。如果在這個時間內沒有收到ACK,則重傳該分組。所以我們也會預想到這么一種情況,就是一個分組可能因為網絡擁塞等因素經歷了一個很大的時延,發送方還是可能會重傳該分組,即使這個數據分組及其ACK都沒有丟失。這會引入冗余數據分組,但是我們在rdt2.2中已經可以解決冗余ACK,所以解決冗余數據分組也不是問題了(即已經在rdt2.2中有相關功能來處理冗余數據分組)。
</br>
對發送方來說,重傳是“萬能藥”。無論是一個數據分組丟失,還是一個ACK丟失,或者只是該分組或ACK過度延時。都會發送重傳。
重傳機制是基于時間的,所以需要一個倒計數定時器。發送方需要做到
1、 每次發送一個分組(包括第一次分組和重傳分組)時,便啟動一個定時器。
2、 響應定時器中斷(采取適當的動作)
3、 終止定時器
可靠數據傳輸要點
1、 檢驗和
2、 序號
3、 定時器
4、 肯定和否定確認分組
對rdt3.0的優化
雖然rdt3.0是一個功能正確的協議,但它的性能無法讓人十分滿意。這個性能問題的核心在于它是一個停等(stop-and-wait)協議。它造成了發送方信道極低的利用率。因為每個分組要排隊發送,發送方把第一個分組發送到達接收方,并且還要收到接收方的ACK后,才能繼續發送第二個分組。
解決辦法自然就是不用停等方式運行,允許發送方發送多個分組而無需等待確認。這種技術稱為流水線技術。
流水線技術需要解決的問題
- 必須增加序號范圍,因為每個輸送中的分組(不計算重傳的)必須有一個唯一的序號,而且也許有多個在輸送中未確認的報文。
- 協議的發送方和接收方兩端也許必須緩存多個分組。發送方最低限度應當能春沖那些已發送但沒有確認的分組。接收方或許也需要緩存那些已正確接收的分組。
- 所需序號范圍和對緩沖的要求取決于數據傳輸協議如何處理丟失、損壞及延時過大的分組。解決流水線的差錯恢復兩種基本方法:回退N步和選擇重傳
回退N步協議(又稱滑動窗口協議)
這個協議中,發送方分組被分成四個類型
1、已發送并已被確認的分組
2、已發送但還未確認的分組
3、可用,但還未發送的分組
4、不可用的分組。
其中第二種和第三種分組在滑動窗口內。
回退N步發送方必須響應的三種類型的事件
1、上層的調用
2、收到一個ACK
3、超時事件
回退N步會造成單個分組出錯就引起重傳大量分組的問題,但許多分組其實沒有必要重傳,所以可以使用“選擇重傳”。
選擇重傳
這個協議中,發送方分組也是上面的四個類型,不同的是第一種分組(已發送并已被確認的分組)也會出現在滑動窗口內。
接收方的滑動窗口也有三種類型的分組
1、失序(已緩存),但未被確認的分組
2、可接受的分組
3、期待,還未收到的分組
可靠數據傳輸機制及其用途的總結
</br>
TCP
TCP連接總是點對點的,即在單個發送方與單個接收方之間的連接。所謂“多播”,即在一次發送操作中,從一個發送方將數據傳送給多個接收方,對TCP來說這是不可能的。對TCP而言,兩臺主機是一對的。
TCP的“三次握手”中,前兩次的報文段不承載“有效載荷”,也就是不包含應用層數據,第三次報文段可以承載有效載荷。
TCP連接的組成包括:一臺主機上的緩存、變量和與進程連接的套接字,以及另一臺主機上的另一組緩存、變量和與進程連接的套接字。在這兩臺主機之間的網絡元素(路由器、交換機和中繼器)中,沒有為該連接分配任何緩存和變量。
TCP報文段結構
- 源端口號和目的端口號:用于多路復用/分解
- 序號和確認號:被TCP發送方和接收方用來實現可靠數據傳輸服務
- 接收窗口:用于流量控制。指示接收方愿意接受的字節數量。
- 首部長度:由于TCP選項字段,TCP首部的長度是可變的。(通常,選項字段為空,所以TCP首部的典型長度就是20字節)
- 選項:用于發送方和接收方協商最大報文段長度(MSS)時,或在高速網絡環境下用作窗口調節因子時使用
- 6比特的標志字段:ACK(成功接受報文段的確認)、RST-SYN-FIN(用于TCP連接的建立和拆除)、PSH(接收方應立即把數據交給上層)、URG(略)
</br>
流量控制
一條TCP連接每一側主機都為該連接設置了接收緩存。當該TCP連接收到正確、按序的字節后,它就將數據放入接收緩存。
相關聯的應用進程會從該緩存中讀取數據,但不必是數據剛一到達就立即讀取。事實上,接收方應用也許正忙于其他任務,甚至要過很長時間后才去讀取該數據。如果某應用程序讀取數據時相對緩慢,而發送方發送得太多、太快,發送的數據就會很容易使該連接的接收緩存溢出。
所以TCP為它的應用程序提供了流量控制服務以消除發送方使接收方緩存溢出的可能性。流量控制是一個速度匹配服務,即發送方的發送速率與接收方應用程序的讀取速率相匹配。
TCP通過讓發送方維護一個稱為接收窗口的變量來提供流量控制。通俗地講,接收窗口用于給發送方一個指示——該接收方還有多少可用的緩存空間。因為TCP是全雙工通信,在連接兩端的發送方都各自維護一個接收窗口。
SYN洪泛攻擊
這種攻擊針對的是TCP的“三次握手”。
在正常的三次握手中,服務器為了響應一個收到的SYN,分配并初始化連接變量和緩存。然后服務器發送一個SYN ACK進行響應,并等待來自客戶的ACK報文段。如果某客戶不發送ACK來完成該三次握手的第三步,最終(通常在一分多鐘之后)服務器將終止該半開連接并回收資源。
在SYN洪泛攻擊中,攻擊者發送大量的TCP SYN報文段,而不完成第三次握手的步驟。隨著這種SYN報文段紛至沓來,服務器不斷為這些半開連接分配資源(但從未使用),導致服務器的連接資源被消耗殆盡。
有效應對SYN洪泛攻擊的防御系統稱為SYN cookie。
TCP擁塞控制
TCP采用的方法是讓每一個發送方根據所感知到的網絡擁塞程度來限制其能向連接發送流量的速率。如果一個TTCP發送方感知從它到目的地之間的路徑上沒有什么擁塞,則TCP發送方增加其發送速率;如果發送方感知沿著該路徑有擁塞,則發送方就會降低其發送速率。
TCP連接的每一端都是由一個接收緩存、一個發送緩存和幾個變量組成。運行在發送方的TCP擁塞控制機制跟蹤一個額外的變量,即擁塞窗口。通過調節擁塞窗口的大小,可以調整發送方向連接發送數據的速率。
TCP擁塞控制算法
三個部分
1、慢啟動(擁塞窗口在一開始以指數級增長,直至丟包——擁塞)
2、擁塞避免(出現擁塞時,擁塞窗口變為擁塞時的一半,并改為線性增長)
3、快速恢復