WebSocket

返回 计算机网络

WebSocket 是基于 TCP 的全双工通信协议,通过 HTTP 握手升级建立持久连接,服务端可主动推送消息。


与 HTTP 对比

特性HTTPWebSocket
连接模式请求-响应,短连接持久连接,全双工
消息方向客户端发起双向,服务端可主动推送
头部开销每次请求携带完整头部建立后帧头极小(2~10 字节)
适用场景普通接口调用实时聊天、推送、游戏

握手升级

WebSocket 连接通过 HTTP 101 Upgrade 建立:

客户端请求:
GET /ws HTTP/1.1
Host: example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13

服务端响应:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

握手成功后,HTTP 连接升级为 WebSocket,不再遵循 HTTP 协议。


消息帧结构

WebSocket 数据以帧(Frame)为单位传输:

  • opcode:0x1 文本帧、0x2 二进制帧、0x8 关闭、0x9 Ping、0xA Pong
  • Masking:客户端→服务端的帧必须掩码处理,服务端→客户端不需要
  • 帧头极小(2~10 字节),相比 HTTP 头部开销大幅降低

前端使用

const ws = new WebSocket('wss://example.com/ws')
 
ws.onopen = () => {
  ws.send(JSON.stringify({ type: 'join', room: 'general' }))
}
 
ws.onmessage = (event) => {
  const data = JSON.parse(event.data)
  console.log('收到消息:', data)
}
 
ws.onclose = (event) => {
  console.log('连接关闭', event.code, event.reason)
}
 
ws.onerror = (error) => {
  console.error('连接错误', error)
}
 
// 主动关闭
ws.close(1000, '正常关闭')

Spring Boot 集成

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
 
    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(new ChatHandler(), "/ws/chat")
                .setAllowedOrigins("*");
    }
}
 
@Component
public class ChatHandler extends TextWebSocketHandler {
 
    private final Set<WebSocketSession> sessions = ConcurrentHashMap.newKeySet();
 
    @Override
    public void afterConnectionEstablished(WebSocketSession session) {
        sessions.add(session);
    }
 
    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        for (WebSocketSession s : sessions) {
            if (s.isOpen()) {
                s.sendMessage(message);
            }
        }
    }
 
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
        sessions.remove(session);
    }
}

心跳与重连

// 客户端心跳保活
let heartbeat = setInterval(() => {
  if (ws.readyState === WebSocket.OPEN) {
    ws.send(JSON.stringify({ type: 'ping' }))
  }
}, 30000)
 
// 断线自动重连
ws.onclose = () => {
  clearInterval(heartbeat)
  setTimeout(() => connect(), 3000)
}

适用场景

场景说明
实时聊天消息双向推送,低延迟
在线协作文档多人同时编辑,操作实时同步
股票行情服务端主动推送价格变动
在线游戏高频状态同步
消息通知替代轮询,服务端事件触发推送

关闭状态码

ws.close(1000, '正常关闭')
状态码含义
1000正常关闭
1001端点离开(页面跳转/服务关闭)
1006连接异常断开(无 Close 帧,网络故障)
1008违反策略(消息不合法)
1011服务端内部错误
4000-4999应用自定义关闭码

安全性

Origin 验证:握手请求携带 Origin 头,服务端必须校验,防止跨站 WebSocket 劫持(CSWSH):

// Spring:只允许指定来源
registry.addHandler(handler, "/ws")
        .setAllowedOrigins("https://my-frontend.com");

使用 wss(WebSocket Secure):WebSocket 同样有明文(ws://)与加密(wss://)两种,生产环境必须使用 wss,底层走 TLS。

消息验证:WebSocket 建立后不自动携带 Cookie 到每帧,需在握手阶段或首条消息中完成身份认证(Token 传入 URL 参数或首帧 payload)。


子协议(Sec-WebSocket-Protocol)

握手阶段可以协商应用层子协议,常用于统一消息格式:

客户端:
Sec-WebSocket-Protocol: chat, v2.chat

服务端(选择其一):
Sec-WebSocket-Protocol: v2.chat

STOMP over WebSocket

STOMP(Simple Text Oriented Messaging Protocol)是常见的 WebSocket 上层协议,Spring WebSocket 内置支持:

// 前端 @stomp/stompjs
const client = new Client({
  brokerURL: 'wss://example.com/ws',
  onConnect: () => {
    // 订阅频道
    client.subscribe('/topic/general', (message) => {
      console.log(JSON.parse(message.body))
    })
    // 发送消息
    client.publish({
      destination: '/app/chat',
      body: JSON.stringify({ content: 'Hello' }),
    })
  },
})
client.activate()
// Spring Boot 服务端
@Configuration
@EnableWebSocketMessageBroker
public class StompConfig implements WebSocketMessageBrokerConfigurer {
 
    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        registry.enableSimpleBroker("/topic", "/queue");
        registry.setApplicationDestinationPrefixes("/app");
    }
 
    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/ws")
                .setAllowedOriginPatterns("*")
                .withSockJS();  // 降级兼容
    }
}
 
@Controller
public class ChatController {
 
    @MessageMapping("/chat")
    @SendTo("/topic/general")
    public ChatMessage handle(ChatMessage message) {
        return message;
    }
}

STOMP vs 原生 WebSocket:STOMP 提供发布/订阅模型、消息路由、Ack 机制;原生 WebSocket 更轻量,适合自定义协议。


负载均衡与扩展性

WebSocket 是持久连接,负载均衡需要特殊处理:

粘性会话(Sticky Session):同一客户端的请求必须路由到同一服务节点,否则连接断开。Nginx 配置示例:

upstream ws_backend {
    ip_hash;   # 基于客户端 IP 固定路由
    server 10.0.0.1:8080;
    server 10.0.0.2:8080;
}

消息总线方案:多节点间通过 Redis Pub/Sub 或 MQ 广播消息,每节点只管理自己的连接:

客户端 A → 节点 1 → Redis Pub/Sub → 节点 2 → 客户端 B

Spring WebSocket + Spring Session + Redis 是常见生产方案。


WebSocket vs SSE vs 轮询 选型

方案方向连接复杂度适用场景
短轮询单向(拉)频繁建立低频通知,兼容旧系统
长轮询单向(拉)保持 hold无 SSE/WS 支持的场景
SSE单向(服务端推)持久 HTTP消息流、进度推送、AI 流式输出
WebSocket双向持久 TCP聊天、协同编辑、游戏、高频双向通信

SSE(Server-Sent Events) 适合只需要服务端推送的场景,基于普通 HTTP,自动重连,无需额外握手协议:

const es = new EventSource('/api/stream')
es.onmessage = (e) => console.log(e.data)
es.addEventListener('progress', (e) => updateProgress(e.data))

相关

  • HTTP — WebSocket 通过 HTTP Upgrade 建立
  • TCP — WebSocket 底层基于 TCP
  • 前后端交互 — WebSocket 在前后端实时通信中的应用
  • Socket — 操作系统层面的网络编程接口