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

相关文章

Conda与Python venv虚拟环境的区别与使用方法详解

《Conda与Pythonvenv虚拟环境的区别与使用方法详解》随着Python社区的成长,虚拟环境的概念和技术也在不断发展,:本文主要介绍Conda与Pythonvenv虚拟环境的区别与使用... 目录前言一、Conda 与 python venv 的核心区别1. Conda 的特点2. Python v

C#中Guid类使用小结

《C#中Guid类使用小结》本文主要介绍了C#中Guid类用于生成和操作128位的唯一标识符,用于数据库主键及分布式系统,支持通过NewGuid、Parse等方法生成,感兴趣的可以了解一下... 目录前言一、什么是 Guid二、生成 Guid1. 使用 Guid.NewGuid() 方法2. 从字符串创建

SpringBoot+Docker+Graylog 如何让错误自动报警

《SpringBoot+Docker+Graylog如何让错误自动报警》SpringBoot默认使用SLF4J与Logback,支持多日志级别和配置方式,可输出到控制台、文件及远程服务器,集成ELK... 目录01 Spring Boot 默认日志框架解析02 Spring Boot 日志级别详解03 Sp

Python使用python-can实现合并BLF文件

《Python使用python-can实现合并BLF文件》python-can库是Python生态中专注于CAN总线通信与数据处理的强大工具,本文将使用python-can为BLF文件合并提供高效灵活... 目录一、python-can 库:CAN 数据处理的利器二、BLF 文件合并核心代码解析1. 基础合

java中反射Reflection的4个作用详解

《java中反射Reflection的4个作用详解》反射Reflection是Java等编程语言中的一个重要特性,它允许程序在运行时进行自我检查和对内部成员(如字段、方法、类等)的操作,本文将详细介绍... 目录作用1、在运行时判断任意一个对象所属的类作用2、在运行时构造任意一个类的对象作用3、在运行时判断

Python使用OpenCV实现获取视频时长的小工具

《Python使用OpenCV实现获取视频时长的小工具》在处理视频数据时,获取视频的时长是一项常见且基础的需求,本文将详细介绍如何使用Python和OpenCV获取视频时长,并对每一行代码进行深入解析... 目录一、代码实现二、代码解析1. 导入 OpenCV 库2. 定义获取视频时长的函数3. 打开视频文

java如何解压zip压缩包

《java如何解压zip压缩包》:本文主要介绍java如何解压zip压缩包问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Java解压zip压缩包实例代码结果如下总结java解压zip压缩包坐在旁边的小伙伴问我怎么用 java 将服务器上的压缩文件解压出来,

MySQL 中的 CAST 函数详解及常见用法

《MySQL中的CAST函数详解及常见用法》CAST函数是MySQL中用于数据类型转换的重要函数,它允许你将一个值从一种数据类型转换为另一种数据类型,本文给大家介绍MySQL中的CAST... 目录mysql 中的 CAST 函数详解一、基本语法二、支持的数据类型三、常见用法示例1. 字符串转数字2. 数字

SpringBoot中SM2公钥加密、私钥解密的实现示例详解

《SpringBoot中SM2公钥加密、私钥解密的实现示例详解》本文介绍了如何在SpringBoot项目中实现SM2公钥加密和私钥解密的功能,通过使用Hutool库和BouncyCastle依赖,简化... 目录一、前言1、加密信息(示例)2、加密结果(示例)二、实现代码1、yml文件配置2、创建SM2工具

Spring WebFlux 与 WebClient 使用指南及最佳实践

《SpringWebFlux与WebClient使用指南及最佳实践》WebClient是SpringWebFlux模块提供的非阻塞、响应式HTTP客户端,基于ProjectReactor实现,... 目录Spring WebFlux 与 WebClient 使用指南1. WebClient 概述2. 核心依