網路基礎:TCP/IP 與 HTTP 協定
當你在瀏覽器輸入 https://www.google.com 並按下 Enter——這背後發生了什麼事?
這短短的一秒內,你的電腦經歷了 DNS 查詢 → TCP 握手 → TLS 加密 → HTTP 請求/回應 等一系列複雜過程。
🔥 Vibe Prompt
「用 Python 實作一個簡易 HTTP 伺服器,支援靜態檔案服務、路由處理、並顯示每個請求的 TCP 連線資訊。另外實作一個命令行 HTTP 用戶端,手動傳送 GET/POST 請求並解析回應。」
TCP/IP 協定棧
網路通訊不是一個單一的協定,而是一組分層協定:
| 層級 | 名稱 | 代表協定 | 職責 | |------|------|---------|------| | 4 | 應用層 | HTTP, DNS, SMTP | 提供應用程式之間的通訊服務 | | 3 | 傳輸層 | TCP, UDP | 端到端的可靠/不可靠傳輸 | | 2 | 網路層 | IP, ICMP | 路由與定址(找到對方位置) | | 1 | 網路介面層 | Ethernet, WiFi | 實體訊號傳輸 |
封裝過程
當你發送 HTTP 請求時,資料會像俄羅斯娃娃一樣被層層包裝:
[HTTP 資料] ← 應用層
[TCP 標頭 + HTTP 資料] ← 傳輸層
[IP 標頭 + TCP 標頭 + HTTP 資料] ← 網路層
[Ethernet 標頭 + IP 標頭 + TCP 標頭 + HTTP 資料 + 檢查碼] ← 網路介面層
TCP 的三次握手
TCP 是連線導向的協定,建立連線需要三次交握:
用戶端 伺服器
│ │
├─── SYN (seq=x) ────────────►│ 1. 我要連線
│◄─── SYN+ACK (seq=y, ack=x+1)├─ 2. 收到,我也要連線
├─── ACK (seq=x+1, ack=y+1)──►│ 3. 好,連線建立
│◄═══════════════════════════►│ 連線已建立(資料傳輸)
import socket
# 簡易 TCP 伺服器
def simple_tcp_server():
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(('localhost', 8080))
server.listen(5)
print("🌐 TCP 伺服器啟動在 http://localhost:8080")
while True:
client, addr = server.accept()
print(f"📩 收到連線: {addr}")
data = client.recv(1024)
response = b"""HTTP/1.1 200 OK
Content-Type: text/plain
Hello from TCP Server!"""
client.send(response)
client.close()
# 簡易 TCP 用戶端
def simple_tcp_client():
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(('localhost', 8080))
client.send(b"GET / HTTP/1.1\r\nHost: localhost\r\n\r\n")
response = client.recv(4096)
print(f"📬 伺服器回應:\n{response.decode()}")
client.close()
HTTP 協定
HTTP (HyperText Transfer Protocol) 是網際網路的基石。
HTTP 請求結構
GET /index.html HTTP/1.1 ← 請求行(方法 + 路徑 + 版本)
Host: www.example.com ← 標頭
User-Agent: Mozilla/5.0
Accept: text/html
← 空行
(請求主體 — 僅 POST/PUT 有) ← 主體
HTTP 回應結構
HTTP/1.1 200 OK ← 狀態行(版本 + 狀態碼 + 訊息)
Content-Type: text/html ← 標頭
Content-Length: 1234
Set-Cookie: session_id=abc123
← 空行
<!DOCTYPE html> ← 回應主體
<html>...
HTTP 狀態碼
| 狀態碼 | 類別 | 範例 | |--------|------|------| | 1xx | 資訊 | 100 Continue | | 2xx | 成功 | 200 OK, 201 Created, 204 No Content | | 3xx | 重新導向 | 301 Moved Permanently, 304 Not Modified | | 4xx | 用戶端錯誤 | 400 Bad Request, 401 Unauthorized, 404 Not Found | | 5xx | 伺服器錯誤 | 500 Internal Server Error, 502 Bad Gateway, 503 Service Unavailable |
實作簡易 HTTP 框架
import socket
import threading
class SimpleHTTP:
def __init__(self, host='localhost', port=8000):
self.host = host
self.port = port
self.routes = {}
def route(self, path, method='GET'):
def wrapper(handler):
self.routes[(method, path)] = handler
return handler
return wrapper
def handle_request(self, client, addr):
data = client.recv(8192).decode()
if not data:
client.close()
return
# 解析請求行
request_line = data.split('\r\n')[0]
method, path, _ = request_line.split(' ')
print(f"➡️ {method} {path} from {addr}")
# 尋找對應路由
handler = self.routes.get((method, path))
if handler:
body = handler()
response = f"HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length: {len(body)}\r\n\r\n{body}"
else:
body = "<h1>404 Not Found</h1>"
response = f"HTTP/1.1 404 Not Found\r\nContent-Length: {len(body)}\r\n\r\n{body}"
client.send(response.encode())
client.close()
def start(self):
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind((self.host, self.port))
server.listen(10)
print(f"🚀 伺服器啟動: http://{self.host}:{self.port}")
while True:
client, addr = server.accept()
thread = threading.Thread(target=self.handle_request, args=(client, addr))
thread.start()
# 使用範例
app = SimpleHTTP()
@app.route('/')
def home():
return "<h1>🏠 首頁</h1><p>歡迎來到簡易 HTTP 伺服器!</p>"
@app.route('/about')
def about():
return "<h1>📖 關於</h1><p>這是用純 Python socket 實作的 HTTP 伺服器。</p>"
# 啟動(註解掉以避免阻塞)
# app.start()
DNS 解析
DNS (Domain Name System) 將人類可讀的網域名稱轉換為 IP 位址:
你在瀏覽器輸入 google.com
↓
瀏覽器查詢 DNS 快取 → 沒有
↓
查詢系統設定的 DNS 伺服器(如 8.8.8.8)
↓
DNS 伺服器回傳 142.250.190.46
↓
瀏覽器透過 IP 位址建立 TCP 連線
import socket
# DNS 查詢範例
def dns_lookup(domain):
try:
ip = socket.gethostbyname(domain)
print(f"🔍 {domain} → {ip}")
return ip
except socket.gaierror as e:
print(f"❌ DNS 查詢失敗: {e}")
return None
# 查詢常見網站
sites = ['google.com', 'github.com', 'stackoverflow.com']
for site in sites:
dns_lookup(site)
HTTPS 與 TLS
HTTP 是明碼傳輸,所有資料(包含密碼、信用卡號)都可以被攔截。HTTPS (HTTP Secure) 在 HTTP 和 TCP 之間加入了一層 TLS (Transport Layer Security) 加密:
HTTP/1.1 200 OK ← 明文
↓
TLS 加密層 ← 資料被加密
↓
TCP 傳輸 ← 加密後的資料在網路上傳輸
TLS 握手過程
客戶端 伺服器
│ │
├── ClientHello ──────────────►│ 支援的加密套件、TLS 版本
│◄── ServerHello ──────────────┤ 選擇的加密套件、憑證
│◄── Certificate ──────────────┤ 伺服器憑證(含公開金鑰)
├── 金鑰交換 ────────────────►│ 產生對稱金鑰
│◄════════════════════════════►│ 加密通訊開始
HTTP/2 與 HTTP/3
| 版本 | 特性 | 優勢 | |------|------|------| | HTTP/1.1 | 序列請求、連線無法重用 | 簡單、普及 | | HTTP/2 | 多工串流、header 壓縮、伺服器推送 | 減少延遲 | | HTTP/3 | 基於 QUIC (UDP) | 更快連線建立、更好的弱網表現 |
RESTful API 設計
GET /api/users ← 取得使用者列表
GET /api/users/123 ← 取得特定使用者
POST /api/users ← 建立新使用者
PUT /api/users/123 ← 更新使用者
DELETE /api/users/123 ← 刪除使用者
REST 原則
- 無狀態 (Stateless):每個請求包含所有必要資訊
- 資源導向 (Resource-Oriented):URL 表示資源
- 統一介面 (Uniform Interface):HTTP 方法表示操作
WebSocket
HTTP 是「請求-回應」模式,伺服器無法主動推送資料。WebSocket 建立持久連線,允許雙方隨時發送資料:
# WebSocket 服務端(使用 FastAPI)
from fastapi import FastAPI, WebSocket
app = FastAPI()
@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
await websocket.accept()
while True:
data = await websocket.receive_text()
await websocket.send_text(f"收到: {data}")
負載均衡 (Load Balancing)
使用者 ─── DNS ─── Load Balancer ─── 伺服器 1
─── 伺服器 2
─── 伺服器 3
| 演算法 | 策略 | |--------|------| | Round Robin | 輪流分配請求 | | Least Connections | 分配給連線數最少的伺服器 | | IP Hash | 同一 IP 固定到同一伺服器 | | Weighted | 根據伺服器能力分配權重 |
CDN (Content Delivery Network)
CDN 將靜態資源快取到全球各地的邊緣節點,讓使用者從最近的節點取得資料:
使用者(東京)→ 東京 CDN 邊緣節點(快取)→ 原始伺服器(美國)
使用者(倫敦)→ 倫敦 CDN 邊緣節點(快取)→ 原始伺服器(美國)
本章總結
| 概念 | 核心重點 | |------|---------| | TCP/IP | 分層協定棧,每層負責不同功能 | | 三次握手 | 確保雙方準備好通訊 | | HTTP | 請求-回應模型,無狀態協定 | | HTTPS/TLS | HTTP + 加密層,保護資料安全 | | HTTP/2 | 多工串流、header 壓縮 | | HTTP/3 (QUIC) | 基於 UDP,更快連線建立 | | DNS | 網域名稱解析為 IP 位址 | | RESTful API | 資源導向的 API 設計風格 | | WebSocket | 持久連線,雙向即時通訊 | | 負載均衡 | 分散流量到多台伺服器 | | CDN | 邊緣快取,加速全球存取 |
實戰練習
💡 Vibe Coding 練習:請 AI 建立一個「網路通訊互動式學習工具」:
- TCP 握手模擬:可視化展示 SYN、SYN-ACK、ACK 三次握手
- HTTP 偵測器:捕獲並解析 HTTP 請求/回應,顯示所有標頭
- TLS 握手展示:逐步展示 ClientHello → ServerHello → 金鑰交換
- REST API 測試工具:輸入端點和方法,發送請求並顯示結果
- WebSocket 聊天室:建立即時雙向通訊的展示
- CDN 模擬:顯示不同地理位置的存取延遲差異
網路通訊的基礎:TCP/IP 與 HTTP
TCP vs UDP
| 比較 | TCP | UDP | |:----|:---|:---| | 連線 | 面向連線(三次握手) | 無連線 | | 可靠度 | 保證送達、順序正確 | 不保證(可能遺失、亂序) | | 速度 | 慢(需要確認) | 快 | | 適合 | 網頁、Email、檔案傳輸 | 影音串流、線上遊戲、DNS |
HTTP 請求的生命週期
1. DNS 解析:example.com → 93.184.216.34
2. TCP 連線:三次握手建立連線
3. TLS 交握:HTTPS 的加密協商
4. 發送請求:GET /api/products HTTP/1.1
5. 伺服器處理:解析請求 → 查資料庫 → 產生回應
6. 接收回應:200 OK + JSON 資料
下一章預告:資料結構
網路是 Process 之間的通訊。下一章看看資料在 Process 內部是怎麼組織的——Array、Linked List、Tree、Hash Table。