🌐 网络与服务器基础
深入理解 TCP/IP 协议栈、HTTP 协议演进、WebSocket 实时通信——这是服务器开发的地基。
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.0 | 1996 | 短连接,每次请求新建TCP | 性能差,连接开销大 |
| HTTP/1.1 | 1997 | 持久连接 Keep-Alive,管道化 | 队头阻塞 (HOL blocking) |
| HTTP/2 | 2015 | 多路复用、头部压缩、服务器推送 | TCP 层队头阻塞 |
| HTTP/3 | 2022 | 基于 QUIC/UDP,彻底解决队头阻塞 | 普及率还在提升 |
HTTP 请求结构
请求行
POST /api/users HTTP/1.1
请求头
Host: api.example.com
Content-Type: application/json
Authorization: Bearer eyJhbGc...
Content-Length: 45
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
:8083package 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))
}