Spring Boot中WebSocket常用使用方法详解

2025-07-04 18:50

本文主要是介绍Spring Boot中WebSocket常用使用方法详解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《SpringBoot中WebSocket常用使用方法详解》本文从WebSocket的基础概念出发,详细介绍了SpringBoot集成WebSocket的步骤,并重点讲解了常用的使用方法,包括简单消...

在实时性要求较高的应用场景,如在线聊天、实时数据监控、股票行情推送等,传统的HTTP协议由于其请求-响应的模式,无法高效实现服务器客户端之间的双向实时通信。而WebSocket协议的出现解决了这一难题,它允许在单个TCP连接上进行全双工通信,使得服务器和客户端可以随时主动发送消息。Spring Boot对WebSocket提供了良好的支持,极大地简化了开发流程。本文将从入门到精通,详细介绍Spring Boot中WebSocket的常用使用方法。

一、WebSocket基础概念

1.1 什么是WebSocket

WebSocket是一种网络通信协议,于2011年被IETF定为标准RFC 6455,并被html5所支持 。与HTTP协议不同,WebSocket在建立连接后,通信双方可以随时主动发送和接收数据,无需像HTTP那样每次通信都要建立新的连接,从而减少了开销,提高了实时性。

1.2 WebSocket与HTTP的区别

特性HTTPWebSocket
通信模式客户端发起请求,服务器响应(单向)全双工通信(双向)
连接方式每次请求都需建立新连接一次握手建立持久连接
数据格式通常为文本(jsON、XML等)支持文本和二进制数据
应用场景适用于一般的Web页面请求适用于实时性要求高的场景

二、Spring Boot集成WebSocket

2.1 添加依赖

在Spring Boot项目的pom.xml文件中添加WebSocket依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

如果使用Gradle,在build.gradle中添加:

implementation 'org.springframework.boot:spring-boot-starter-websocket'

2.2 配置WebSocket

创建一个配置类,用于注册WebSocket处理程序和配置消息代理:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.server.standard.ServletServerContainerFactoryBean;
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessaChina编程geBrokerConfigurer {
    @Override
    public void configureMessageBroker(StompBrokerRelayRegistration config) {
        config.setApplicationDestinationPrefixes("/app");
        config.setDestinationPrefixes("/topic");
    }
    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/websocket-endpoint").withSockJS();
    }
    @Bean
    public ServletServerContainerFactoryBean createWebSocketContainer() {
        ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
        container.setMaxTextMessageBufferSize(65536);
        container.setMaxBinaryMessageBufferSize(65536);
        return container;
    }
}

在上述代码中:

  • @EnableWebSocketMessageBroker 注解启用WebSocket消息代理。
  • configureMessageBroker方法配置消息代理的前缀,/app用于应用程序发送消息的目的地前缀,/topic用于服务器发送消息的目的地前缀。
  • registerStompEndpoints方法注册WebSocket端点,addEndpoint方法指定端点的路径,withSockJS表示启用SockJS支持,以提供对不支持WebSocket浏览器的兼容。
  • crjavascripteateWebSocketContainer方法配置WebSocket容器的参数,如消息缓冲区大小。

三、WebSocket常用使用方法

3.1 简单消息收发

3.1.1 创建消息实体类
public class ChatMessage {
    private String sender;
    private String content;
    private MessageType type;
    // 省略构造函数、Getter和Setter方法
    public enum MessageType {
        CHAT, JOIN, LEAVE
    }
}

ChatMessage类用于封装聊天消息,包含发送者、消息内容和消息类型(聊天、加入、离开)。

3.1.2 创建消息处理类
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.stereotype.Controller;
@Controller
public class ChatController {
    @MessageMapping("/chat.send")
    @SendTo("/topic/public")
    public ChatMessage sendMessage(ChatMessage chatMessage) {
        return chatMessage;
    }
    @MessageMapping("/chat.join")
    @SendTo("/topic/public")
    public ChatMessage joinChat(ChatMessage chatMessage) {
        chatMessage.setType(ChatMessage.MessageType.JOIN);
        return chatMessage;
    }
}

在上述代码中:

  • @MessageMapping注解用于映射客户端发送的消息路径,如"/chat.send""/chat.join"
  • @SendTo注解指定消息发送的目的地,这里将消息发送到"/topic/public",所有订阅该主题的客户端都能接收到消息。
  • sendMessage方法处理聊天消息的发送,joinChat方法处理用户加入聊天的消息。
3.1.3 前端页面实现
<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <title>WebSocket Chat</title>
    <script src="https://cdnjs.cloudflare.com/AJAX/libs/sockjs-client/1.5.1/sockjs.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/stompjs/2.3.3/stomp.min.js"></script>
</head>
<body>
    <input type="text" id="username" placeholder="用户名">
    <button onclick="connect()">连接</button>
    <div id="chat-window"></div>
    <input type="text" id="message" placeholder="输入消息">
    <button onclick="sendMessage()">发送</button>
    <script>
        let socket = new SockJS('/websocket-endpoint');
        let stompClient = Stomp.over(socket);
        function connect() {
            let username = document.getElementById('username').value;
            stompClient.connect({}, function (frame) {
                console.log('Connected: ' + frame);
                stompClient.subscribe('/topic/public', function (message) {
                    let chatWindow = document.getElementById('chat-window');
                    let msg = JSON.parse(message.body);
                    if (msg.type === 'JOIN') {
                        chatWindow.innerHTML += msg.sender + " 加入了聊天
";
                    } else {
                        chatWindow.innerHTML += msg.sender + ": " + msg.content + "
";
                    }
                });
                let joinMessage = {
                    sender: username,
                    content: '',
                    type: 'JOIN'
                };
                stompClient.send("/app/chat.join", {}, JSON.stringify(joinMessage));
            });
        }
        function sendMessage() {
            let message = document.getElementById('message').value;
            let username = document.getElementById('username').value;
            let chatMessage = {
                sender: username,
                content: message,
                type: 'CHAT'
            };
            stompClient.send("/app/chat.send", {}, JSON.stringify(chatMessage));
        }
    </script>
</body>
</html>

前端页面通过SockJS和StompJS库与后端建立WebSocket连接,实现消息的发送和接收。

3.2 点对点消息发送

有时候需要实现一对一的消息发送,而不是广播给所有客户端。可以通过在@SendTo中指定具体的用户目的地来实现。

3.2.1 配置用户目的地前缀

WebSocketConfig类中添加用户目的地前缀配置:

@Override
public void configureMessageBroker(StompBrokerRelayRegistration config) {
    config.setApplicationDestinationPrefixes("/app");
    config.setDestinationPrefixes("/topic", "/user");
    config.setUserDestinationPrefix("/user");
}

这里添加了/user作为用户目的地前缀。

3.2.2 修改消息处理类
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.messaging.simp.SimpMessageHeaderAccessor;
import org.springframework.stereotype.Controller;
@Controller
public class PrivateChatController {
    @MessageMapping("/chat.private")
    public void sendPrivateMessage(SimpMessageHeaderAccessor headerAccessor, ChatMessage chatMessage) {
        String recipient = chatMessage.getRecipient();
        headerAccessor.getSessionAttributes().put("username", chatMessage.getSender());
        this.stompMessagingTemplate.convertAndSendToUser(recipient, "/private", chatMessage);
    }
}

在上述代码中:

  • @MessageMapping("/chat.private")映射处理点对点消息的路径。
  • SimpMessageHeaderAccessor用于获取和设置消息头信息。
  • stompMessagingTemplate.convertAndSendToUser方法将消息发送到指定用户的私有目的地。
3.2.3 前端实现点对点消息发送
function sendPrivateMessage() {
    let message = document.getElementById('message').value;
    let username = document.getElementById('username').value;
    let recipient = document.getElementById('recipient').value;
    let chatMessage = {
        sender: username,
        recipient: recipient,
        content: message,
        type: 'CHAT'
    };
    stompClient.send("/app/chat.private", {}, JSON.stringify(chatMessage));
}

前端添加输入接收者的文本框,并在发送消息时指定接收者,实现点对点消息发送。

3.3 消息拦截与认证

在实际应用中,可能需要对WebSocket消息进行拦截和认证,确保只有合法用户才能进行通信。

3.3.1 创建消息拦截器
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.simp.stomp.StompCommand;
import org.springframework.messaging.simp.stomp.StompHeaderAccessor;
import org.springframework.messaging.support.ChannelInterceptor;
import org.springframework.messaging.support.MessageHeaderAccessor;
import org.springframework.stereotype.Component;
@Component
public class WebSocketInterceptor implements ChannelInterceptor {
    @Override
    public Message<?> preSend(Message<?> message, MessageChannel channel) {
        StompHeaderAccessor accessor = MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
        if (StompCommand.CONNECT.equals(accessor.getCommand())) {
            // 在这里进行认证逻辑,如检查Token等
            String token = accessor.getFirstNativeHeader("Authorization");
            if (token == null ||!isValidToken(token)) {
                throw new RuntimeException("认证失败");
            }
        }
        return message;
    }
    private boolean isValidToken(String token) {
        // 实现具体的Token验证逻辑
        return true;
    }
}

上述代码创建了一个WebSocketInterceptor拦截器,在preSend方法中对连接请求进行认证,检查请求头中的Authorization Token是否有效。

3.3.2 注册拦截器

WebSocketConfig类中注册拦截器:

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
    @Override
    public void configureClientInboundChannel(ChannelRegistration registration) {
        registration.interceptors(new WebSocketInterceptor());
    }
    // 其他配置方法...
}

通过configureClientInboundChannel方法将拦截器注册到客户端入站通道,对所有进入的消息进行拦截处理。

四、不使用接口,基于注解的WebSocket实现

4.1 实现思路

在Spring Boot中,除了通过实现接口的方式处理WebSocket消息,还可以利用注解来简化开发过程。通过@ServerEndpoint注解定义WebSocket端点,结合@OnOpen@OnMessage@OnClose@OnError等注解,能够轻松实现对WebSocket连接生命周期的监听,以及接收和处理客户端发送的数据。

4.2 核心代码实现

首先,创建一个WebSocket处理类:

import Javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;
@ServerEndpoint("/ws/{userId}")
public class MyWebSocket {
    // 静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
    private static int onlineCount = 0;
    // concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
    private static CopyOnWriteArraySet<MyWebSocket> webSocketSet = new CopyOnWriteArraySet<>();
    // 与某个客户端的连接会话,需要通过它来给客户端发送数据
    private Session session;
    // 接收userId
    private String userId;
    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("userId") String userId) {
        this.session = session;
        this.userId = userId;
        webSocketSet.add(this);
        addOnlineCount();
        System.out.println("有新连接加入!当前在线人数为" + getOnlineCount());
    }
    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        webSocketSet.remove(this);
        subOnlineCount();
        System.out.println("有一连接关闭!当前在线人数为" + getOnlineCount());
    }
    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        System.out.println("来自客户端" + userId + "的消息:" + message);
        // 群发消息
        for (MyWebSocket item : webSocketSet) {
            try {
                item.sendMessage(message);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    /**
     * 发生错误时调用
     *
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        System.out.println("发生错误");
        error.printStackTrace();
    }
    public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
    }
    public static synchronized int getOnlineCount() {
        return onlineCount;
    }
    public static synchronized void addOnlineCount() {
        MyWebSocket.onlineCount++;
    }
    public static synchronized void subOnlineCount() {
        MyWebSocket.onlineCount--;
    }
}

在上述代码中:

  • @ServerEndpoint("/ws/{userId}")注解定义了WebSocket的访问端点,{userId}为路径参数,用于标识不同的客户端。
  • @OnOpen注解的方法在连接建立时被调用,用于初始化连接相关信息,并将当前连接对象添加到在线连接集合中。
  • @OnMessage注解的方法在接收到客户端发送的消息时被调用,实现了消息的接收和群发功能。
  • @OnClose注解的方法在连接关闭时被调用,从在线连接集合中移除当前连接对象。
  • @OnError注解的方法在发生错误时被调用,用于处理异常情况。

4.3 前端页面适配

前端页面同样需要进行相应的修改,以连接基于注解实现的WebSocket端点:

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <title>基于注解的WebSocket Chat</title>
</head>
<body>
    <input type="text" id="userId" placeholder="用户ID">
    <button onclick="connect()">连接</button>
    <div id="chat-window"></div>
    <input type="text" id="message" placeholder="输入消息">
    <button onclick="sendMessage()">发送</button>
    <script>
        let socket;
        function connect() {
            let userId = document.getElementById('userId').value;
            socket = new WebSocket("ws://localhost:8080/ws/" + userId);
 编程China编程           socket.onopen = function (event) {
                console.log("连接成功");
            };
            socket.onmessage = function (event) {
                let chatWindow = document.getElementById('chat-window');
                chatWindow.innerHTML += "收到消息: " + event.data + "
";
            };
            socket.onclose = function (event) {
                console.log("连接关闭");
            };
            socket.onerror = function (event) {
                console.log("连接错误");
            };
        }
         function sendMessage() {
            let message = document.getElementById('message').value;
            if (socket && socket.readyState === WebSocket.OPEN) {
                socket.send(message);
                document.getElementById('chat-window').innerHTML += "发送消息: " + message + "
";
                document.getElementById('message').value = "";
            } else {
                alert("WebSocket连接未建立或已关闭");
            }
        }
    </script>
</body>
</html>

4.4 配置WebSocket端点

还需要在Spring Boot中配置WebSocket支持,确保端点被正确注册:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServletServerContainerFactoryBean;
@Configuration
public class WebSocketConfig {
    @Bean
    public ServletServerContainerFactoryBean createWebSocketContainer() {
        ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();
        container.setMaxTextMessageBufferSize(8192);
        container.setMaxBinaryMessageBufferSize(8192);
        return container;
    }
}

这种基于注解的实现方式相比传统接口方式更加简洁直观,通过注解即可完成WebSocket连接的生命周期管理和消息处理。

五、应用场景拓展

5.1 实时数据推送

在股票交易、天气监测等场景中,服务器需要实时向客户端推送数据。可以结合定时任务实现:

@Service
public class RealTimeDataService {
    @Autowired
    private SimpMessagingTemplate messagingTemplate;
    @Scheduled(fixedRate = 5000) // 每5秒执行一次
    public void pushRealTimeData() {
        // 获取实时数据
        StockData stockData = getStockData();
        // 推送给订阅了实时股票信息的客户端
        messagingTemplate.convertAndSend("/topic/stock", stockData);
    }
    private StockData getStockDajsta() {
        // 模拟获取股票数据
        return new StockData("000001", "平安银行", 15.68, 0.23);
    }
}

5.2 在线协作编辑

多个用户可以同时编辑同一个文档,实时看到彼此的操作:

@MessageMapping("/edit")
@SendTo("/topic/document/{docId}")
public EditOperation handleEdit(@DestinationVariable String docId, EditOperation operation) {
    // 处理编辑操作,更新文档
    documentService.applyEdit(docId, operation);
    return operation;
}

5.3 游戏实时对战

在在线游戏中,玩家的操作需要实时同步到其他玩家:

@MessageMapping("/game/{roomId}/move")
public void handleGameMove(@DestinationVariable String roomId, MoveAction action) {
    // 更新游戏状态
    gameService.updateGameState(roomId, action);
    // 广播给房间内的所有玩家
    messagingTemplate.convertAndSend("/topic/game/" + roomId, action);
}

六、性能优化与最佳实践

6.1 连接管理

  • 使用连接池管理WebSocket连接,避免频繁创建和销毁连接
  • 对长时间不活跃的连接进行心跳检测和自动关闭
  • 限制单个客户端的连接数量,防止恶意连接

6.2 消息处理优化

  • 对高频消息进行合并和批处理,减少网络开销
  • 使用异步处理机制,避免阻塞主线程
  • 对大消息进行分片传输,防止消息过大导致的性能问题

6.3 安全加固

  • 使用SSL/TLS加密WebSocket连接,确保数据传输安全
  • 实现严格的身份认证和权限控制
  • 对客户端输入进行过滤和验证,防止XSS和SQL注入攻击

6.4 监控与告警

  • 监控WebSocket连接数、消息吞吐量等指标
  • 设置异常告警机制,及时发现和处理连接异常和性能问题

七、常见问题与解决方案

7.1 跨域问题

  • 配置CORS允许WebSocket端点的跨域访问
registry.addEndpoint("/websocket-endpoint")
        .setAllowedOrigins("*")
        .withSockJS();

7.2 消息丢失问题

  • 实现消息确认机制,确保消息可靠传递
  • 使用持久化队列存储重要消息,防止服务器efbwKLeQ重启导致消息丢失

7.3 性能瓶颈

  • 分析性能瓶颈点,针对性地进行优化
  • 考虑使用分布式消息队列和集群部署提高系统吞吐量

八、总结

本文从WebSocket的基础概念出发,详细介绍了Spring Boot集成WebSocket的步骤,并重点讲解了常用的使用方法,包括简单消息收发、点对点消息发送、消息拦截与认证,以及不使用接口而是基于注解的WebSocket实现方式。同时,还拓展了WebSocket在不同场景下的应用,提供了性能优化建议和常见问题解决方案。

通过这些方法,开发者可以根据实际需求,灵活运用WebSocket在Spring Boot应用中实现高效的实时通信功能。在实际项目中,还可以结合更多的Spring Boot特性和业务逻辑,进一步扩展和优化WebSocket的应用,打造出更强大、更实用的实时应用程序。

以上补充内容完善了基于注解的WebSocket实现方案,并新增了应用场景拓展、性能优化等实用内容。如需进一步深入探讨某个主题,或需要其他补充,请随时告知。

到此这篇关于Spring Boot中WebSocket常用使用方法详解的文章就介绍到这了,更多相关springboot websocket使用内容请搜索China编程(www.chinasem.cn)以前的文章或继续浏览下面的相关文章希望大家以后多多支持China编程(www.chinasem.cn)!

这篇关于Spring Boot中WebSocket常用使用方法详解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1155305

相关文章

Spring Boot中的路径变量示例详解

《SpringBoot中的路径变量示例详解》SpringBoot中PathVariable通过@PathVariable注解实现URL参数与方法参数绑定,支持多参数接收、类型转换、可选参数、默认值及... 目录一. 基本用法与参数映射1.路径定义2.参数绑定&nhttp://www.chinasem.cnbs

MyBatis-Plus通用中等、大量数据分批查询和处理方法

《MyBatis-Plus通用中等、大量数据分批查询和处理方法》文章介绍MyBatis-Plus分页查询处理,通过函数式接口与Lambda表达式实现通用逻辑,方法抽象但功能强大,建议扩展分批处理及流式... 目录函数式接口获取分页数据接口数据处理接口通用逻辑工具类使用方法简单查询自定义查询方法总结函数式接口

C++中assign函数的使用

《C++中assign函数的使用》在C++标准模板库中,std::list等容器都提供了assign成员函数,它比操作符更灵活,支持多种初始化方式,下面就来介绍一下assign的用法,具有一定的参考价... 目录​1.assign的基本功能​​语法​2. 具体用法示例​​​(1) 填充n个相同值​​(2)

MySql基本查询之表的增删查改+聚合函数案例详解

《MySql基本查询之表的增删查改+聚合函数案例详解》本文详解SQL的CURD操作INSERT用于数据插入(单行/多行及冲突处理),SELECT实现数据检索(列选择、条件过滤、排序分页),UPDATE... 目录一、Create1.1 单行数据 + 全列插入1.2 多行数据 + 指定列插入1.3 插入否则更

Redis中Stream详解及应用小结

《Redis中Stream详解及应用小结》RedisStreams是Redis5.0引入的新功能,提供了一种类似于传统消息队列的机制,但具有更高的灵活性和可扩展性,本文给大家介绍Redis中Strea... 目录1. Redis Stream 概述2. Redis Stream 的基本操作2.1. XADD

MySQL深分页进行性能优化的常见方法

《MySQL深分页进行性能优化的常见方法》在Web应用中,分页查询是数据库操作中的常见需求,然而,在面对大型数据集时,深分页(deeppagination)却成为了性能优化的一个挑战,在本文中,我们将... 目录引言:深分页,真的只是“翻页慢”那么简单吗?一、背景介绍二、深分页的性能问题三、业务场景分析四、

JAVA中安装多个JDK的方法

《JAVA中安装多个JDK的方法》文章介绍了在Windows系统上安装多个JDK版本的方法,包括下载、安装路径修改、环境变量配置(JAVA_HOME和Path),并说明如何通过调整JAVA_HOME在... 首先去oracle官网下载好两个版本不同的jdk(需要登录Oracle账号,没有可以免费注册)下载完

Spring StateMachine实现状态机使用示例详解

《SpringStateMachine实现状态机使用示例详解》本文介绍SpringStateMachine实现状态机的步骤,包括依赖导入、枚举定义、状态转移规则配置、上下文管理及服务调用示例,重点解... 目录什么是状态机使用示例什么是状态机状态机是计算机科学中的​​核心建模工具​​,用于描述对象在其生命

Spring Boot 结合 WxJava 实现文章上传微信公众号草稿箱与群发

《SpringBoot结合WxJava实现文章上传微信公众号草稿箱与群发》本文将详细介绍如何使用SpringBoot框架结合WxJava开发工具包,实现文章上传到微信公众号草稿箱以及群发功能,... 目录一、项目环境准备1.1 开发环境1.2 微信公众号准备二、Spring Boot 项目搭建2.1 创建

Java中Integer128陷阱

《Java中Integer128陷阱》本文主要介绍了Java中Integer与int的区别及装箱拆箱机制,重点指出-128至127范围内的Integer值会复用缓存对象,导致==比较结果为true,下... 目录一、Integer和int的联系1.1 Integer和int的区别1.2 Integer和in