This commit is contained in:
2026-05-24 11:42:38 +08:00
commit 30d312ac35
521 changed files with 146481 additions and 0 deletions
@@ -0,0 +1,270 @@
---
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 影响