# TCP - Retransmission
> 這篇文章中簡介幾個 TCP 重傳的情境。
[TOC]
## 課程影片
### 第 8G 講 TCP 與網路阻塞偵測與控制技術 L08 7
{%youtube lavgdjfEFNE %}
### How TCP Works - Duplicate Acknowledgments
{%youtube KWiHAMDbrng %}
## 前情提要:TCP 的 ACK 機制
### 原則一:ACK 的流水號 = 預計收到的下個位元組的流水號
TCP 以 *byte* 為單位,使用 *cumilated ACK*。也就是說:若接收方回覆的 ACK 中的流水號是 $n$,就表示「流水號嚴格小於 $n$ 的 *byte* 都有收到」,或者是說「下一個期待收到的位元是 $n$」。
### 原則二:收到封包一定要 ACK
除了這之外,只要收到封包,就一定要 ACK --- 就算已經收過這個封包也一樣。而不管是哪一個情境,收到封包要回的 ACK 一定是依照上一個原則,回覆「預計下一個收到的位元組的流水號」。
## 幾種重傳情境
### 情境一:Lost ACK
一個罪名線的狀況是:在時限結束前都沒有收到那個封包的 ACK。一個可能的情境像這樣:

在這個情況中:
1. 接收方收到收到 `(seq=70, len=20)` 的封包。
2. 接收方回覆 `ACK=90`。
3. 接收方的 `ACK=90` 封包遺失。
接下來,因為傳輸方沒有收到 `ACK=90`,所以把 `(seq=70, len=20)` 的封包重新傳輸。接收方在收到之後,回覆 `ACK=90`。
### 情境二:Cumilated ACK
不過,遺失 ACK 不一定表示需要重傳。這是因為 TCP 採用 *cumilated ACK*,ACK 某個封包時,其實就是在 ACK 所有前面的封包。所以即使前面有 ACK 封包遺失,只要後面的 ACK 有送到,那麼這個後到的 ACK 就足以讓發送方知道前面的封包已經送達。比如說以下的情境:

1. 發送方依序傳送 `(seq=70, len=20)` 跟 `(seq=90, len=10)` 的封包。
2. 接收方確實收到這兩個封包,並且依序回傳 `ACK=90` 與 `ACK=100`。
3. `ACK=100` 的封包先到,但是 `ACK=90` 的封包遺失。
這時候,因為採用 *cumilated ACK*,所以發送方一但看到 `ACK=100` ,就知道「流水號嚴格小於 `100` 的那些位元組都收到了」,而這也包含流水號 `70` 到 `89` 的位元組。因此,即使沒有收到 `(seq=70, len=20)` 封包的 `ACK=90`,只要收到 `ACK=100`,發送方也可以知道 `(seq=70, len=20)` 已經順利送達,所以就不需要重傳。
### 情境三:Delayed ACK
ACK 除了可能少之外,也有可能多。比如說接收方雖然有發送了 ACK,但來不及在原先的 *Timeout* 前抵達,因此在下一個 *timeout* 時間區間內抵達的時候。這時候在發送方看來,就會像是「莫名其妙多出了很多個 ACK」。比如說這個情境:
1. 發送方依序傳送 `(seq=70, len=20)` 跟 `(seq=90, len=10)` 的封包。
2. 接收方確實收到這兩個封包,並且依序回傳 `ACK=90` 與 `ACK=100`。
3. 這兩個 ACK 太慢,沒有在時間內送達。
4. 因為沒有在時間內送達,所以發送方重新發送。第一個封包是 `(seq=70, len=20)` 的封包。
5. 重傳的 `(seq=70, len=20)` 順利送達。因為「收到封包一定要 ACK」,就算這個封包的內容已經收過。所以接收方準備 ACK:

這個狀況可以使用 *cumilated ACK* 化解。在接收方收到重複的封包準備 ACK 時:
6. 因為接收方已經已經收到 `(seq=70, len=20)` 與 `(seq=90, len=10)` 封包了,所以接下來預期會收到 `seq=100` 的封包。因此在這個 ACK 當中,就要回覆 `ACK=100`。
7. (在這個情境中) 假定 `ACK=100` 被發送方收到。這時因為採用 *cumilated ACK*,所以即時前面的 `ACK=90` 與第一次的 `ACK=100` 通通遺失,只要第二個 `ACK=100` 有確實收到,就可以當作流水號比 `100` 小的位元組都有順利收到。
### 情境四:Duplicated ACK
如果有一個流水號的封包一直沒收到,那接收方就會一直在 ACK 裡面的流水號說「它期待收到的那個封包」的流水號 --- 就算更後面的封包已經送達。像下面這樣:

在這個例子中,在一次 *timeout* 裡面,傳送了 5 個封包。其中:
1. 每個封包都傳 1 個位元組,因此流水號各自是 1 ~ 5。
2. 只有流水號為 2 封包沒有送達,而其他封包都送達了。
這時,依照前面的傳輸原則:每收到一個封包,都一定要 ACK 下一個期待收到的流水號(在這個例子中,就是一直沒收到的 2 號封包)。所以:
1. 在收到編號 3、4、5 的封包時,接收方都會回 `ACK=2`。
2. 如果這時發送方什麼都沒做,那麼就要一直到逾時之後,才會發現流水號 2 的封包沒有送達。因此就需要在逾時之後重送。
3. 接收方收到重送之後,因為流水號 1 ~ 5 的封包都收齊了,所以接收方期待下一個收到的封包具有流水號 6。因此回覆 `ACK=6`。
## Fast Retransmission
在上面的例子中,可以發現:如果發送方發現自己收到非常多重複的 ACK,那就表示那個流水號的封包可能一直沒有收到。既然接收方都喊這麼多次沒收到同樣的封包了,不如就直接在逾時之前重送這個封包,不用等逾時後才重送。
因此,在 *TCP* 當中,如果在逾時之前,就發現某一個流水號的 ACK 重複了 3 次,那麼就連逾時都不等,直接補送一份過去。
## Wireshark Tips
### Patterns in TCP Retransmissions (SF18US)
{%youtube yFlgTWRNdhg %}
### Going down the retransmission hole (SF20V)
{%youtube YupQjxPyuUQ %}