01-4. TCP vs UDP

⏱️ 閱讀時間: 12 分鐘 🎯 難度: ⭐⭐ (簡單)


🎯 本篇重點

深入理解 TCP 和 UDP 的差異、使用場景、三次握手流程,以及如何選擇適合的協定。


🤔 TCP 和 UDP 是什麼?

TCP (Transmission Control Protocol) = 可靠的傳輸協定 UDP (User Datagram Protocol) = 快速的傳輸協定

一句話解釋: TCP 像掛號信(確保送達、有順序),UDP 像廣播(快速但不保證收到)。


📮 用寄信來比喻 TCP vs UDP

TCP = 掛號信

你要寄重要文件給朋友:

步驟 1:確認地址(建立連線)
你:我要寄信給 123 號,有這個地址嗎?
郵局:有,可以寄

步驟 2:寄信(傳輸資料)
郵局:編號 1 號包裹
郵局:編號 2 號包裹
郵局:編號 3 號包裹

步驟 3:確認收到(回覆確認)
朋友:收到 1 號 ✅
朋友:收到 2 號 ✅
朋友:收到 3 號 ✅

步驟 4:完成(關閉連線)
你:所有包裹都寄完了
朋友:確認收到全部

優點:
✅ 保證送達
✅ 保證順序
✅ 可以重寄

缺點:
❌ 慢(需要確認)
❌ 開銷大(需要建立連線)

UDP = 廣播

你用喇叭廣播通知大家:

步驟 1:直接喊話(無需建立連線)
你:大家注意!現在下雨了!

步驟 2:不管有沒有人聽到
- 可能有人聽到
- 可能沒人聽到
- 可能只聽到一半

優點:
✅ 快速(不用確認)
✅ 簡單(不用建立連線)
✅ 低開銷

缺點:
❌ 不保證送達
❌ 不保證順序
❌ 不會重傳

🔍 TCP vs UDP 完整對比

核心差異表

特性TCPUDP
連線方式面向連線(Connection-oriented)無連線(Connectionless)
可靠性可靠(保證送達)不可靠(可能遺失)
順序性保證順序不保證順序
速度
開銷大(需要握手、確認)小(直接傳送)
錯誤檢查有(並重傳)有檢查但不重傳
流量控制
擁塞控制
資料單位Segment(區段)Datagram(資料包)
標頭大小20-60 bytes8 bytes

使用場景對比

場景用 TCP用 UDP
網頁瀏覽
電子郵件
檔案下載
視訊通話
線上遊戲
直播串流
DNS 查詢
VoIP 語音

🤝 TCP 三次握手(Three-Way Handshake)

為什麼需要三次握手?

建立可靠連線需要雙方確認:

  1. 客戶端能發送
  2. 伺服器能接收和發送
  3. 客戶端能接收

三次握手流程

【第一次握手 - SYN】
客戶端 → 伺服器:你好,我想連線(SYN)
- 發送 SYN 標記
- 序號 = 100

【第二次握手 - SYN-ACK】
伺服器 → 客戶端:好的,我收到了,我也想連線(SYN-ACK)
- 發送 SYN + ACK 標記
- 確認號 = 101(客戶端序號 + 1)
- 序號 = 200

【第三次握手 - ACK】
客戶端 → 伺服器:確認,開始傳輸資料(ACK)
- 發送 ACK 標記
- 確認號 = 201(伺服器序號 + 1)

連線建立!開始傳輸資料

用生活比喻理解

類比:打電話

第一次握手(SYN):
你:喂?(拿起電話撥號)

第二次握手(SYN-ACK):
朋友:喂?我是小明(接起電話)

第三次握手(ACK):
你:嗨小明,我是小華(確認身份)

現在可以開始對話了!

為什麼不是兩次握手?

如果只有兩次握手:

客戶端 → 伺服器:我想連線(SYN)
伺服器 → 客戶端:好的(SYN-ACK)

問題:
如果客戶端發送的第一個 SYN 在網路延遲...
過了很久才到達伺服器...
伺服器以為是新連線,就建立連接...
但客戶端早就放棄了!

結果:伺服器白白浪費資源等待

三次握手可以避免這個問題:
客戶端會發送最後的 ACK 確認,伺服器才真正建立連線

🔚 TCP 四次揮手(Four-Way Handshake)

為什麼需要四次揮手?

關閉連線時,雙方都要確認沒有資料要傳了。


四次揮手流程

【第一次揮手 - FIN】
客戶端 → 伺服器:我沒資料要傳了(FIN)
- 發送 FIN 標記

【第二次揮手 - ACK】
伺服器 → 客戶端:好的,我知道了(ACK)
- 發送 ACK 標記
- 但伺服器可能還有資料要傳

【第三次揮手 - FIN】
伺服器 → 客戶端:我也沒資料要傳了(FIN)
- 發送 FIN 標記

【第四次揮手 - ACK】
客戶端 → 伺服器:好的,確認關閉(ACK)
- 發送 ACK 標記

連線關閉!

用生活比喻理解

類比:結束電話

第一次揮手(FIN):
你:我說完了,先這樣(準備掛電話)

第二次揮手(ACK):
朋友:好的,我知道了(但他還想說)

第三次揮手(FIN):
朋友:我也說完了,掛了吧(真的要掛了)

第四次揮手(ACK):
你:好,再見(真的掛電話)

電話結束!

💻 實戰範例

TCP Socket 程式範例(Python)

# TCP 伺服器
import socket

# 建立 TCP Socket
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(('localhost', 8080))
server.listen(5)  # 最多 5 個等待連線
print("TCP 伺服器啟動,等待連線...")

while True:
    # 接受連線(三次握手發生在這裡)
    client, addr = server.accept()
    print(f"客戶端連線:{addr}")

    # 接收資料
    data = client.recv(1024)
    print(f"收到:{data.decode()}")

    # 回傳資料
    client.send("伺服器收到了!".encode())

    # 關閉連線(四次揮手發生在這裡)
    client.close()
# TCP 客戶端
import socket

# 建立 TCP Socket
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 連線到伺服器(三次握手)
client.connect(('localhost', 8080))

# 發送資料
client.send("Hello TCP!".encode())

# 接收回應
response = client.recv(1024)
print(f"伺服器回應:{response.decode()}")

# 關閉連線(四次揮手)
client.close()

UDP Socket 程式範例(Python)

# UDP 伺服器
import socket

# 建立 UDP Socket
server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server.bind(('localhost', 8080))
print("UDP 伺服器啟動,等待資料...")

while True:
    # 接收資料(無需建立連線)
    data, addr = server.recvfrom(1024)
    print(f"收到來自 {addr} 的訊息:{data.decode()}")

    # 發送回應(直接發送,不確認)
    server.sendto("伺服器收到了!".encode(), addr)
# UDP 客戶端
import socket

# 建立 UDP Socket
client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# 發送資料(無需連線)
client.sendto("Hello UDP!".encode(), ('localhost', 8080))

# 接收回應(可能收到,可能沒收到)
try:
    client.settimeout(2)  # 設定 2 秒逾時
    response, addr = client.recvfrom(1024)
    print(f"伺服器回應:{response.decode()}")
except socket.timeout:
    print("沒有收到回應(UDP 特性)")

# 關閉 Socket
client.close()

🎬 完整流程範例

TCP 傳輸網頁(可靠)

你瀏覽 www.google.com

步驟 1:三次握手(建立連線)
1. 瀏覽器 → Google:SYN
2. Google → 瀏覽器:SYN-ACK
3. 瀏覽器 → Google:ACK
✅ 連線建立

步驟 2:發送 HTTP 請求
瀏覽器 → Google:GET /index.html HTTP/1.1
Google → 瀏覽器:ACK(確認收到)

步驟 3:接收 HTTP 回應
Google → 瀏覽器:HTTP/1.1 200 OK + 網頁內容
瀏覽器 → Google:ACK(確認收到)

步驟 4:四次揮手(關閉連線)
1. 瀏覽器 → Google:FIN
2. Google → 瀏覽器:ACK
3. Google → 瀏覽器:FIN
4. 瀏覽器 → Google:ACK
✅ 連線關閉

結果:網頁完整顯示,沒有遺失任何資料

UDP 傳輸視訊通話(快速)

你用 Zoom 視訊通話

步驟 1:直接傳輸(無需握手)
你的電腦 → 對方電腦:視訊封包 1
你的電腦 → 對方電腦:視訊封包 2
你的電腦 → 對方電腦:視訊封包 3

步驟 2:不確認收到
- 封包 1:送達 ✅
- 封包 2:遺失 ❌
- 封包 3:送達 ✅

結果:
- 畫面稍微卡頓(封包 2 遺失)
- 但對話繼續(即時性重要)
- 不會重傳(太慢了)

如果用 TCP:
- 封包 2 遺失 → 等待重傳 → 畫面停住
- 用戶體驗很差

🤔 如何選擇 TCP 還是 UDP?

決策流程圖

開始
  ↓
需要保證資料完整性嗎?
  ├─ 是 → 用 TCP
  │        ├─ 網頁瀏覽
  │        ├─ 電子郵件
  │        ├─ 檔案傳輸
  │        └─ API 呼叫
  │
  └─ 否 → 需要即時性嗎?
           ├─ 是 → 用 UDP
           │        ├─ 視訊通話
           │        ├─ 線上遊戲
           │        ├─ 直播串流
           │        └─ VoIP
           │
           └─ 否 → 視情況
                    └─ DNS(UDP 優先,失敗才用 TCP)

實際案例分析

案例 1:下載 1GB 檔案

需求:
- 檔案完整(不能少一個 byte)
- 速度不是最重要
- 可以接受稍慢

選擇:TCP ✅
理由:
- 保證每個 byte 都正確
- 自動重傳錯誤封包
- 有流量控制(不會塞爆網路)

案例 2:視訊會議

需求:
- 即時性最重要(延遲 < 200ms)
- 少量畫格遺失可接受
- 不能卡頓

選擇:UDP ✅
理由:
- 快速傳輸,無需確認
- 遺失少量封包不影響整體
- 如果用 TCP 等待重傳會更卡

案例 3:線上遊戲

需求:
- 即時反應(延遲 < 50ms)
- 遺失少量位置更新可接受
- 但關鍵操作(攻擊、施法)要確保送達

選擇:混合 ✅
理由:
- 玩家位置更新 → UDP(快速)
- 攻擊指令 → TCP(可靠)
- 或者用 UDP + 應用層確認機制

案例 4:DNS 查詢

需求:
- 快速查詢(< 100ms)
- 資料量小(通常 < 512 bytes)
- 偶爾失敗可接受(重試即可)

選擇:UDP(優先)✅
理由:
- 快速簡單
- 資料量小,不容易出錯
- 失敗就重試(應用層處理)
- 如果回應太大(> 512 bytes),改用 TCP

📊 TCP vs UDP 效能比較

傳輸速度測試

import socket
import time

def test_tcp_speed():
    """測試 TCP 傳輸速度"""
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.bind(('localhost', 8080))
    server.listen(1)

    client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    client_socket.connect(('localhost', 8080))

    conn, _ = server.accept()

    # 傳輸 1MB 資料
    data = b'x' * 1_000_000
    start = time.time()
    client_socket.send(data)
    conn.recv(1_000_000)
    end = time.time()

    print(f"TCP 傳輸時間:{end - start:.4f} 秒")

    client_socket.close()
    conn.close()
    server.close()

def test_udp_speed():
    """測試 UDP 傳輸速度"""
    server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    server.bind(('localhost', 8080))

    client_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

    # 傳輸 1MB 資料(分批,UDP 有大小限制)
    chunk_size = 65507  # UDP 最大封包大小
    data = b'x' * chunk_size

    start = time.time()
    for _ in range(1_000_000 // chunk_size):
        client_socket.sendto(data, ('localhost', 8080))
        server.recvfrom(chunk_size)
    end = time.time()

    print(f"UDP 傳輸時間:{end - start:.4f} 秒")

    client_socket.close()
    server.close()

# 執行測試
test_tcp_speed()  # 約 0.15 秒
test_udp_speed()  # 約 0.08 秒(快 2 倍)

🎓 面試常見問題

Q1:TCP 為什麼需要三次握手?兩次不行嗎?

A:不行,原因有兩個:

1. 防止舊連線請求
   - 如果只有兩次握手
   - 舊的 SYN 在網路延遲後到達
   - 伺服器會誤以為是新連線
   - 浪費資源

2. 確認雙方都能收發
   - 第一次握手:確認客戶端能發送
   - 第二次握手:確認伺服器能接收和發送
   - 第三次握手:確認客戶端能接收
   - 三次才能完整確認

範例:
SYN1(延遲)→ 網路延遲中...
SYN2 → SYN-ACK → ACK → 正常連線 → 關閉
SYN1(終於到了)→ 伺服器收到

如果兩次握手:
伺服器建立連線,但客戶端不理它
→ 浪費資源

如果三次握手:
伺服器等客戶端的 ACK
→ 客戶端不回應(因為不是它發的)
→ 伺服器逾時,不建立連線

Q2:為什麼 TCP 關閉需要四次揮手?

A:因為 TCP 是全雙工(Full-Duplex)通訊

全雙工:雙方可以同時傳送和接收

關閉連線時:
1. 客戶端說:我不傳了(FIN)
2. 伺服器說:知道了(ACK)
   - 但伺服器可能還有資料要傳
3. 伺服器傳完後說:我也不傳了(FIN)
4. 客戶端說:知道了(ACK)

為什麼不能合併 2 和 3?
- 因為伺服器收到 FIN 時,可能還有資料要傳
- 需要先回 ACK 告知收到
- 等資料傳完才發 FIN

特殊情況:
如果伺服器沒有資料要傳,可以合併 ACK 和 FIN
→ 變成三次揮手

Q3:UDP 沒有連線,怎麼知道要傳給誰?

A:UDP 每個封包都帶有目的地資訊

UDP 封包結構:
├─ 來源 IP 地址
├─ 來源 Port
├─ 目的 IP 地址
├─ 目的 Port
└─ 資料

每個封包都是獨立的:
- 不需要建立連線
- 每個封包都知道要去哪裡
- 就像寄明信片,每張都寫地址

TCP vs UDP:
TCP:
- 建立連線(記住對方是誰)
- 後續封包直接傳(不用每次都寫地址)
- 像打電話(線路建立後,不用每次報身份)

UDP:
- 不建立連線
- 每個封包都寫地址
- 像寄明信片(每張都要寫地址)

Q4:什麼情況下 UDP 比 TCP 好?

A:需要速度 > 可靠性時

1. 即時通訊(視訊、語音)
   - 延遲 < 200ms 才不會卡
   - 少量畫格遺失可接受
   - 重傳會讓畫面更卡

2. 線上遊戲
   - 反應要快(延遲 < 50ms)
   - 位置更新遺失沒關係(下一個封包會更新)
   - 重傳會導致角色瞬移

3. 廣播/多播
   - 一對多傳輸
   - 不可能跟每個人建立 TCP 連線
   - DNS、DHCP 都用 UDP

4. 物聯網(IoT)
   - 設備資源有限
   - TCP 開銷太大
   - 簡單的感測器數據用 UDP

但要注意:
- UDP 沒有流量控制 → 可能塞爆網路
- UDP 沒有擁塞控制 → 可能造成網路癱瘓
- 需要應用層自己處理可靠性

Q5:TCP 如何保證可靠性?

A:五個機制保證可靠性

1. 序號(Sequence Number)
   - 每個封包編號
   - 接收方可以排序
   - 例:封包 3 → 封包 1 → 封包 2
   - 排序後:封包 1 → 封包 2 → 封包 3

2. 確認號(Acknowledgment)
   - 接收方回覆:我收到第 N 個封包了
   - 發送方知道成功送達

3. 重傳機制
   - 發送方等待 ACK
   - 逾時沒收到 → 重傳
   - 例:發送封包 5 → 等待 → 逾時 → 重傳封包 5

4. 校驗和(Checksum)
   - 檢查資料有沒有損壞
   - 損壞 → 丟棄 → 重傳

5. 流量控制(Flow Control)
   - 接收方告訴發送方:我的緩衝區還有多少空間
   - 發送方依此調整速度
   - 避免接收方來不及處理

範例:
發送方:傳送封包 1, 2, 3, 4, 5
接收方:收到 1, 2, 4, 5(3 遺失)
接收方:回覆 ACK=3(我收到了 1, 2,等待 3)
發送方:重傳封包 3
接收方:收到 3,排序成 1, 2, 3, 4, 5
接收方:回覆 ACK=6(全部收到)

✅ 重點回顧

TCP vs UDP 核心差異:

  • TCP = 可靠、慢、有連線(掛號信)
  • UDP = 快速、不可靠、無連線(廣播)

TCP 特性:

  • ✅ 三次握手建立連線(SYN → SYN-ACK → ACK)
  • ✅ 保證送達、保證順序
  • ✅ 流量控制、擁塞控制
  • ✅ 四次揮手關閉連線(FIN → ACK → FIN → ACK)

UDP 特性:

  • ✅ 無需建立連線(直接傳)
  • ✅ 快速、低開銷
  • ❌ 不保證送達、不保證順序

使用場景:

  • TCP:網頁、郵件、檔案傳輸、API
  • UDP:視訊、遊戲、直播、DNS、VoIP

選擇原則:

  • ✅ 需要完整資料 → TCP
  • ✅ 需要即時反應 → UDP
  • ✅ 混合使用(關鍵資料用 TCP,即時更新用 UDP)

面試重點:

  • ✅ 三次握手的目的(防止舊連線、確認雙向收發)
  • ✅ 四次揮手的原因(全雙工,需要雙方都確認關閉)
  • ✅ TCP 可靠性機制(序號、確認、重傳、校驗、流量控制)
  • ✅ UDP 適用場景(即時性 > 可靠性)

上一篇: 01-3. TCP/IP 四層模型 下一篇: 02-1. HTTP 基礎概念


最後更新:2025-01-06

0%