1. TCP/IP 协议栈

互联网通信建立在分层协议栈上。理解每一层的职责,是理解服务器工作原理的前提。

5
应用层 Application Layer
HTTP / HTTPS / WebSocket / gRPC / DNS
4
传输层 Transport Layer
TCP(可靠传输、三次握手) / UDP(高速、无连接)
3
网络层 Network Layer
IP 寻址、路由转发、TTL / ICMP
2
数据链路层 Data Link Layer
Ethernet / WiFi / MAC 地址、帧封装
1
物理层 Physical Layer
网线、光纤、无线信号、比特流传输

TCP 三次握手与四次挥手

客户端 (Client)
服务器 (Server)
— 三次握手 (建立连接) —
SYN (seq=x)
第 1 次:我想连接
SYN+ACK (seq=y, ack=x+1)
第 2 次:好的,我准备好了
ACK (ack=y+1)
第 3 次:确认,连接建立 ✓
— 数据传输 —
HTTP 请求/响应、数据流
DATA 双向传输
— 四次挥手 (关闭连接) —
FIN
客户端:我发完了
ACK
服务器:收到,等我也发完
FIN
服务器:我也发完了
ACK
客户端:确认,连接关闭 ✓

用 Go 演示 TCP 服务器

package main

import (
    "bufio"
    "fmt"
    "net"
    "strings"
)

func main() {
    // 监听 TCP 端口 8080
    listener, err := net.Listen("tcp", ":8080")
    if err != nil {
        panic(err)
    }
    defer listener.Close()
    fmt.Println("TCP 服务器启动,监听 :8080")

    for {
        // 阻塞等待客户端连接
        conn, err := listener.Accept()
        if err != nil {
            continue
        }
        // 每个连接开一个 goroutine 处理 (并发核心)
        go handleConn(conn)
    }
}

func handleConn(conn net.Conn) {
    defer conn.Close()
    remote := conn.RemoteAddr().String()
    fmt.Printf("新连接: %s\n", remote)

    scanner := bufio.NewScanner(conn)
    for scanner.Scan() {
        line := scanner.Text()
        fmt.Printf("[%s] 收到: %s\n", remote, line)
        // Echo: 回显数据
        response := strings.ToUpper(line) + "\n"
        conn.Write([]byte(response))
    }
    fmt.Printf("连接断开: %s\n", remote)
}

2. HTTP 协议深度解析

HTTP 版本演进

版本年份核心特性问题
HTTP/1.01996短连接,每次请求新建TCP性能差,连接开销大
HTTP/1.11997持久连接 Keep-Alive,管道化队头阻塞 (HOL blocking)
HTTP/22015多路复用、头部压缩、服务器推送TCP 层队头阻塞
HTTP/32022基于 QUIC/UDP,彻底解决队头阻塞普及率还在提升

HTTP 请求结构

请求行
POST /api/users HTTP/1.1
请求头
Host: api.example.com
Content-Type: application/json
Authorization: Bearer eyJhbGc...
Content-Length: 45
空行
(必须,分隔请求头与请求体)
请求体
{"name": "Alice", "email": "alice@test.com"}

HTTP 状态码速查

2xx 成功

  • 200 OK — 请求成功
  • 201 Created — 资源已创建
  • 204 No Content — 成功,无返回体
  • 206 Partial Content — 范围请求成功

4xx 客户端错误

  • 400 Bad Request — 请求格式错误
  • 401 Unauthorized — 未认证
  • 403 Forbidden — 无权限
  • 404 Not Found — 资源不存在
  • 429 Too Many Requests — 限流

5xx 服务器错误

  • 500 Internal Server Error — 服务器异常
  • 502 Bad Gateway — 网关错误
  • 503 Service Unavailable — 服务不可用
  • 504 Gateway Timeout — 网关超时

3xx 重定向

  • 301 Moved Permanently — 永久重定向
  • 302 Found — 临时重定向
  • 304 Not Modified — 缓存未修改
  • 307 Temporary Redirect — 临时(保持方法)

用 Go 标准库实现 HTTP 服务器

package main

import (
    "encoding/json"
    "fmt"
    "log"
    "net/http"
    "time"
)

type Response struct {
    Status  string `json:"status"`
    Message string `json:"message"`
    Time    string `json:"time"`
}

func main() {
    mux := http.NewServeMux()

    // 路由注册
    mux.HandleFunc("GET /", homeHandler)
    mux.HandleFunc("GET /health", healthHandler)
    mux.HandleFunc("POST /echo", echoHandler)

    // 服务器配置 (生产环境必须设置超时)
    server := &http.Server{
        Addr:         ":8080",
        Handler:      loggingMiddleware(mux),
        ReadTimeout:  10 * time.Second,
        WriteTimeout: 10 * time.Second,
        IdleTimeout:  30 * time.Second,
    }

    fmt.Println("HTTP 服务器启动: http://localhost:8080")
    log.Fatal(server.ListenAndServe())
}

func homeHandler(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(Response{
        Status:  "ok",
        Message: "Welcome to Go Server!",
        Time:    time.Now().Format(time.RFC3339),
    })
}

func healthHandler(w http.ResponseWriter, r *http.Request) {
    w.WriteHeader(http.StatusOK)
    json.NewEncoder(w).Encode(map[string]string{"status": "healthy"})
}

func echoHandler(w http.ResponseWriter, r *http.Request) {
    var body map[string]any
    if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
        http.Error(w, "invalid JSON", http.StatusBadRequest)
        return
    }
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(body)
}

// 中间件:请求日志
func loggingMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        start := time.Now()
        next.ServeHTTP(w, r)
        log.Printf("%s %s %s %v", r.Method, r.URL.Path, r.RemoteAddr, time.Since(start))
    })
}

3. WebSocket 实时通信

WebSocket 在 HTTP 握手后升级为全双工 TCP 连接,服务器可主动推送数据,适合聊天室、实时通知、在线协作等场景。

客户端 (Client)
服务器 (Server)
— HTTP 升级握手 —
GET /ws HTTP/1.1
Upgrade: websocket
Connection: Upgrade
发起 WebSocket 升级请求
HTTP/1.1 101 Switching Protocols
服务器同意升级,握手完成 ✓
— 全双工 WebSocket 通信 —
Text Frame: "Hello Server"
客户端随时推送消息
Text Frame: "Hello Client"
服务器主动推送,无需请求
保持连接活跃
Ping / Pong (心跳)
package main

import (
    "fmt"
    "log"
    "net/http"

    "github.com/gorilla/websocket"
)

var upgrader = websocket.Upgrader{
    ReadBufferSize:  1024,
    WriteBufferSize: 1024,
    // 生产环境应验证 Origin
    CheckOrigin: func(r *http.Request) bool { return true },
}

// Hub 管理所有 WebSocket 连接
type Hub struct {
    clients    map[*websocket.Conn]bool
    broadcast  chan []byte
    register   chan *websocket.Conn
    unregister chan *websocket.Conn
}

func newHub() *Hub {
    return &Hub{
        clients:    make(map[*websocket.Conn]bool),
        broadcast:  make(chan []byte, 256),
        register:   make(chan *websocket.Conn),
        unregister: make(chan *websocket.Conn),
    }
}

func (h *Hub) run() {
    for {
        select {
        case conn := <-h.register:
            h.clients[conn] = true
            fmt.Printf("客户端连接,当前连接数: %d\n", len(h.clients))

        case conn := <-h.unregister:
            if _, ok := h.clients[conn]; ok {
                delete(h.clients, conn)
                conn.Close()
                fmt.Printf("客户端断开,当前连接数: %d\n", len(h.clients))
            }

        case message := <-h.broadcast:
            // 广播给所有客户端
            for conn := range h.clients {
                if err := conn.WriteMessage(websocket.TextMessage, message); err != nil {
                    h.unregister <- conn
                }
            }
        }
    }
}

func wsHandler(hub *Hub, w http.ResponseWriter, r *http.Request) {
    conn, err := upgrader.Upgrade(w, r, nil)
    if err != nil {
        log.Println("WebSocket 升级失败:", err)
        return
    }
    hub.register <- conn

    // 读取消息并广播
    for {
        _, msg, err := conn.ReadMessage()
        if err != nil {
            hub.unregister <- conn
            break
        }
        hub.broadcast <- msg
    }
}

func main() {
    hub := newHub()
    go hub.run()

    http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
        wsHandler(hub, w, r)
    })

    log.Println("WebSocket 服务器: ws://localhost:8080/ws")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

4. DNS 与服务发现

浏览器 输入域名
本地缓存 hosts / OS 缓存
递归 DNS ISP / 8.8.8.8
根 DNS 13 组根服务器
TLD DNS .com / .net
权威 DNS 返回 IP 地址
Web 服务器 TCP + HTTP
package main

import (
    "context"
    "fmt"
    "net"
    "time"
)

func dnsLookup(host string) {
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()

    resolver := &net.Resolver{
        PreferGo: true,
        Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
            d := net.Dialer{Timeout: 3 * time.Second}
            // 使用 Google Public DNS
            return d.DialContext(ctx, "udp", "8.8.8.8:53")
        },
    }

    // 查询 A 记录 (IPv4)
    ips, err := resolver.LookupHost(ctx, host)
    if err != nil {
        fmt.Printf("DNS 查询失败: %v\n", err)
        return
    }
    fmt.Printf("主机 %s 的 IP 地址:\n", host)
    for _, ip := range ips {
        fmt.Printf("  → %s\n", ip)
    }

    // 查询 MX 记录 (邮件)
    mxs, _ := resolver.LookupMX(ctx, host)
    for _, mx := range mxs {
        fmt.Printf("MX: %s (优先级: %d)\n", mx.Host, mx.Pref)
    }
}

func main() {
    dnsLookup("github.com")
}

5. TLS/HTTPS 原理

TLS(Transport Layer Security)在 TCP 之上提供加密、认证和完整性保护。

🔒

TLS 1.3 握手只需 1-RTT

相比 TLS 1.2 的 2-RTT,TLS 1.3 合并了握手步骤,显著降低延迟。对于已知服务器还支持 0-RTT 恢复。

package main

import (
    "crypto/tls"
    "fmt"
    "log"
    "net/http"
)

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "HTTPS 连接成功! 协议: %s", r.Proto)
    })

    // TLS 配置 (推荐配置)
    tlsConfig := &tls.Config{
        MinVersion: tls.VersionTLS13,    // 最低 TLS 1.3
        CurvePreferences: []tls.CurveID{
            tls.X25519,                  // 首选现代曲线
            tls.CurveP256,
        },
        CipherSuites: []uint16{          // TLS 1.3 自动选择,1.2 手动指定
            tls.TLS_AES_128_GCM_SHA256,
            tls.TLS_AES_256_GCM_SHA384,
        },
    }

    server := &http.Server{
        Addr:      ":443",
        Handler:   mux,
        TLSConfig: tlsConfig,
    }

    // 证书可通过 Let's Encrypt 自动获取
    log.Fatal(server.ListenAndServeTLS("cert.pem", "key.pem"))
}

// 自动 HTTPS (使用 autocert)
// import "golang.org/x/crypto/acme/autocert"
// m := &autocert.Manager{
//     Cache:      autocert.DirCache("certs"),
//     Prompt:     autocert.AcceptTOS,
//     HostPolicy: autocert.HostWhitelist("example.com"),
// }
// server.TLSConfig = m.TLSConfig()
💡

开发环境证书

使用 mkcert 工具快速生成本地可信证书:mkcert -install && mkcert localhost 127.0.0.1

6. 反向代理与负载均衡

客户端请求
Nginx / 负载均衡器
轮询 / 最少连接 / IP Hash
后端服务 1  :8081
后端服务 2  :8082
后端服务 3  :8083
package main

import (
    "fmt"
    "log"
    "net/http"
    "net/http/httputil"
    "net/url"
    "sync/atomic"
)

// 简易轮询负载均衡器
type LoadBalancer struct {
    backends []*url.URL
    counter  atomic.Uint64
}

func NewLoadBalancer(backends []string) *LoadBalancer {
    lb := &LoadBalancer{}
    for _, b := range backends {
        u, _ := url.Parse(b)
        lb.backends = append(lb.backends, u)
    }
    return lb
}

func (lb *LoadBalancer) next() *url.URL {
    idx := lb.counter.Add(1) % uint64(len(lb.backends))
    return lb.backends[idx]
}

func (lb *LoadBalancer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    target := lb.next()
    proxy := httputil.NewSingleHostReverseProxy(target)
    proxy.ErrorHandler = func(w http.ResponseWriter, r *http.Request, err error) {
        http.Error(w, "backend unavailable", http.StatusBadGateway)
    }
    proxy.ServeHTTP(w, r)
    fmt.Printf("请求转发到: %s\n", target.Host)
}

func main() {
    lb := NewLoadBalancer([]string{
        "http://localhost:8081",
        "http://localhost:8082",
        "http://localhost:8083",
    })
    log.Fatal(http.ListenAndServe(":80", lb))
}

7. Go 1.22 标准库 net/http 路由增强

在 Go 1.22 之前,net/http.ServeMux 无法区分 HTTP 方法,也不支持路径参数,因此生产项目必须引入 Gorilla Mux、chi 等第三方路由库。Go 1.22 对 ServeMux 进行了重要增强,使其足以满足大多数中小型项目的路由需求,减少依赖。

package main

import (
    "encoding/json"
    "log/slog"
    "net/http"
    "os"
    "strconv"
    "time"
)

// ─── Go 1.22 ServeMux 完整路由示例 ───────────────────────────
func setupRouter() http.Handler {
    mux := http.NewServeMux()

    // ① 精确匹配根路径(不匹配 /foo)
    mux.HandleFunc("GET /{$}", func(w http.ResponseWriter, r *http.Request) {
        json.NewEncoder(w).Encode(map[string]string{"service": "api", "version": "v1"})
    })

    // ② 方法限定 + 路径参数
    mux.HandleFunc("GET /api/v1/users", listUsersHandler)
    mux.HandleFunc("POST /api/v1/users", createUserHandler)
    mux.HandleFunc("GET /api/v1/users/{id}", getUserHandler)     // {id} 单段
    mux.HandleFunc("PUT /api/v1/users/{id}", updateUserHandler)
    mux.HandleFunc("DELETE /api/v1/users/{id}", deleteUserHandler)

    // ③ 嵌套资源
    mux.HandleFunc("GET /api/v1/users/{uid}/posts/{pid}", func(w http.ResponseWriter, r *http.Request) {
        uid := r.PathValue("uid")
        pid := r.PathValue("pid")
        json.NewEncoder(w).Encode(map[string]string{"user_id": uid, "post_id": pid})
    })

    // ④ 尾部通配符:文件服务
    mux.HandleFunc("GET /static/{path...}", func(w http.ResponseWriter, r *http.Request) {
        http.ServeFile(w, r, "./public/"+r.PathValue("path"))
    })

    // ⑤ 主机限定(host/path 格式)
    // mux.HandleFunc("admin.example.com/dashboard", dashboardHandler)

    return mux
}

func getUserHandler(w http.ResponseWriter, r *http.Request) {
    idStr := r.PathValue("id")         // 提取路径参数
    id, err := strconv.Atoi(idStr)
    if err != nil {
        http.Error(w, `{"error":"invalid id"}`, http.StatusBadRequest)
        return
    }
    // 模拟查询
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(map[string]any{"id": id, "name": "Alice"})
}

func main() {
    logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{
        Level: slog.LevelInfo,
    }))

    handler := loggingMiddleware2(logger)(setupRouter())

    server := &http.Server{
        Addr:         ":8080",
        Handler:      handler,
        ReadTimeout:  10 * time.Second,
        WriteTimeout: 10 * time.Second,
        IdleTimeout:  60 * time.Second,
    }

    logger.Info("服务器启动", "addr", server.Addr)
    if err := server.ListenAndServe(); err != nil {
        logger.Error("启动失败", "err", err)
        os.Exit(1)
    }
}

func loggingMiddleware2(logger *slog.Logger) func(http.Handler) http.Handler {
    return func(next http.Handler) http.Handler {
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            start := time.Now()
            next.ServeHTTP(w, r)
            logger.Info("http",
                "method",   r.Method,
                "path",     r.URL.Path,
                "latency",  time.Since(start).String(),
                "remote",   r.RemoteAddr,
            )
        })
    }
}

// 占位函数
func listUsersHandler(w http.ResponseWriter, r *http.Request)   {}
func createUserHandler(w http.ResponseWriter, r *http.Request)  {}
func updateUserHandler(w http.ResponseWriter, r *http.Request)  {}
func deleteUserHandler(w http.ResponseWriter, r *http.Request)  {}
⚠️

何时仍需第三方路由库?

Go 1.22 ServeMux 不支持:路由中间件分组(需对每个路由单独包装)、正则表达式路径、路由优先级控制(当两个模式有歧义时)。如果你需要这些特性,chi(轻量、标准库兼容)或 Gin(功能最全)仍是更好的选择。对于 CRUD API 服务,标准库已经足够。

8. WebSocket 实时通信

原理:WebSocket 是建立在 TCP 之上的全双工通信协议。连接过程分两阶段:① HTTP 升级握手(客户端发送 Upgrade: websocket 头,服务端回复 101 Switching Protocols);② 建立 WebSocket 帧通信(二进制帧协议,比 HTTP 开销小得多)。

package main

import (
    "encoding/json"
    "log"
    "net/http"
    "sync"
    "time"

    "github.com/gorilla/websocket"  // go get github.com/gorilla/websocket
)

// ─── 消息类型 ─────────────────────────────────────────────────
type WSMessage struct {
    Type    string `json:"type"`
    Payload any    `json:"payload"`
    From    string `json:"from,omitempty"`
}

// ─── 连接管理器(广播中心) ───────────────────────────────────
type Hub struct {
    clients   map[*Client]bool
    broadcast chan []byte        // 广播消息队列
    register  chan *Client       // 新连接
    unregister chan *Client      // 断开连接
    mu        sync.RWMutex
}

type Client struct {
    hub  *Hub
    conn *websocket.Conn
    send chan []byte             // 发送队列(缓冲)
    id   string
}

var upgrader = websocket.Upgrader{
    ReadBufferSize:  1024,
    WriteBufferSize: 1024,
    CheckOrigin: func(r *http.Request) bool {
        return true  // 生产环境需要检查 Origin
    },
}

func NewHub() *Hub {
    return &Hub{
        clients:    make(map[*Client]bool),
        broadcast:  make(chan []byte, 256),
        register:   make(chan *Client),
        unregister: make(chan *Client),
    }
}

// Hub 主循环:管理连接与广播
func (h *Hub) Run() {
    for {
        select {
        case client := <-h.register:
            h.mu.Lock()
            h.clients[client] = true
            h.mu.Unlock()
            log.Printf("客户端 %s 连接,当前 %d 个连接\n", client.id, len(h.clients))

        case client := <-h.unregister:
            h.mu.Lock()
            if _, ok := h.clients[client]; ok {
                delete(h.clients, client)
                close(client.send)
            }
            h.mu.Unlock()

        case message := <-h.broadcast:
            h.mu.RLock()
            for client := range h.clients {
                select {
                case client.send <- message:  // 非阻塞发送
                default:
                    // 发送队列满:断开慢消费者
                    close(client.send)
                    delete(h.clients, client)
                }
            }
            h.mu.RUnlock()
        }
    }
}

// 读协程:处理客户端消息
func (c *Client) readPump() {
    defer func() {
        c.hub.unregister <- c
        c.conn.Close()
    }()
    c.conn.SetReadLimit(512 * 1024)            // 最大消息 512KB
    c.conn.SetReadDeadline(time.Now().Add(60 * time.Second))
    c.conn.SetPongHandler(func(string) error {  // 收到 Pong 更新 deadline
        c.conn.SetReadDeadline(time.Now().Add(60 * time.Second))
        return nil
    })

    for {
        _, message, err := c.conn.ReadMessage()
        if err != nil {
            break
        }
        // 广播给所有连接
        c.hub.broadcast <- message
    }
}

// 写协程:发送消息给客户端,定时发送 Ping 心跳
func (c *Client) writePump() {
    ticker := time.NewTicker(30 * time.Second)
    defer func() {
        ticker.Stop()
        c.conn.Close()
    }()

    for {
        select {
        case message, ok := <-c.send:
            c.conn.SetWriteDeadline(time.Now().Add(10 * time.Second))
            if !ok {
                c.conn.WriteMessage(websocket.CloseMessage, []byte{})
                return
            }
            w, err := c.conn.NextWriter(websocket.TextMessage)
            if err != nil { return }
            w.Write(message)
            // 批量发送:将队列中积压的消息一起写出
            n := len(c.send)
            for i := 0; i < n; i++ {
                w.Write([]byte("\n"))
                w.Write(<-c.send)
            }
            if err := w.Close(); err != nil { return }

        case <-ticker.C:
            // 每 30 秒发送一次 Ping
            c.conn.SetWriteDeadline(time.Now().Add(10 * time.Second))
            if err := c.conn.WriteMessage(websocket.PingMessage, nil); err != nil {
                return
            }
        }
    }
}

func serveWS(hub *Hub, w http.ResponseWriter, r *http.Request) {
    conn, err := upgrader.Upgrade(w, r, nil)
    if err != nil {
        log.Println("WebSocket 升级失败:", err)
        return
    }
    client := &Client{
        hub:  hub,
        conn: conn,
        send: make(chan []byte, 256),
        id:   r.RemoteAddr,
    }
    hub.register <- client
    go client.writePump()  // 每个连接两个 goroutine
    go client.readPump()
}

func main() {
    hub := NewHub()
    go hub.Run()

    http.HandleFunc("GET /ws", func(w http.ResponseWriter, r *http.Request) {
        serveWS(hub, w, r)
    })
    http.HandleFunc("POST /broadcast", func(w http.ResponseWriter, r *http.Request) {
        var msg WSMessage
        json.NewDecoder(r.Body).Decode(&msg)
        data, _ := json.Marshal(msg)
        hub.broadcast <- data
        w.WriteHeader(http.StatusNoContent)
    })

    log.Println("WebSocket 服务启动 :8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}
⚠️

WebSocket 生产环境注意事项

① 必须设置 SetReadDeadlineSetWriteDeadline,防止慢/死连接永久占用 goroutine。② CheckOrigin 在生产中必须严格验证来源,防止 CSRF。③ 每个 WebSocket 连接需要两个 goroutine(读/写),1 万连接 = 2 万 goroutine ≈ 40MB 栈内存,需要压测评估。④ 负载均衡时需要 sticky session(粘性会话)或使用 Redis Pub/Sub 实现跨节点广播。