信道通常指鏈接信號發送方和接收方的傳輸線路,包括雙絞銅線等物理媒體。在實際的網絡傳輸中,信道是不可靠的,在其上傳輸的分組可能會損壞或丟失,甚至相對次序都不能保證。網絡
TCP 這一 運輸層協議 則提供了可靠的數據傳輸框架解決上述問題,其向上層(應用層)提供面向鏈接的可靠的服務。而TCP的下層是網絡層,網絡層提供的盡力而爲的服務,也就是說不提供任何質量保證。簡單來講,TCP實現如下兩個功能,即「可靠」的概念:傳輸的數據比特不會受到損壞或丟失;全部數據都是按照其發送順序進行交付的。框架
在本節中咱們僅討論單向數據傳輸(unidirectional data transfer)的狀況,介紹可靠傳輸協議、流水線協議、回退N步、選擇重傳。函數
rdt_send()
函數,調用數據傳輸協議的發送方,把數據交付給接收方的較高層。udt_send()
函數,將分組發送到不可靠的信道。rdt_recv()
。rdt
協議向較高層交付數據時,調用deliver_data()
。
有限狀態機(FSM) 能夠表示有限個狀態及在這些狀態之間的轉移和動做等行爲的數學模型,下圖即表示發送方和接收方的有限狀態機,底層信道是徹底可靠的,發送方和接收方有各自的FSM,每一個FSM都只有一個狀態。性能
(圖解:FSM描述中的箭頭指示了協議從一個狀態變遷到另外一個狀態。引發變遷的事件顯示在橫線的上方,事件發生時所採用的運動顯示在橫線的下方。FSM的初始狀態用虛線表示。)spa
rdt_send(data)
事件接收來自較高層的數據發送請求。在完成一次數據發送請求中須要兩個動做:
make_pkt(data)
產生)udt_send(packet)
發送到信道中rdt_rcv(packet)
事件從底層信道接收一個分組。在一次數據接收過程當中一樣須要兩個動做:
extract(packet, data)
產生)deliver_data(data)
動做)如今咱們能夠假設在信道傳輸過程當中分組中的比特可能受損了,在這種比特可能受損的狀況下,來看一下如何構造可靠數據傳輸協議 rdt 2.0。注意如今仍然假定全部發送的分組(即便受損)將按照其發送的順序被接收,同時信道中的分組也不會丟失。code
接收方收到分組之後,能夠向發送方發送確定確認 (ACK) 或否認確認 (NAK) 。這些控制報文可讓發送方知道哪些內容被正確接收,哪些內容接收有誤並所以須要重複。這種基於重傳的可靠數據傳輸協議被稱爲自動重傳請求 (Automatic Repeat reQuest, ARQ) 協議。blog
ARQ協議須要三個功能來處理分組出錯的狀況:接口
下圖爲rdt 2.0 的有限狀態機描述圖,該數據傳輸協議(自動重傳請求協議)採用了差錯檢測、確定確認與否認確認。事件
rdt_send(data)
事件時:
sndpkt = make_pkt(data, checksum)
產生一個包含待發送數據且帶有校驗和的分組udt_send(sndpkt)
發送到信道中rdt_rcv(rcvpkt) && isACK(rcvpkt)
),那麼發送方知道最近一個分組已經被正確接收,所以協議返回左邊狀態,繼續等待下一次由較高層傳下來的數據發送請求rdt_rcv(rcvpkt) && isNAK(rcvpkt)
),那麼發送端知道接收端接收到的分組是受損的,因此調用 udt_send(sndpkt)
從新發送該分組,而後狀態不變,繼續等待接收接收端的 ACK 或 NAK 分組。在上述協議中,當發送方處於等待ACK或NAK狀態時,它不能從上層得到更多數據。這樣子的協議被稱爲停等協議 (stop-and-wait)。資源
rdt_rcv(rcvpkt) && corrupt(rcvpkt)
,則返回 NAK 分組rdt_rcv(rcvpkt) && notcorrupt(rcvpkt)
,則返回 ACK 分組
rdt2.1 彷佛是一個能夠在有比特差錯信道上正常工做的可靠數據傳輸協議了,但仔細想一想,咱們沒有考慮 ACK 或 NAK 分組受損的狀況。
處理ACK/NAK分組損壞的方法有如下三種:
想要解決這個問題,咱們須要在數據分組中添加一個新的字段,而後讓發送端對其數據分組編號,將發送數據分組的序號放在該字段中。因而,接收端只須要檢查序號就能夠肯定收到的分組是不是一次從新傳送的分組。
咱們把引入序號的協議稱爲rdt 2.1
,FSM圖以下。
rdt_send(data)
事件,經過 sndpkt = make_pkt(0, data, checksum)
產生一個序號爲 0,包含待發送數據且帶有校驗和的分組,接着經過 udt_send(sndpkt)
將其發送到信道中,而後狀態變遷爲「等待接收接收端的 ACK 或 NAK 0」udt_send(sndpkt)
從新傳送剛纔的序號爲 0 的分組到信道中rdt_send(data)
事件,經過 sndpkt = make_pkt(1, data, checksum)
產生一個序號爲 1,包含待發送數據且帶有校驗和的分組,接着經過 udt_send(sndpkt)
將其發送到信道中,而後狀態變遷爲「等待接收接收端的 ACK 或 NAK 1」udt_send(sndpkt)
從新傳送剛纔的序號爲 1 的分組到信道中rdt_rcv(rcvpkt)
從底層信道接收了一個分組數據:
rdt_crv(rcvpkt) && corrupt(rcvpkt)
),那麼由 sndpkt = make_pkt(NAK, checksum)
產生一個附帶校驗和的 NAK 分組,接着由 udt_send(sndpkt)
發送回發送端rdt_rcv(rcvpkt) && notcorrupt(rcvpkt) && has_seq1(rcvpkt)
),那麼由 sndpkt = make_pkt(ACK, checksum)
產生一個附帶校驗和的 ACK 分組,接着由 udt_send(sndpkt)
發送回發送端rdt_rcv(rcvpkt) && notcorrupt(rcvpkt) && has_seq0(rcvpkt)
),那麼經過 extract(rcvpkt, data)
和 deliver_data(data)
將分組數據上傳給較高層程序。接着,由 sndpkt = make_pkt(ACK, checksum)
產生一個附帶校驗和的 ACK 分組,由 udt_send(sndpkt)
發送回發送端
協議rdt 2.1 使用了確定確認和否認確認。當接收到正確的分組時,接收方發送一個「確定確認」,當接收到受損的分組時,發送一個「否認確認」。若是不發送NAK,而是對上次正確接收的分組發送一個ACK,也能夠實現同樣的效果。發送方若是接收到兩個同樣的ACK,就說明接收方沒有正確接收到跟在被確認兩次的分組後面的分組。
咱們把只使用ACK的協議稱爲rdt 2.2
,其FSM圖以下。
具體 rdt 2.2 的流程由於和 rdt 2.1 基本相似,故不贅述。
在現實的網絡環境中,除了比特受損外,底層信道還會丟包;有不少可能的方法能夠解決丟包問題,這裏咱們讓發送方負責檢測和恢復丟包工做。
假定發送端傳輸一個數據分組,該分組發生丟失 或者 接收端對該分組的 ACK 發生了丟失。在這兩種狀況下,發送端都收不到應當到來的接收端的響應。因此,若是發送端願意等待足夠長的時間以肯定該分組缺失已丟失,則它只須要重傳該數據分組便可。
從發送端的觀點來看,重傳是一個萬能靈藥。爲了實現基於時間的重傳機制,須要一個倒數計時器 (countdown timer),在一個給定的時間量過時以後,可中斷髮送方。發送方須要作到:1)每次發送一個分組(包括第一次分組和重傳分組)時,就啓動一個定時器;2)相應定時器中斷;3)終止定時器。
下圖是rdt 3.0
的發送方FSM,該協議運行在可能發生出錯和丟失的信道上。
rdt 2.2 協議中的接收端有限狀態機描述圖仍然適用於 rdt 3.0 協議,下面我仍然用文字來簡要描述一下上圖中的發送端發送分組流程:
rdt_send(data)
事件,經過 sndpkt = make_pkt(0, data, checksum)
產生一個序號爲 0,包含待發送數據且帶有校驗和的分組,接着經過 udt_send(sndpkt)
將其發送到信道中並啓動定時器,而後狀態變遷爲「等待接收接收端的 ACK 0」corrupt(rcvpkt)
)或者收到了 ACK 1(即 isACK(rcvpkt, 1)
,也就是收到了本身發送的上一個分組的 ACK),則直接忽略udt_send(sndpkt)
從新發送該分組並從新啓動定時器
假設有兩臺主機,分別位於美國西海岸和東海岸,端對端延時 大約爲 30ms,假定它們經過一條速率 R 爲 1Gbps 的信道相連。包括首部字段和數據的分組長 L 爲 1KB (8000 bits),因此發送一個分組進入 1Gbps 鏈路實際所需
若是發送方在 t=0 時刻開始發送分組,則在 8μs 後,最後1bit進入了發送端信道。通過 15ms 後,分組的第一個 bit 到達接收端;在 15.008ms 時刻,分組的最後一個 bit 到達接收端。假設接收端的 ACK 產生和發送不佔用時間,則再通過 15ms 之後,即t=30.008ms,發送端接收到接收端的ACK,發送端能夠發送下一個分組。
在 30.008ms 內,發送方的發送使用了0.008ms。咱們定義信道利用率爲:發送方實際忙於發送比特到信道的時間與發送時間之比,則中止等待協議的發送方利用率爲:
在1G連路上每30毫秒才發送一個分組(33KB/s),這就是網絡協議限制了物理資源的利用的例子。
校驗和、序號、定時器、確定和否認確認分組,這些技術都在協議的運行中起到了必不可少的做用。如今 rdt 3.0 已是一個功能正確的協議,但由於它的本質仍然是停等協議,因此效率實在捉急。在本筆記的下篇中,將介紹流水線可靠數據傳輸協議、回退 N 步協議以及選擇重傳協議,最終將會獲得一個可靠而且效率較高的協議實現方法。