Files
cs-note/hhs/NETWORK/05-应用层协议/05-DNS与DHCP与WebSocket.md
T
2026-05-24 11:42:38 +08:00

271 lines
9.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
tags: [计算机网络, DNS, DHCP, WebSocket]
create time: 2026-05-18 03:10
---
# DNS / DHCP / WebSocket
## 一、DNS 原理与优化
### DNS 查询流程(递归 vs 迭代)
```mermaid
sequenceDiagram
participant U as 用户浏览器
participant LRD as 本地 DNS Resolver<br/>ISP 或 1.1.1.1/9.9.9.9
participant TLD as TLD Server<br/>.com
participant AUTH as Authoritative NS<br/>example.com
U->>LRD: 递归查询 "example.com?"
Note over LRD: 检查本地缓存...
LRD->>TLD: 迭代查询 ".com?"
TLD-->>LRD: "去问 example.com 的 NS"
LRD->>AUTH: 迭代查询 "example.com A 记录?"
AUTH-->>LRD: 93.184.216.34
Note over LRD: TTL=300 → 缓存 5 分钟
LRD-->>U: ✅ 93.184.216.34
```
### 关键概念
| 术语 | 说明 |
|------|------|
| **Recursion Desired (RD)** | 客户端要求 DNS 服务器递归查找 |
| **Authority Section** | 指向下一级的授权 NS |
| **CNAME Chain** | 别名链,最多 63 层嵌套 |
| **TTL** | Time To Live,缓存有效期(秒) |
| **ANAME/ALIAS** | 根域名的 CNAME 替代方案 |
| **Any 查询** | `query type = ANY`,RFC 8753 建议禁用 |
### 常见记录类型
| 类型 | 名称 | 用途 | 示例 |
|------|------|------|------|
| A | Address | IPv4 地址 | `@ A 93.184.216.34` |
| AAAA | Address V6 | IPv6 地址 | `@ AAAA 2606:2800:220:1:248:1893:25c8:1946` |
| CNAME | Canonical Name | 别名 | `www CNAME example.com` |
| MX | Mail Exchange | 邮件服务器优先级 | `@ MX 10 mail.example.com` |
| TXT | Text | SPF/DKIM/域名验证 | `v=spf1 include:_spf.google.com ~all` |
| SRV | Service | 服务定位 | `_sip._tcp SVC 1 0 5060 sip.example.com` |
| NS | Name Server | 授权 NS 记录 | `@ NS ns1.provider.com` |
| PTR | Pointer | 反向解析 (IP → 域名) | `34.216.184.93.in-addr.arpa PTR example.com` |
| SOA | Start of Authority | 区域文件起始权威记录 | `ns1 admin email serial refresh retry expire minimum` |
| DS / DNSKEY | — | DNSSEC 签名验证 | — |
### DNS 缓存层次
```
┌─────────────────────────────────────┐
│ Tier 1: Application/CPU Cache │ ← Go sync.Map / HTTP cache header
│ TTL controlled by Content-TTL │ Browser cache varies 0~30min
├─────────────────────────────────────┤
│ Tier 2: OS Resolver Cache │ ← nscd / systemd-resolved / DNSMasq
│ Linux: nscd (default off!) │ macOS: mDNSResponder
│ macOS: mDNSResponder (~120s) │ Windows: Dnscache
├─────────────────────────────────────┤
│ Tier 3: ISP / Recursive Resolver │ ← Cloudflare 1.1.1.1, Google 8.8.8.8
│ TTL from authoritative server │ 通常 60~300s 最小缓存
├─────────────────────────────────────┤
│ Tier 4: TLD + Authoritative │ ← .com NS → example.com NS
│ No user-controlable caching │
└─────────────────────────────────────┘
```
### DNS-over-HTTPS / DNS-over-TLS
| 协议 | RFC | 端口 | 特点 |
|------|-----|------|------|
| DoH (DNS over HTTPS) | 8484 | 443 (TCP) | 伪装成 HTTPS 流量,CDN 友好 |
| DoT (DNS over TLS) | 7858 | 853 (TCP) | 专用加密通道 |
| DoQ (DNS over QUIC) | 9230 | 853 (UDP via QUIC) | HTTP/3 风格 |
```bash
# Linux 启用 DoH
$ resolvectl dns eth0 1.1.1.1
$ resolvectl domain eth0 "~."
# dig 指定 DNS 服务器
$ dig @1.1.1.1 example.com +short
93.184.216.34
```
## 二、DHCP 自动分配
### DHCP 四步交互(DORA)
```mermaid
sequenceDiagram
participant Client as 新设备<br/>(无 IP)
participant Server as DHCP Server
Note over Client: BOOTING state, src_ip=0.0.0.0
Client->>Server: DHCPDISCOVER (broadcast ff:ff:ff:ff:ff:ff)
Note over Server: 收到后从可用池选择一个 IP
Server-->>Client: DHCPOFFER (ip=x.x.x.x, lease_time=T, gw=y.y.y.y)
Note over Client: SELECTING state
Client->>Server: DHCPREQUEST (requested ip=x.x.x.x)
Note over Server: 确认分配
Server-->>Client: DHCPACK (confirmed allocation)
Note over Client: BOUND state ✅ IP = x.x.x.x
```
### DHCP 续租机制
```
租约时间 = Lease Time(默认通常 24h)
续约时机:
• T1 = 50% 租约时 → RENEW (单播到原服务器)
• T2 = 87.5% 租约时 → REBIND (广播到新服务器)
• 到期前必须续约成功,否则释放 IP
```
```bash
# Linux DHCP 客户端配置
$ cat /etc/dhcp/dhclient.conf
timeout 300; # 超时重试间隔
retry 60; # 初始重试间隔
reboot 10; # 重启时重尝试获取 IP
request subnet-mask, broadcast-address, time-offset, routers;
# 手动刷新 IP
$ sudo dhclient -r eth0 # release
$ sudo dhclient eth0 # request new
```
## 三、WebSocket 全双工通信
### 为什么需要 WebSocket?
```
传统轮询 vs WebSocket:
═══════════════════════
Polling: Client ─→ GET /status ─→ empty
Client ─→ GET /status ─→ {"msg": "hi"}
Client ─→ GET /status ─→ empty
... 浪费带宽,延迟高
Long Polling:
Client ─→ GET /status (挂起) ─→ ...等待... ─→ {"msg": "hi"}
Client ─→ GET /status (再挂起) ─→ ...
WebSocket ✨:
Client ─── 握手升级 ───→ 持久双向通道
Server ─── 推送消息 ───→ Client
Client ─── 推送消息 ───→ Server
← 全双工、低开销! -->
```
### WebSocket 握手升级过程
```
HTTP 请求 → WebSocket → HTTP 响应
Client: Server:
GET /ws/chat HTTP/1.1 101 Switching Protocols
Host: chat.example.com Upgrade: websocket
Upgrade: websocket Connection: Upgrade
Connection: Upgrade Sec-WebSocket-Accept: <base64-hash>
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
```
**关键:** 这不是 TCP 层面的升级,而是 HTTP 协议的升级——服务器返回 `101` 状态码告诉客户端:"好的,我们从这里开始用 WebSocket 协议"。
### Sec-WebSocket-Accept 计算
```go
import (
"crypto/sha1"
"encoding/base64"
"strings"
)
const magicGUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
func computeAccept(key string) string {
h := sha1.New()
h.Write([]byte(key + magicGUID))
return base64.StdEncoding.EncodeToString(h.Sum(nil))
}
// 例: key = "dGhlIHNhbXBsZSBub25jZQ=="
// accept = "s3pPLMBiTxaQ9kYGzzhZRbK+xOo="
```
### WebSocket Frame 格式
```
┌───────────┬───────────┬─────────────┬───────────────┬────────────────┐
│ FIN(1 bit)│ RSV(3bit) │ Opcode(4bit)│ Mask(1 bit) │ Payload Len │
│ │ │ │ │ + Ext Len │
├───────────┴───────────┴─────────────┴───────────────┼────────────────┤
│ Extended Payload Length │ Mask Key │
│ (0, 126, or 127 bytes) │ (4 bytes) │
├─────────────────────────────────────────────────────┴────────────────┤
│ Masked Payload Data (variable) │
└─────────────────────────────────────────────────────────────────────┘
```
| Opcode | 含义 |
|--------|------|
| 0x0 | Continuation frame |
| 0x1 | Text frame |
| 0x2 | Binary frame |
| 0x8 | Connection Close |
| 0x9 | Ping |
| 0xA | Pong |
### Go 中的 WebSocket
```go
package main
import (
"log"
"net/http"
"github.com/gorilla/websocket"
)
var upgrader = websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool {
return true // 生产环境应严格校验 Origin
},
}
func wsHandler(w http.ResponseWriter, r *http.Request) {
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
log.Println("upgrade error:", err)
return
}
defer conn.Close()
for {
mt, message, err := conn.ReadMessage()
if err != nil {
break
}
log.Printf("recv: %s", message)
conn.WriteMessage(mt, message)
}
}
func main() {
http.HandleFunc("/ws", wsHandler)
log.Fatal(http.ListenAndServe(":8080", nil))
}
```
## 关联笔记
- [[hhs/NETWORK/HTTPS与TLS握手]] — SNI 扩展在 DNS 解析中的应用
- [[hhs/NETWORK/WebSocket全双工通信]] — 心跳机制防止 NAT/代理超时断开
- [[hhs/NETWORK/NAT原理与应用]] — WebSocket 也受 NAT 影响