【Spring WebSocket详解】Spring WebSocket从入门到实战

一、WebSocket协议基础

[1.1 Websocket概念](#1.1 Websocket概念)

[1.2 WebSocket握手流程:](#1.2 WebSocket握手流程:)

[二、Spring WebSocket技术详解](#二、Spring WebSocket技术详解)

[2.1 Spring WebSocket概述](#2.1 Spring WebSocket概述)

[2.2 STOMP协议支持](#2.2 STOMP协议支持)

[三、 STOMP协议实现简单通信系统](#三、 STOMP协议实现简单通信系统)

[3.1 pom.xml文件](#3.1 pom.xml文件)

[3.2 配置STOMP代理](#3.2 配置STOMP代理)

[3.3 使用@MessageMapping的Controller](#3.3 使用@MessageMapping的Controller)

[3.4 前端连接STOMP(Vue2)](#3.4 前端连接STOMP(Vue2))

[四、Spring WebSocket构建简单实时通信系统](#四、Spring WebSocket构建简单实时通信系统)

[4.1 pom.xml文件](#4.1 pom.xml文件)

[4.2 核心配置类](#4.2 核心配置类)

[4.3 消息处理器类](#4.3 消息处理器类)

[4.4 客户端测试代码(Vue2)](#4.4 客户端测试代码(Vue2))

五、扩展与其他


在现代Web应用中,实时通信已成为基本需求。无论是聊天应用、实时通知系统还是在线协作工具,都需要服务器能够主动向客户端推送消息。本文将详细介绍如何使用Spring WebSocket构建一个高效的实时通信系统。

一、WebSocket协议基础

1.1 Websocket概念

WebSocket是一种在单个TCP连接上进行全双工通信的协议,解决了HTTP协议的半双工和轮询效率低的问题。其核心特点:

全双工:客户端和服务端可同时发送数据。

持久连接:一次握手后保持长连接。

低延迟:无需频繁建立连接。

1.2 WebSocket握手流程:

客户端发起HTTP请求:包含Upgrade:websocket头。

服务端响应101状态码:表示协议切换成功。

数据帧传输:后续通信通过二进制帧(Frame)进行。

握手请求示例

html 复制代码
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Version: 13

握手响应示例

html 复制代码
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=

二、Spring WebSocket技术详解

2.1 Spring WebSocket概述

Spring WebSocket是Spring框架对WebSocket协议的实现,提供了更高层次的抽象,与Spring生态系统无缝集成。

核心组件:

WebSocketHandler:处理WebSocket消息的接口

java 复制代码
public class MyRawHandler implements WebSocketHandler {
    
    // 处理消息
    @Override
    public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) {
        if (message instanceof TextMessage) {
            handleTextMessage(session, (TextMessage) message);
        }
        else if (message instanceof BinaryMessage) {
            handleBinaryMessage(session, (BinaryMessage) message);
        }
    }

    // 必须实现的空方法(适配器模式)
    @Override public void afterConnectionEstablished(WebSocketSession session) {}
    @Override public void handleTransportError(WebSocketSession session, Throwable exception) {}
    @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) {}
    @Override public boolean supportsPartialMessages() { return false; }
}

WebSocketSession:代表一个WebSocket连接

java 复制代码
// 广播消息到主题
template.convertAndSend("/topic/public", chatMessage);

// 发送给特定用户
template.convertAndSendToUser(userId, "/queue/private", message);

SimpMessagingTemplate:用于发送消息的工具类

java 复制代码
// 广播消息到主题
template.convertAndSend("/topic/public", chatMessage);

// 发送给特定用户
template.convertAndSendToUser(userId, "/queue/private", message);

@MessageMapping:是 Spring STOMP协议的一部分,用于定义消息的接收端点(类似HTTP的 @RequestMapping)

2.2 STOMP协议支持

Spring WebSocket默认支持STOMP(Simple Text Oriented Messaging Protocol)子协议,提供了更高级的消息模式:

目的地(Destination)概念:

/app:应用前缀(由@MessageMapping处理)

/topic:广播目的地

/queue:点对点队列

/user:用户专属队列(自动转换)

java 复制代码
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
    config.setApplicationDestinationPrefixes("/app");
    config.enableSimpleBroker("/topic", "/queue");
    config.setUserDestinationPrefix("/user");
}

订阅/发布模式:

前端订阅:

javascript 复制代码
stompClient.subscribe('/topic/public', (message) => {
    console.log('收到广播:', message.body);
});

stompClient.subscribe('/user/queue/private', (message) => {
    console.log('收到私信:', message.body);
});

后端发布:

java 复制代码
@MessageMapping("/chat")
@SendTo("/topic/public") 
public ChatMessage broadcast(ChatMessage message) {
    return message; // 自动发送到/topic/public
}

消息确认ACK机制:

服务端配置:

java 复制代码
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
    @Override
    public void configureClientInboundChannel(ChannelRegistration registration) {
        registration.interceptors(new ChannelInterceptor() {
            @Override
            public Message<?> preSend(Message<?> message, MessageChannel channel) {
                // 消息到达前的处理
                return message;
            }
        });
    }
}

客户端ACK示例:

javascript 复制代码
stompClient.subscribe('/topic/orders', (message) => {
    processOrder(message.body);
    message.acknowledge(); // 显式确认
}, { 'ack': 'client' });

三、 STOMP协议实现简单通信系统

3.1 pom.xml文件

XML 复制代码
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-websocket</artifactId>
        <version>2.7.0</version>
    </dependency>
</dependencies>

3.2 配置STOMP代理

java 复制代码
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketStompConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void configureMessageBroker(MessageBrokerRegistry registry) {
        registry.enableSimpleBroker("/topic"); // 客户端订阅前缀
        registry.setApplicationDestinationPrefixes("/app"); // 服务端接收前缀
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/ws-stomp").withSockJS(); // 连接端点
    }
}

3.3 使用@MessageMapping的Controller

java 复制代码
@Controller
public class StompMessageController {

    // 处理发送到 "/app/chat" 的消息
    @MessageMapping("/chat")  
    @SendTo("/topic/messages") // 自动广播到所有订阅"/topic/messages"的客户端
    public String handleChat(String message) {
        return "回复: " + message;
    }
}

3.4 前端连接STOMP(Vue2)

javascript 复制代码
const socket = new SockJS('/ws-stomp');
const stompClient = Stomp.over(socket);

stompClient.connect({}, () => {
    // 订阅消息
    stompClient.subscribe('/topic/messages', (response) => {
        console.log("收到广播: " + response.body);
    });

    // 发送消息
    stompClient.send("/app/chat", {}, "Hello STOMP!");
});

四、Spring WebSocket构建简单实时通信系统

4.1 pom.xml文件

XML 复制代码
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-websocket</artifactId>
        <version>2.7.0</version>
    </dependency>
</dependencies>

4.2 核心配置类

java 复制代码
/**
 * WebSocket 配置类,用于配置 WebSocket 相关参数和处理器
 */
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

    @Autowired  // 自动注入自定义的 WebSocket 消息处理器
    private BroadcastWebSocketHandler handler;

    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        // 添加自定义的 WebSocket 处理器,并指定 WebSocket 连接路径
        registry.addHandler(handler, "/ws")
               // 设置允许跨域访问,* 表示允许所有来源
               .setAllowedOrigins("*");
               // 可以继续添加其他配置,例如:
               // .withSockJS()  // 如果需要支持 SockJS 可以添加此项
    }
}

4.3 消息处理器类

java 复制代码
import org.springframework.web.socket.*;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import java.util.concurrent.CopyOnWriteArrayList;

public class CustomWebSocketHandler extends TextWebSocketHandler {
    
    // 线程安全的连接集合
    private final List<WebSocketSession> sessions = new CopyOnWriteArrayList<>();

    // 1. 连接建立时触发
    @Override
    public void afterConnectionEstablished(WebSocketSession session) {
        sessions.add(session);
        log.info("新连接: ID={}, URI={}", session.getId(), session.getUri());
    }

    // 2. 接收文本消息时触发
    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) {
        String payload = message.getPayload();
        log.info("收到消息: 来自{} 内容={}", session.getId(), payload);
        // 使用Jackson将JSON字符串转换为User对象(假如是user对象)
        ObjectMapper objectMapper = new ObjectMapper();
        User user = objectMapper.readValue(payload, User.class);
        
        // 业务处理逻辑
        processMessage(session, payload);
    }

    // 3. 连接关闭时触发
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
        sessions.remove(session);
        log.info("连接关闭: ID={}, 原因={}", session.getId(), status.getReason());
    }

    // 4. 传输错误处理
    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) {
        log.error("传输错误: " + session.getId(), exception);
    }
}

4.4 前端连接代码(Vue2)

javascript 复制代码
// Client1
const ws1 = new WebSocket('ws://localhost:8080/ws');
ws1.onmessage = e => console.log('客户端1收到:', e.data);
ws1.send("Hello from Client1");

// Client2 
const ws2 = new WebSocket('ws://localhost:8080/ws');
ws2.onmessage = e => console.log('客户端2收到:', e.data);
ws2.send("Hello from Client2");

五、扩展与其他

如果你希望使用Netty实现性能更好的通信系统,请参考我的其他文章:

通过以上实战指南,您可以通过编写指定的handler类来构建一个稳定、高效、可扩展的Spring WebSocket实时通信系统,满足大多数实时通信场景的需求,如果有其他问题欢迎评论区留言讨论。