計算機網絡自頂向下方法:第三章 運輸層


先根據書中的復習題鞏固一遍, 最后回答常見問題TCP三次握手和四次揮手, 如有錯誤, 歡迎指出~

點擊下面跳過復習題

TCP3次握手: 為什么需要初始序號? 為什么需要3次握手, 而不是兩次握手
TCP4次揮手

 

第二章: 運輸層

3.1~3.3節

R1. 假定網絡層提供了下列服務.

  • 在源主機中的網絡層接收最大長度1200字節和來自運輸層的目的主機地址的報文段. 網絡層則保證將該報文段交付給位於目的主機的運輸層. 假定在目的主機上能夠運行許多網絡應用進程.
    a. 設計可能最簡單的運輸層協議, 該協議將使應用程序數據到達位於目的主機的所希望的進程. 假設在目的主機中的操作系統已經為每個運行的應用進程分配了一個4字節的端口號.
    b. 修改這個協議, 使它向目的進程提供一個的"返回地址".
    c. 在你的協議中, 該運輸層在計算機網絡的核心中"必須做任何事"嗎?

 
答:

  • a. 我設計的最簡單運輸層協議將包括兩部分: 目的端口號和報文. 因為該協議只要使應用程序數據到達位於目的主機所需要的進程就可以了, 它將會在下發到源主機的網絡層時加上目的主機的IP地址, 保證了主機之間的邏輯通信. 到達目的主機的網絡層后會把該運輸層報文段提取上交到運輸層, 運輸層根據目的端口好在本機的網絡進程中找到有相同的端口號的目的進程, 並交付應用數據.
  • b. 為了提供讓目的進程返回的地址, 在需要加上一個源端口號字段. 這樣目的進程就能通過把源端口號設置為目的端口號, 進而向源進程傳輸數據. (再加上一些其他字段就是個UDP協議了)
  • c. 它並不需要做"任何事", 目前它只提供交付數據的功能, 不具備諸如擁塞控制, 確保數據完整性等功能.

 

R2. 考慮有一個星球,

  • 每個人都屬於某個六口之家, 每個家庭都住在自己的房子里, 每個房子都有一個唯一的地址, 並且某給定家庭中的每個人有一個獨特的名字. 假定該星球有一個從源家庭到目的家庭交付信件的郵政服務. 該郵件服務要求: 1. 在一個信封中有一封信; 2. 在信封上清楚地寫上目的家庭的地址(並且沒有別的東西). 假設每個家庭有一名家庭成員代表為家庭中的其他成員收集和分發信件. 這些信件沒有必要提供任何有關信的接收者的提示.
    a. 使用對上面復習題R1的解決方案作為啟發, 描述家庭成員代表能夠使用的協議, 以從發送家庭成員向接收家庭成員交付信件.
    b. 在你的協議中, 該郵政服務必須打開信封並檢查信件內容才能提供它的服務嗎?

 
答:

  • a. 這個問題考察的是運輸層協議與上層應用層和下層網絡層之間的關系. 下面描述一次收發郵件的過程.
場景 映射
每個家庭住自己的房子, 房子有唯一地址 每一台主機都有自己的IP地址
家庭中每個人都有自己獨特的名字 主機中運行的每個進程都有自己唯一的端口號
家庭A一名成員代表家庭A收集全家庭的信件 某個運輸層協議, 通過套接字從應用進程獲取應用報文
家庭A代表將收集的所有郵件交給郵政服務 運輸層協議將運輸層報文段交付給網絡層 (多路復用)
信封上除了目的家庭地址沒有別的東西, 郵政服務把郵件傳輸到目的家庭B 網絡層會將報文段與目的地址IP封裝成數據報, 進行主機之間的網絡傳輸
家庭B的代表從郵政服務獲得郵件 目的主機的運輸層從網絡層中接收數據, 抽取出報文段
家庭B的代表把郵件分發給自己家里的成員 目的主機的運輸層協議把數據通過套接字上交給應用進程 (多路分解)
  • b. 郵政服務不需要打開信封並檢查信件內容, 因為它只負責把郵件從一個家庭住址傳送到另一個家庭. 它並不關心信件的內容. 好比網絡層所提供的服務, 它會將傳輸層報文段封裝起來, 報文段的具體內容與它無關.

 

R3. 考慮在主機A和主機B之間有一條TCP連接.

  • 假設從主機A傳送到主機B的TCP報文段具有源端口號x和目的端口號y. 對於從主機B傳送到主機A的報文段, 源端口號和目的端口號分別是多少?
  • 答: 源端口號將設置為y, 目的端口號設置為x.

 

R4. 描述應用程序開發者為什么可能選擇在UDP上運行程序而不是在TCP上運行的原因.

  • 這個問題問的是在什么情況下, UDP的優點明顯蓋過TCP的缺點.
  • UDP協議是一種極為簡化的運輸層協議, 它不提供不必要的服務, 大概就是在IP協議上加上源和目的地的端口后等信息, 所以它可以隨時地, 以任何速率向其他端系統發送數據.
  • TCP協議有許多優良特性, 它確保數據完整性, 提供擁塞控制, 但這些特性會增加端到端通信的時延.
  • 如果一個應用程序需要提供實時服務, 而且能夠容忍一定的分組丟失, 那么UDP協議是更好的選擇.

 

R5. 在今天的因特網中, 為什么語音和圖像流量常常是經過TCP而不是經UDP發送.

  • (提示: 我們尋找的答案與TCP的擁塞控制機制沒有關系)
  • 答: 為了確保數據的完整性, 分組丟失可能會對語音和圖像質量產生影響.

 

R6. 當某應用程序運行在UDP上時, 該應用程序可能得到可靠的數據傳輸嗎? 如果能, 如何實現?

  • 可以的.
  • 需要通過應用層協議實現. 比如谷歌的Chrome瀏覽器中所使用的QUIC協議在UDP之上的應用層協議中實現了可靠性.

 

R7. 假定在主機C上的一個進程有一個具有端口號6789的UDP套接字.

  • 假定主機A和主機B都用目的端口6789向主機C發送一個UDP報文段. 這兩台主機的這些報文段在主機C都被描述為相同的套接字嗎? 如果是這樣的話, 在主機C的該進程將怎樣知道源於兩台不同主機的這兩個報文段?

 

  • 答: 這兩台主機的這些報文段在主機C會被描述為相同的套接字. 因為在傳輸UDP包的時候, 網絡層會附帶上源和目的的IP地址的, 主機C的程序可以通過不同的源IP地址判別.
  • 畢竟主機A和B在選端口的時候不知道彼此具體會選什么, 肯定會有選用一樣端口號的情況, 主機IP能把它們區分開.

 

R8. 假定在主機C端口80上運行一個Web服務器.

  • 假定這個Web服務器使用持續連接, 並且正在接收來自兩台不同主機A和B的請求. 被發送的所有請求都通過位於主機C的相同套接字嗎? 如果它們通過不同的套接字傳遞, 這兩個套接字都具有端口80嗎? 討論和解釋之.

 

  • 答: 這里有個巧妙的關系為題目帶來歧義.
  • A和B的請求會通過80端口找到服務器進程, 就這里而言它們通過為與C的相同套接字, 這個套接字具有端口80.
  • 當它們與服務器進程建立連接的時候, 服務器進程會單獨為它們分配套接字, 通過專門的套接字響應客戶端的請求. 這兩個套接字就不具有80端口了.

 

R9. 在我們的rdt協議中, 為什么需要引入序號?

  • 如果不引入序號會有什么問題? 描述: 一個初始的rdt停等協議是這樣的: 發送方發送一個分組, 發送方進入等待狀態, 不能從上層接收分組, 接收方接收到分組后如果分組正常則回復ACK, 否則回復NAK, 發送方接收到ACK則回到初始狀態等待上層調用, 如果收到NAK則重傳該分組繼續等待.
  • 問題來了: 如果接收方的ACK, NAK分組在傳輸過程中受損, 發送方該如何處理? 最簡單實用的方法就是重傳了, 也就是當發送方不確定接收方是否收到分組就把分組重新發送一遍.
  • 但是重發分組會帶來新的問題, 接收方在接收到重發的分組時, 它並不知道這是一個新的分組還是一個重傳的分組(對上一個分組已經確認過了).
  • 於是便引入序號, 這里的序號用一個比特位表示0和1就能解決. 加入當前發送方發送帶有0序號的分組, 那么接收方會進行響應. 發送方如果接收到損壞的確認分組, 那么它重傳一次帶有0序號的分組. 否則傳送帶1序號的下一個分組. 接收方根據分組的序號進行判斷, 如果當前分組的序號和上一個接受到的分組的序號相同, 說明這是一次重傳, 如果不相同說明是一個新的分組.
  • 以上基於一個簡單的停等協議進行描述.

 

R9. 在我們的rdt協議中, 為什么需要引入定時器?

  • 在上面一題中了解引入序號是為了解決分組在傳播過程中因分組受損, 發送方重傳分組而帶來的冗余分組問題. 但是在因特網中, 分組除了會受損, 還可能丟失. 這里探討該通過一個怎樣的機制解決分組丟失問題.
  • 如果發送方發送的分組或者接收方響應的確認在傳輸過程中丟失, 發送方將收不到確認. 解決這問題的辦法仍是重傳分組, 但是應該在什么時候進行重傳是值得商榷的. 網絡中的延時具有非常大的不確定性, 如果等待足夠大的時延才重傳分組顯然會降低效率. 應該定一個固定的時間, 只要過了這個時間就認為分組丟失(盡管可能沒有丟失).
  • 這里便引入了定時器, 在每傳輸一個分組時開啟一個定時器, 而且讓發送方響應定時器計時后產生的中斷, 還有要關閉計時器的機制.

當集齊檢驗和, 序號, 定時器, 肯定和否定確認分組這些技術后, 一個基本的可靠數據傳輸協議已經構建好了.

 

R10. 假定發送方和接收方之間的往返時延是固定的並且為發送方所知. 假設分組能夠丟失的話, 在協議rdt3.0中, 一個定時器仍是必需的嗎? 試解釋之.

  • 如果發送方知道了雙方固定的往返時延, 那么就可以不需要定時器了. 因為定時器的存在就是為了估計一個雙方的往返時延值, 超過了就進行重傳. 現在知道具體且固定的往返時延, 那么就可以准確地得出接受到確認分組的時間, 如果超過了該時間還沒有接收到確認就進行重傳.

 

R12. 在配套網站上使用Go-Back-N(回退N步)Java小程序.

a. 讓源發送5個分組, 在這5個分組的任何一個到達目的地之前暫停該動畫. 然后毀掉第一個分組並繼續該動畫. 試描述發生的情況.
b. 重復該實驗, 只是現在讓第一個分組到達目的地並毀掉第一個確認. 再次描述發生的情況.
c. 最后嘗試發送6個分組. 發生了什么情況?

 

R13. 重復復習題R12, 但是現在使用Selective Repeat(選擇重傳)Java小程序. 選擇重傳和回退N步有很么不同?

 

  • 上面的題目由於無法訪問到該小程序暫時跳過, 但是下面的補充會覆蓋掉上面兩個問題的知識點.

補充:

回退N步

  • 在R11過后已經基本建立起了一個可靠數據傳輸協議. 但是它傳輸分組的形式相當於串行傳輸, 對鏈路的利用率極低. 可不可以在等待收到確認分組時繼續發送分組?
  • 答案是可以的. 但是會引入新的問題, 這樣像流水線一樣地發送分組, 如何處理丟失, 損壞及延時過大的分組?
  • 解決流水線的差錯恢復有兩種基本方法是: 回退N步和選擇重傳.
  • 回退N步從字面上是很好理解的, 先發送一個分組a, 在啟動計時器后陸續發送b, c, d ... n, 如果a分組在傳輸過程中出現了問題, 那么就從a開始重新傳輸n個分組.
  • 口頭描述一下回退N步的流程, 下面的FSM圖描述的更清晰. 首先會發送base序號的分組, 並啟動計時器, 然后繼續按序發送窗口內的分組, 如果到達了窗口的邊界base + N - 1處的分組就停下來, 也就是說發送方需要維護發送窗口的上下邊界. 而接收方接收到base分組后會根據base分組的序列號響應回去, 因此接收方只需要維護一個記錄分組n被接收到的變量.
  • 當base分組的計時器計時完還沒有收到確認, 認為出現分組丟失, 這時不管窗口有邊界擴到哪里, 都從base開始重傳分組. 所以如果接收方接在收到base分組之前收到后面的分組(失序), 可以直接把失序的分組丟棄掉, 不需要緩存, 因為發送方必定會重傳一次.
  • 從這也能得出發送方與接收方都是采用累計確認的方式處理分組.

 

選擇重傳

  • 在引入選擇重傳之前, 我們先看看回退N步協議會帶來什么問題. 顯然回退N步是一個在流水線式分組傳輸下一個可靠的協議, 但是如果窗口比較大, 當base分組丟失后, 后面所有的分組都要進行重傳, 這會不會比較浪費呢? 考慮每個分組都丟失一下, 那么重傳的次數將達到二次方的數量級.
  • 可不可以哪個分組丟失了就重傳哪個分組, 而不是重傳全部呢? 可以的, 這就引入了選擇重傳協議. 在理解了回退N步后選擇重傳應該不是個大問題.

 

  • 對於發送方, 在send_base分組被確認后它窗口才會往左移動, 移動的長度取決於send_base分組旁邊有多少個連着的已經確認的分組(選擇重傳, 允許失序的分組被確認后緩存起來, 等待被確認).
  • 對於接收方, 當接收到rcv_base分組后才會移動窗口, 移動的長度也取決於旁邊有多少個連着的失序(已緩存)但未被確認的分組.
  • 從這能看出, 發送方與接收方的窗口並不是同步移動的. 這里會引入一個大問題. 見下圖:

 

  • 上圖描述的問題是現在分組的序列號的取值范圍是0~3, 也就是大小為4(之前講過0~1的), 窗口大小為3.
  • 假設發送方發送了分組0, 1, 2, 發送方全部接收到, 發送方的窗口移動3格, 等待3, 4, 5分組.
  • 不巧的是0, 1, 2三個分組的ACK分組都丟失了, 發送方將重傳0, 1, 2三個分組.
  • 這時問題就來了, 假設0, 1, 2分組對應的序列分別為0, 1, 2. 但是4, 5分組對應的序列號也為0, 1. 為了確保發送方的窗口能夠滑動, 接收方會緩存rcv_base - N的分組(已經確認), 以便再次收到這個范圍上的冗余分組時能夠發送一個對應的ACK.
  • 這時發送方發送的分組0帶有序號0, 而接收方的既期待分組4(序號為0), 又緩存有分組0(序號為0), 接收方將不知道收到的是分組0還是分組4.
  • 顯然, 如果窗口的長度比序號空間小1時, 可能出現接收方無法確定接受到的分組是一次重傳還是一個新的分組. - 書上給出的窗口取值范圍為: 窗口長度必須小於或等於序號空間大小的一半.

 

繼續是復習題

3.5節

R14. 是非判斷題

a. 主機A經過一條TCP連接向主機B發送一個大文件. 假設主機B沒有數據發往主機A. 因為主機B不能隨數據捎帶確認, 所以主機B將不向主機A發送確認.
答: 錯誤. 主機B將不向主機A發送確認這句話違背了一個可靠數據傳輸協議的基本原則. 首先要明確為了確保可靠的數據傳輸, 接收方向發送方發送確定報文是協議的一部分. 再分析推導出這一錯誤結論的原因: 主機B不能隨數據捎帶確認. 書本上提出捎帶的概念時, 給的是一個具體場景, 發送方要發送一個字符, 接收方要返回該字符進行回顯, 所以順便把確認信息放入到發給發送方的數據的報文段中. 要區分清楚確認和捎帶確認之間的關系.

b. 在整個連接的過程中, rwnd的長度決不會變化.
答: 錯誤. rwnd = RcvBuffer - [LastByteRcvd - LastByteRead]. rwnd表示的是接收窗口的大小, 這取決於接收方應用程序從緩存中讀取數據的速率和發送方發送的速率, 是會變化的.

c. 假設主機A通過一條TCP連接向主機B發送一個大文件. 主機A發送但未被確認的字節數不會超過接收緩存的大小.
答: 正確. 講的就是TCP的流量控制.

d. 假設主機A通過一條TCP連接向主機B發送一個大文件. 如果對於這條連接的一個報文段的序號為m, 則對於后繼報文段的序號將必然是m + 1.
答: 錯誤. 序號是根據TCP數據的字節流決定的, 而不是建立在報文序列之上. 后繼報文段的序號應該是m + n, 而n是最大報文段長度.

e. TCP報文段在它的首部中有一個rwnd字段.
答: 錯誤. TCP報文段有接收窗口字段, rwnd存放在接收窗口字段中.

f. 假定在一條TCP連接中最后的SampleRTT等於1秒, 那么對於該連接的TimeoutInterval的當前值必定大於等於1秒.
答: 正確. TimeoutInterval = EstimatedRTT + 4 * DevRTT.

g. 假設主機A通過一條TCP連接向主機B發送一個序號為38的4個字節的報文段. 在這個相同的報文段中, 確認號必定是42.
答: 錯誤. 概念性錯誤, 確認號是期待接收方發送的序號.

 

R15. 假設主機A通過一條TCP連接向主機B發送兩個緊挨着的TCP報文段. 第一個報文段的序號為90, 第二個報文段序號為110.

a. 第一個報文段中有多少數據?
答: 110 - 90 = 20個字節.

b. 假設第一個報文段丟失而第二個報文段到達主機B. 那么在主機B發往主機A的確認報文中, 確認號應該是多少?
答: 90. 在建立連接的時候主機B就知道要先接收90.

 

R16. 考慮在3.5節中討論的Talnet的例子.

  • 在用戶鍵入字符C數秒之后, 用戶又鍵入字符R. 那么在用戶鍵入字符R之后, 總共發送了多少個報文段, 這些報文段中的序號和確認字段應該填入什么?
  • 答: 由於用戶在鍵入C數秒后鍵入R, TCP沒有斷開連接. 所以鍵入R后有用戶到服務器和服務器到用戶兩個報文段. 用戶到服務器: 序號44, 確認80, 服務器到用戶: 序號80, 確認45

 

3.7節

R17. 假設兩條TCP連接存在於一個寬帶為Rbps的瓶頸鏈路上.

  • 它們都要發送一個很大的文件(以相同方向經過瓶頸鏈路), 並且兩者是同時開始發送文件. 那么TCP將為每條連接分配什么樣的傳輸速率?
  • 答: 這里涉及到2條TCP連接的公平性問題, 很難保證兩條TCP連接分配均等的傳輸速率. 但是2條TCP連接的速率之和是會在R/2~R之間浮動的. 至於哪條快一點, 哪條慢一點是不確定的.

 

R18. 是非判斷題. 考慮TCP的擁塞控制. 當發送方定時器超時時, 其ssthresh的值將被設置為原來值的一半.

  • 錯誤. 當發送方定時器超時時, TCP發送方將cwnd設置為1並重新開始慢啟動過程. 它還將第二個狀態變量的值ssthresh設置為cwnd/2, 即當檢測到擁塞時將ssthresh置為擁塞窗口值的一半.

 

R19. 在3.7節的"TCP分岔"討論中, 對於TCP分岔的響應時間, 斷言大約是4 * RTT(FE) + RTT(BE) + 處理時間. 評價該斷言.

  • 我認為該斷言是合理的. 它給出的是一條包含重要時延參數的公式. 在實際情況中, 某些參數可能可以省略, 但是在省略之前還是要經過考慮的.

 

插入對TCP協議的總結

  • 3.4節講的是如何從零創建起一個可靠高效的數據傳輸協議, 而3.5節基於3.4節的基礎具體講述TCP的實現, 這里有必要對TCP協議進行一個簡單的總結.

 

TCP三次握手

> 在講述三次握手之前先看看TCP報文段結構

 

  • 對於三次握手問題, 我們暫時不考慮報文段中的其他字段, 只看報文段中序號和確認號字段. 對這兩個字段有印象后, 下面開始描述一次完整的TCP三次握手過程.
  • 故事開始...
  1. 第一步: 客戶端TCP向服務器端的TCP發送一個特殊的TCP報文段, 該報文段不包含應用層數據. 報文段首部中的標志位SYN置1, 簡稱為SYN報文段. 同時客戶端隨機選取一個初始序列號client_isn, 放置於SYN報文段的序號字段中, 最后把該報文段經下層封裝發送給服務器. SYN的意思是: xxx服務器, 我想向你發起TCP連接, 我的初始序號為client_isn.
  2. 第二步: 服務器收到SYN報文段后, 響應一個SYNACK報文段. SYNACK報文段的SYN標志位置1, 確認號字段設置為client_isn + 1, 序號字段由服務器選擇自己的初始序號server_isn. SYNACK報文段的意思是: 我收到了你的SYN報文段, 序號為client_isn, 我同意該連接, 我自己的序號為server_isn.
  3. 第三步: 客戶端接收到SYNACK后要告知服務器自己收到了. 於是發送最后一個報文段, SYN標志位置0, 把確認字段設置為server_isn + 1, 並設置自己的序號. 這個報文意思是: 好的, 我知道你同意了, 我們開始傳輸數據吧.

 

在解釋序號和確認號之前先解釋一下為什么是三次握手而不是兩次握手.
  • 其實如果上面的過程理解了, 就能回答這個問題了, 不過這里有個漫畫幫助理解.
  • 先演示的是三次握手.

 

  • 接着演示兩次握手.
明白了為什么是三次握手而不是四次揮手后, 再來看TCP報文段中的序號和確認號.
  • 序號:
  • 序號是建立在傳送的字節流之上, 而不是建立在傳送的報文段的序列之上的. 意思是說如果把傳輸的數據看作是一個字節文本, 序號則是對文本的首字節進行編號.
  • 我知道還是很抽象, 看具體例子. 現在要通過TCP發送一個500000字節的大文件, 最大報文長度為1000(一個報文段最多只能裝1000個字節). 那么這次TCP傳輸就要分為500個報文段. 第一個報文段序號為0, 因為在500000字節中首字節的序號為0; 第二個報文段的序號為1000, 因為第一個報文段裝了1000個字節, 第二個報文段從第1000個字節開始裝起; 同理, 第三個報文段的序號為2000...
  • 為什么要使用序號?
  • 發送序號是為了告訴接收方, 下一次我將從哪個地方開始傳數據給你. 接收方同時也會期待下次從下一個字節序列的位置開始接收發送方的數據. 接收方會把這個位置寫到確認號中, 比如上面的例子, 接收方在接收到0序列分組后, 會在確認字段填入1000, 下一次期待接收1000位置的字節.
  • 由上可見: 主機A填充進報文段的確認號是主機A期望從主機B收到的下一字節的序號.

 

TCP四次揮手

- 四次揮手指的是TCP的兩端斷開連接時的四次報文段傳輸.

 

  1. 首先客戶端TCP向服務器發送一個特殊的TCP報文段, 其中FIN標志位被置1.
  2. 服務器收到該報文段后就向發送方發送一個確認報文段.
  3. 然后服務器發送自己的終止報文段, 同樣是把FIN位置1.
  4. 最后客戶端對服務器的終止報文段發送確認響應.
  • 兩次揮手行不行? 就是客戶端提出關閉, 服務器響應后TCP就結束.
  • 答: 不行, 因為客戶單方面提出關閉的話, 服務器還是可以向客戶端發送數據, 必須雙方都提出關閉並得到確認后TCP連接才算關閉.

擴展閱讀:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM