目錄
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 完整對比
核心差異表
| 特性 | TCP | UDP |
|---|---|---|
| 連線方式 | 面向連線(Connection-oriented) | 無連線(Connectionless) |
| 可靠性 | 可靠(保證送達) | 不可靠(可能遺失) |
| 順序性 | 保證順序 | 不保證順序 |
| 速度 | 慢 | 快 |
| 開銷 | 大(需要握手、確認) | 小(直接傳送) |
| 錯誤檢查 | 有(並重傳) | 有檢查但不重傳 |
| 流量控制 | 有 | 無 |
| 擁塞控制 | 有 | 無 |
| 資料單位 | Segment(區段) | Datagram(資料包) |
| 標頭大小 | 20-60 bytes | 8 bytes |
使用場景對比
| 場景 | 用 TCP | 用 UDP |
|---|---|---|
| 網頁瀏覽 | ✅ | ❌ |
| 電子郵件 | ✅ | ❌ |
| 檔案下載 | ✅ | ❌ |
| 視訊通話 | ❌ | ✅ |
| 線上遊戲 | ❌ | ✅ |
| 直播串流 | ❌ | ✅ |
| DNS 查詢 | ❌ | ✅ |
| VoIP 語音 | ❌ | ✅ |
🤝 TCP 三次握手(Three-Way Handshake)
為什麼需要三次握手?
建立可靠連線需要雙方確認:
- 客戶端能發送
- 伺服器能接收和發送
- 客戶端能接收
三次握手流程
【第一次握手 - 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