# 3.1 Introduction * Logical communication: 傳輸層協定為在不同主機上的應用處理程序 (processes) 之間提供**有邏輯的溝通**,即便他們在物理上可能是在世界的兩端 * 傳輸層協定是在**終端機**上運作 ## 傳輸層與網路層之間的關係 * 兄弟姊妹、房子、郵差:在東邊、西邊各有一個房子,房子裡各住著 12 個兄弟姊妹,他們每個人都寫信給另一邊的每一個兄弟姊妹。西邊的 Ann 跟東邊的 Bill 負責收集兄弟姊妹的信,交給郵差。等郵差送來另一邊寄來的信後,Ann 跟 Bill 又會把信分別交給每個人  * 傳輸層是在 processes 之間有邏輯地溝通 - 人 - segment * 網路層是在 hosts 之間有邏輯地溝通 - 地點 - package * 換了一對親戚 Susan 和 Harvey:常常漏掉信件,比喻不一樣的 protocal ## TCP 與 UDP * 責任:多路復用,多路分解 multiplexing and demultiplexing * UDP:和 IP 一樣,是不可靠的傳輸 * TCP:提供 "可靠的傳輸" 以及 "壅塞控制",需先設定 connection (參考 2.1.4 & 2.7) # 3.2 Multiplexing and Demultiplexing * Multiplexing: 把不同兄弟姊妹的信收集起來 > 用於處理多個 socket 內的資料並加上傳輸 header * Demultiplexing: 把信分送給對應的兄弟姊妹 > 用 header 中的資訊將 segment 傳輸到正確的 socket  * 如何辨識正確的 socket? * UDP 依靠兩個 identifier: Destination IP & Destination port > 擁有相同 dest IP & port,但是不同 source IP & port 的 segment 都會被送到同一個 socket  * TCP 依靠四個 identifier: Source IP & Port, Destination IP & Port 圖中三個 segment 都送到同一個 server (同 IP、port) 但是會被送到不同的 socket 中。(P3 > P4、P3 > P5、P2 > P6)  # 3.3 Connectionless Transport: UDP ## Connectionless * Why "connectionless"? > 收送端之間不會做 handshaking 這個動作、每個 UDP segment 都是獨立被處理的 * UDP 缺點:不可靠、segment 會遺失、順序會錯誤 * UDP 優點:少了 handshaking 減少延遲、無狀態、無壅塞控制(想多快就多快)、header 較小 * 哪些情境會偏好用到 UDP? * 網路電話/視訊、DNS、SNMP、HTTP/3 (在應用層建立所需的可靠性、壅塞控制)、WebRTC  ## UDP Sender & Receiver Actions Sender actions: 1. 接收應用層傳下來的訊息 2. 決定每個 UDP segment 要加上的 header 資訊 3. 建立 UDP segment 4. 將 segment 傳給 IP Receiver action: 1. 從 IP 拿到 segment 2. 檢查 header 中的 checksum 值 3. 將應用層訊息從 segment 中取出 4. 透過 socket 將訊息傳輸給應用層 ## UDP Checksum Checksum: 用來檢查資訊在傳輸的過程中是否有發生問題(例如:位元反轉)  接收方會去計算兩個數字的總和,再去和發送方送來的 checksum 做比較,若算出來的不一樣,表示資訊是有問題的。  # 3.4 Principles of Reliable Data Transfer (RDT) 在 UDP 中,資料會遺失、出錯、反轉,但是大多數時間我們會需要資料被正確的傳送,那有哪些機制可以去確保資料有正確傳送呢? * 資料是透過 "可靠的協定" (TCP) 在 "不可靠的通道" (IP) 之間 "雙向" 傳輸的 (即便對應用層來說,資料的傳輸是單向的) * 可靠協定的機制,主要是受到不可靠通道的各種特性的影響 (遺失、出錯、反轉) * 傳送方和接收方只能透過訊息來得知對方的狀態,就像是中間隔著簾幕一樣 * rdt 與其他層的介面 (interfaces)  * 本章會用 "有限狀態機" (finite state machine, FSM) 來解釋傳送方與接收方的狀態 * event: 造成狀態改變的事件 * actions: 狀態改變時採取的動作   ## rdt 1.0: reliable transfer + reliable channel 完全沒有位元錯誤、遺失的狀況 >> **傳送方和接收方不需要採取除了 "傳送" 或是 "接收" 這兩個動作以外的動作**,以狀態機來說明的話,就是只有單一狀態  ## rdt 2.0: channel with bit errors * checksum: 檢查錯誤 (前面提過了) * 檢查完錯誤了,要如何**從錯誤中復原**呢? >> **stop & wait** * acknowledgement (ACKs): 正面回覆 "我收到了" * negative acknowledgement (NAKs): 反面回覆 "我沒收到" * 當傳送方收到 NAK 時,"重新傳輸" 該訊息 * 沒有錯誤的情況 (not corrupted)  * 有錯誤的情況 (corrupted)  * rdt 2.0 的致命問題: **如果 ACK、NAK 本身就損毀了怎麼辦?** * 傳送方就不知道接收方出了什麼事 * 也不能單純重新傳送,因為可能會重覆傳兩次 * 解決方式: 增加 **sequence number** 機制 * 若 ACK、NAK 損壞,傳送方會重新傳送目前的封包 * 傳送方會在封包上多加上一個 **sequence number** 資訊 * 接收方依據此 sequence 判斷是否有重覆,若有則**丟棄該封包**,不傳到應用層 ## rdt 2.1: corrupted ACKs, NAKs * 傳送方有限狀態機: 有四種狀態:等待 call 0、等待 pck 0 的回應 (ACK/NAK)、等待 call 1、等待 pck 1 的回應  * 接收方有限狀態機  * rdt 2.1 傳送方: * 在封包上加上了 sequence number * (0, 1) 兩個序列就夠了,為什麼?(見補充) * 需要檢查收到的 ACK、NAK 是否損壞 * 兩倍的狀態:必須要**記得**目前期待的封包是 seq #1 或 #0 * rdt 2.1 接收方: * 需要檢查收到的封包是否為重覆的 (狀態會記得目前是期待 seq #1 或 #0) :::info **只需要兩個序列號(#1和#0)來檢測重複的原因如下:** * 滑動窗口大小:在 RDT 2.1 中,發送方維持大小為 1 的滑動窗口。這意味著一次只能發送一個未確認的封包。因此,只需要兩個序列號(#1和#0)就足以區分當前正在發送的封包(#1)和上一個發送的封包(#0)。 * 沒有並行傳輸:在大小為 1 的滑動窗口下,沒有封包可以同時傳輸。每個封包都是按順序發送的,發送方在發送下一個封包之前會等待接收方的確認。因此,不需要額外的序列號來跟踪多個未確認的封包。 ::: ## rdt 3.0 channel with errors and **loss** 傳輸的過程中不只會發生錯誤,還會**遺失封包**,前面所提過的 checksum、sequence numbers、ACKS、retransmission 有幫助,但是仍然還不夠。 解決方式:**傳送方會等待接收方傳來的 ACK「一段時間」。** 1. 如果在這段期間內沒有收到 ACK,傳送方會重新傳送該封包 2. 如果 pkt 或 ACK 只是延遲,而非遺失: * 重新傳送的封包就會變成重覆的 * 接收方必須針對「被 ACK」的封包進行編號 3. 使用倒數計時,在一定時間後介入 * rdt 3.0 傳送方有限狀態機:在送出封包之後啟動 timer,在收到封包之後停止 timer  * 用互動的方式來看: * (a) 沒有遺失的時候,很順的等待著正確 seq 的 ACK * (b) 當有 pkt 遺失的時候 (pkt1),傳送方會等待 timeout 結束後,再重新傳送一次 pkt1  * (**c**) 當有 ACK 遺失時 (ack1),傳送方一樣等待 timeout 結束,並重新傳送剛剛的 pkt1,不過此時,接收方就會發現是剛剛已經收過的 pkt1,他會重新傳送一次 ack1,但不會把此 pkt 傳到應用層。 * (d) 如果 ack1 只是延遲,而非遺失,當 timeout 結束後,傳送方仍然會重新傳送 pkt1,接收方也會照樣接收並偵測到重覆,並再次回傳 ack1。剛剛那個延遲的 ack1,過了一段時間後傳送方收到了,他會繼續傳送下一個 pkt0。至於重覆傳送的 ack1,因為傳送方已發送 pkt0,當他收到 ack1 時,就會直接忽略他。  * rdt 3.0 的問題:**效能不佳** 1. 延遲增加:每次發送封包後需要等待確認才能繼續發送下一個數據包。這導致了往返時間(Round-Trip Time, RTT)的增加 2. 無充分利用傳輸資源:在許多情況下,通信系統可能有能力同時處理多個數據包,但 RDT 3.0 只能一次傳一個,限制了這種並行性 * 解決方式:**Pipelining (管線化?)**,允許傳送方在接收到 ACK 之前,同時傳送多個封包,達到資源的充分利用。 * 必須增加 sequence number 標記不同封包 * 傳送方 (或接收方) 必須做緩衝  ## Go-back-N: cumulative ACKs * 滑動窗口 (window):用於控制並調節數據包的傳輸流量。這個窗口包含了一定數量的連續序列號的數據包,**表示發送方可以向接收方發送的範圍**。滑動窗口的大小是固定的,通常由協議或系統參數指定。  * 發送方操作: * 發送方維護一個固定大小的滑動窗口,窗口內存放尚未收到確認的數據包。 * 發送方將窗口內的數據包按照順序發送到接收方,並記錄每個數據包的序列號。 * 一旦發送方發送了窗口內的所有數據包,它開始等待來自接收方的確認。 * 接收方操作: * 接收方收到發送方的數據包後,檢查序列號是否按照順序。如果序列號正確,則接收方將該數據包交付給應用層,並向發送方發送確認。 * 如果接收方收到的數據包的序列號與期望的序列號不匹配,則接收方丟棄該數據包,並向發送方**發送重複確認 (cumulative ACK)。** * 接收方維護一個期望收到的下一個數據包的序列號,當它收到一個序列號為期望序列號的數據包時,它將期望序列號增加1。 以下圖的互動為例 (注意左邊窗口的移動): 1. 傳送方維護一個 N=4 的窗口,他會同時傳送 pkt0 到 pkt4 2. pkt2 在傳送過程當中遺失了 3. 接收方收到 pkt0、pkt1、pkt3,但是因為他沒有收到 pkt2,所以當他收到 pkt3 的時候會將他遺棄,並**再次傳送重覆的 ack1給傳送方** 4. 傳送方收到 ack0,將窗口往後移,傳送 pkt4 5. 傳送方收到 ack1,再次將窗口往後移,傳送 pkt5 6. 傳送方收到剛剛重覆的 ack1,忽略他 7. 接收方也收到了新傳來的 pkt4 & pkt5,但因為還是沒有收到 pkt2,所以這兩個都會被遺棄,**並重新傳送重覆的 ack1** 8. 此時,pkt2 的 timeout 時間到,傳送方重新傳送 pkt2、pkt3、pkt4、pkt5 (窗口內所有的 pkt) 9. 後續接收方正確收到了 pkt2、pkt3、pkt4、pkt5,因為有按照序列,因此會將這些 pkt 都提交給應用層,並發出對應的 ack  ## Selective Repeat: individual ACK 與 Go-back-N 不同,當接收方檢測到丟失的封包時,**傳送方只重新傳輸丟失的那些數據包,而不是整個窗口的封包**。此外,接收方會**緩衝(而非丟棄)**,有正確傳輸的封包,值到封包按照序列傳輸成功時,再一次提交到應用層。 以下圖的互動來比較 Go-back-N 和 Selective Repeat: 1. 傳送方維護一個 N=4 的窗口,他會同時傳送 pkt0 到 pkt4 2. pkt2 在傳送過程當中遺失了 3. 接收方收到 pkt0、pkt1、pkt3,他沒有收到 pkt2,但此時,與 Go-back-N 不同,**他會單獨傳送 ack3 給傳送方,並將收到的 pkt3 緩衝,先不提交給應用層** 4. 傳送方收到 ack0,將窗口往後移,傳送 pkt4 5. 傳送方收到 ack1,再次將窗口往後移,傳送 pkt5 6. 傳送方收到 ack3,**因為還沒收到 ack2,不會將窗口往後移**,會記錄他收到了 ack3 7. 接收方收到了新傳來的 pkt4 & pkt5,但因為還是沒有收到 pkt2,**所以這兩個都會被緩衝,並傳送各自對應的 ack4 & ack5** 8. 此時,pkt2 的 timeout 時間到,傳送方**只會重新傳送 pkt2,而非窗口內所有的 pkt** 9. 當接收方收到了 pkt2,他會將 pkt 提交,**並同時將緩衝的 pkt3 ~ pkt5 提交給應用層**,並回傳 ack2 10. 提問:當 ack2 抵達的時候會發生什麼事? 11. 答:ack2 抵達,將窗口後移,傳送下一個 pkt6  # 3.5 Connection-Oriented Transport: TCP ## TCP Overview * Point-to-point: 只有一個傳送方,一個接收方 * Reliable, in-order **byte stream**: TCP 不保留 message boundaries (訊息邊界),相反,它將資料視為連續的位元組串流,並且在資料中沒有提供任何明確的指示表明一個訊息的結尾和下一個訊息的開始。因此,接收端必須依賴於更高層次的應用邏輯或訊息框架技術來確定接收到的資料中的訊息邊界。 * Full duplex data: 在同一個連線中的雙向傳輸,MSS (Maximun Segment Size) * Cumulative ACKs: 重覆回傳同一個 ACK * Pipelining: 用滑動窗口來同時傳送多個封包 * Connection-oriented: 在交換資料前要先有 handshaking 動作 * Flow control: 流量控制,傳送方不會過度傳送封包 :::info **補充:訊息邊界的處理** 假設有一個即時聊天應用程式,它使用TCP來傳輸訊息。每個訊息可能由不同的用戶發送,並且在傳送過程中,它們可能會被TCP分割成多個TCP數據包,甚至可能會與其他訊息的片段混在一起。 當接收端接收到TCP數據包時,它無法直接知道這些數據包中的哪些部分屬於同一個訊息,因為TCP沒有提供訊息邊界的概念。因此,接收端的應用程式需要在接收到足夠的數據後,根據應用程式的訊息格式或其他協定約定的規則,來識別出一個完整的訊息。 例如,應用程式可以使用特定的結束符號(例如換行符號 \n)來標識每個訊息的結尾。當接收端接收到TCP數據包後,它可以逐個字節地檢查數據,直到找到結束符號為止,這樣就可以確定接收到了一個完整的訊息。然後,它可以將該訊息交付給應用程式進行處理。 ::: ## TCP Segment Structure  ### TCP Sequence numbers & ACKs * Sequence numbers: 標記傳輸中的第一個 byte 序列號 * ACK: 標記傳送方期待的下一個 byte 的序列號  * 當回傳的 ACK = seq + 1 時,表示傳輸中的封包有依照正確序列傳送,若無,則根據各種機制去復原這個錯誤(例如 timeout 後重傳) ## TCP Round trip time (RTT) & timeout TCP 是如何去計算 Retransmission Timeout (RTO) (也是差不多一個 RTT) 究竟要設多久時間?因為設定的**太快**會導致太多延遲的封包被重傳,導致資源浪費,**太慢**的話又會讓 TCP 對遺失的反應速度太慢。 * SampleRTT: 計算一個封包從送出到收到 ACK 的時間 * 但不同封包之間的 RTT 可能差異很大,所以要計算 "平均值" * 還有其他哩哩扣扣的演算法,不過總之,TCP 會根據 RTT 測量和網絡條件動態調整 timeout 時間 (RTO)。例如,如果收到確認比估計的 RTT 快得多,TCP 可能會減少 RTO 以避免不必要的延遲。相反,如果確認被延遲了很長時間,TCP 可能會增加 RTO 以防止過早的重傳。 ## TCP Sender (有限狀態機 event & actions) 1. Event: 接收到應用層傳來的 data Actions: 1. 建立 seq 為 # 的封包 2. 啟動 timer 2. Event: timeout Actions: 1. 重新傳送造成該 timeout 的封包 2. 重啟 timer 3. Event: 收到 ACK Actions: 1. 如果符合預期的 ACK,更新下一個預期的 ACK,如果有尚未被 ACK 的封包,重啟 timer ## TCP Receiver  ## TCP Retransmission scenarios 1. Lost ACK: 回傳的 ACK 遺失了,傳送方在 timeout 後再次傳送當前的封包 2. Premeture timeout: 設定的 timeout 時間過短,在收回 ACK 以前就時間到,並重新傳送一次 timeout 的封包,當接收方收到再次傳來的封包時,會傳送一個 cumulative ACK for 剛才收到的兩個封包。一次傳送針對兩個封包的 cumulative ACK 的好處是,**如果前面那一個 ACK 遺失的話,傳送方還是可以透過 cumulative ACK 知道接收方已正確接收前面的封包**,並繼續傳送接下來的封包。  ### TCP fast retransmit 傳送方傳送四個封包,序列中的第二個遺失了,接收方於是傳送了三個 duplicate ACKs (針對序列中第一個封包)。當傳送方收到三個重覆的 ACK,**即便 timeout 尚未結束,傳送方還是能夠偵測到封包遺失,並快速的重新傳送封包**,傳輸更快地從遺失中復原。  ## TCP Flow control 接收方從網路層把 datagram 送到傳輸層緩衝區,然後應用層再把資料從 TCP socket 中取出。如果今天**網路層傳輸的速度大於應用層取出資料的速度會發生什麼事?** >>> **塞車!** 所以,流量控制 (Flow control) 允許**接收方去控制傳送方**,不要傳送太多或太快。 * 接收方在 TCP segment 中的 rwnd 欄位中告訴傳送方他有多少可用的緩衝空間,讓傳送方將 unACKed 的封包數量不得超過接收方的 rwnd 值,確保接收端的緩衝區不會爆炸! * RcvBuffer 的值由 socket 的設定決定,典型的預設值是 4096 bytes  ## TCP Reliability ### Problem of 2 way handshake 在人類的溝通中,只要一個人說安安在嗎,另外一個人說在啊,就可以建立一個有效的溝通,但是在網路的情境中,可能會有**延遲、重傳、順序調換**的問題,而且溝通中的機器彼此是 "看不見" 彼此的。 1. client 傳送連接要求,server 收到了,回傳接收連接的訊息,client 收到接受訊息,開始傳送資訊 x + 1,接著 server 收到了 x + 1,會傳 ACK x + 1。 此時雙方都沒有出現任何問題 2. 假設今天在同樣的情境裡,client 因為種種原因而**再次傳送了第二次連接要求**,當 server 收到這個第二次連接要求並回傳接收連接的訊息時,前面一個 connection 已經被關閉了,此時**已經沒有 client** 在聆聽任何的訊息 3. 接續上一個情境,如果不只連接要求被重新傳送,連接下來的 data x + 1 也被重新傳送一次會如何呢?在 server 端,因為接收了第二個連接請求而再次建立了 connection,並順著接下了第二次的 data x + 1。於是 data x + 1 被傳輸了兩次!如果這個是刷卡的動作的話,等於是被扣了兩次錢  ### Three way handshake 所以只有雙向交握是不夠的,需要三向。從 server 端來看,當他接收到 client 傳來的連線請求時,**不會直接進入 ESTAB 狀態,而是會先進入 SYN RVD 狀態**,等到再次收到 client 傳來的 ACK 時,表示 client 有正確收到回應,此時才會正式進入 ESTAB 狀態,連線建立完成。  ### Closing a connection * 要關閉連線時,由 client 端發送包含 FIN bit=1 的 segment * Server 端收到含有 FIN bit=1 的訊息時,會傳送 ACK 並繼續把還沒傳完的資料傳一傳。當剩下的資料傳完後,由 server 端發送含有 FIN bit=1 的訊息。 * Client 收到 server 傳來的 FIN,回傳 ACK 跟 server 說收到了,而 server 在收到這個 ACK 之後就會關閉連線。 * Client 端會再等待一小段時間,確定 server 有收到剛才的 ACK (=已經關閉),並關閉連線。 # 3.6 Principles of Congestion Control ## Flow control v.s. congestion control * 流量控制 (Flow control) 用來預防發送者傳送過多的流量使接收者超出可負荷的容量 * 壅塞控制 (Congestion control) 用來預防網路被注入過多的資料而造成 router/ switch 或 link 超出負荷 # 3.7 TCP Congestion Control 簡單看過就好 * TCP壅塞控制的概念是讓每個傳送端決定目前網路有多大容量,以便知道自己可以同時傳送多少封包 * TCP 被稱為自我時序調節 (self-clocking) ,也就是透過 ACKs 來調節封包傳輸的節奏 * TCP 使用 **線性增加倍數減少法** Additive Increase Multiplicative Decrease (AIMD) 來進行壅塞控制   # 3.8 Evolution of Transport-Layer Functionality ## HTTP/3 & QUIC: Quick UDP Internet Connections * QUIC 是建立在 UDP 上的應用層協定,用來提升 HTTP 的效能 * 常見於 Google 伺服器上 (Chrome、mobile YouTube APP) * QUIC 在應用層實作了很多 TCP 的可靠性措施:**連線建立、遺失錯誤處理、壅塞控制**等等 * 可以允許多個應用層 "stream" 匯流到單一個 QUIC 連線中  ### Single handshake 在 HTTP/2 中,需要經過兩次的 handshake (TCP、TLS) 才能開始傳輸資訊。但是在 QUIC 當中,只需要一次就可以把 handshake 完成,用來加速資料傳輸的速率。  ### No HOL blocking :::info **HOL Blocking (head of line blocking) 隊頭阻塞** 採用 HTTP/2 時,典型的瀏覽器通過單個 TCP 連接進行數十或數百個並行傳輸。 如果 HTTP/2 連接雙方的網路中有一個封包丟失,或者任何一方的網路出現中斷,**整個TCP連接就會暫停,丟失的封包需要被重新傳輸。** 因為 TCP 是一個按序傳輸的鏈,因此,如果其中一個點丟失了,在**該點之後的所有內容將陷入等待**。 這種單一封包造成的阻塞,就是所謂 TCP 上的隊頭阻塞( head of line blocking )。 ::: 使用 QUIC 時,兩個終端間仍建立**一個連接**,該連接也經過協商使得資料得到安全且可靠的傳輸。 但是,當我們在這個連接上建立兩個不同的資料串流時,它們將被視為互相獨立。也就是說,如果其中一個資料串流遺失封包了,那只有該資料串流必須停下來,然後等待重傳。 下面為兩終端間的示意圖,黃色與藍色是兩個獨立的資料串流。  以下圖例子來說,client 向 server 請求三個資源,在 HTTP 1.1 情境,若第二個資源在傳輸的過程中發生錯誤,那麼第三個資源必須在第二個資源復原之後,才能進行傳送。 在 QUIC 的話,當第二個資源發生錯誤時,第三個資源可以在第二個資源重新傳輸、復原的過程當中被傳送,不用再傻傻等待。  :::info **Difference between HTTP level HOL blocking & TCP HOL blocking** One form of HOL blocking in HTTP/1.1 is **when the number of allowed parallel requests in the browser is used up**, and subsequent requests need to wait for the former ones to complete. HTTP/2 addresses this issue through request multiplexing, which eliminates HOL blocking at the application layer, but HOL still exists at the transport (TCP) layer. :::
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up