Files
2026-05-24 11:42:38 +08:00

9.2 KiB
Raw Permalink Blame History

tags, create time
tags create time
计算机网络
DNS
DHCP
WebSocket
2026-05-18 03:10

DNS / DHCP / WebSocket

一、DNS 原理与优化

DNS 查询流程(递归 vs 迭代)

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 风格
# 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)

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
# 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 计算

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

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))
}

关联笔记