SpringBoot整合WebSocket服务

1、前言
2、开始使用
2.1、加入Maven依赖
2.2 (推荐方式) 使用 WebSocketConfigurer方式创建 WebSocket端点 (二选一)
2.2.1 创建一个 测试 WebSocket处理器
2.2.2 开启 WebSocket 并且注册WebSocket处理器
2.3 使用 ServerEndpoint方式创建 WebSocket端点 (二选一)
2.3.1创建一个 测试 WebSocket处理器
2.3.2 开启 WebSocket
3、 连接访问
4、两种方式如何选择
ServerEndpointExporter
registerWebSocketHandlers

1、前言
注意: 仅使用 SpringBoot封装好的 spring-boot-starter-websocket 服务,并不是使用第三方 Netty或者 Apache MINA

spring-boot-starter-web: 底层使用 Java WebSocket API (JSR-356)实现
spring-boot-starter-webflux: 底层使用 Netty实现(没有使用过,不知道具体)

官方文档1
官方文档2

2、开始使用

2.1、加入Maven依赖

  
      org.springframework.boot
      spring-boot-starter-websocket
  
2.2 (推荐方式) 使用 WebSocketConfigurer方式创建 WebSocket端点 (二选一)
2.2.1 创建一个 测试 WebSocket处理器

类名称:TestSocketHandler

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.socket.*;

/**
 * 

测试 - socket连接处理器

*

* *

* * @author LiYang * @createTime 2022年07月19日 9:58?上午 */ public class TestSocketHandler implements WebSocketHandler { private final static Logger LOGGER = LoggerFactory.getLogger(TestSocketHandler.class); @Override public void afterConnectionEstablished(WebSocketSession session) throws Exception { LOGGER.info("Test Socket 连接成功,sessionId:{}", session.getId()); } @Override public void handleMessage(WebSocketSession session, WebSocketMessage message) throws Exception { if (message instanceof TextMessage) { handlerTextMessage(session, (TextMessage) message); } else { LOGGER.error("Test Socket 消息处理失败,只接受 文本消息,sessionId:{}", session.getId()); } } public void handlerTextMessage(WebSocketSession session, TextMessage message) throws Exception { final String msg = message.getPayload(); LOGGER.info("Test Socket 收到消息:{}", msg); } @Override public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception { LOGGER.error("Test Socket 处理异常,sessionId:{}, 异常原因:{}", session.getId(), exception.getMessage()); } @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception { LOGGER.info("Test Socket 关闭,sessionId:{}", session.getId()); } @Override public boolean supportsPartialMessages() { return false; } }
2.2.2 开启 WebSocket 并且注册WebSocket处理器

类名称: WebSocketConfig

import com.ddt.wms.handler.BlankingPdaSocketHandler;
import com.ddt.wms.handler.StretchingStationPdaSocketHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import org.springframework.web.socket.server.standard.ServletServerContainerFactoryBean;

/**
 * 

*

* *

* * @author LiYang * @createTime 2022年07月19日 9:57?上午 */ @Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { // 注册 socket处理器 registry.addHandler(testSocketHandler (), "/ws/test").setAllowedOrigins("*"); } @Bean public ServletServerContainerFactoryBean createWebSocketContainer() { ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean(); // 消息缓冲区大小的大小 container.setMaxTextMessageBufferSize(8192); container.setMaxBinaryMessageBufferSize(8192); return container; } @Bean public WebSocketHandler testSocketHandler () { return new TestSocketHandler(); } }

2.3 使用 ServerEndpoint方式创建 WebSocket端点 (二选一)
这个方式创建 Websocket仅在 web环境下测试过,并没有在 webflux测试,如果你使用的是 webflux 可以使用第一种方式创建

一个demo,在线编辑Excel,使用的就是 ServerEndpoint方式创建 WebSocket端点 LuckySheet-Java

2.3.1创建一个 测试 WebSocket处理器
类名称:TestSocketHandler

import jakarta.websocket.*;
import jakarta.websocket.server.ServerEndpoint;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;


/**
 * 

* * @author LiYang * @since 2022年12月05日 16:52 */ @Component @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) @ServerEndpoint(value = "/ws/test") public class TestSocketHandler{ public final static Logger LOGGER = LoggerFactory.getLogger(TestSocketHandler.class); /** * WebSocket 连接成功调用方法 * * @param session 连接对象 */ @OnOpen public void onOpen(Session session) throws IOException { LOGGER.info("Test Socket 连接成功,sessionId:{}", session.getId()); } /** * 接收普通文本消息 * * @param message 消息 * @param session 当前socket连接对象 */ @OnMessage public void onMessage(String message, Session session) { if (StringUtils.isBlank(message)) { return; } LOGGER.info("Test Socket 收到消息:{}", msg); } /** * 关闭连接调用的方法 */ @OnClose public void onClose(Session session) { LOGGER.info("Test Socket 关闭,sessionId:{}", session.getId()); } /** * socket 异常调用的方法 */ @OnError public void onError(Session session, Throwable error) { LOGGER.error("Test Socket 处理异常,sessionId:{}, 异常原因:{}", session.getId(), error.getMessage()); } }
2.3.2 开启 WebSocket

类名称: WebSocketConfig

import com.ddt.wms.handler.BlankingPdaSocketHandler;
import com.ddt.wms.handler.StretchingStationPdaSocketHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
import org.springframework.web.socket.server.standard.ServletServerContainerFactoryBean;

/**
 * 

*

* *

* * @author LiYang * @createTime 2022年07月19日 9:57?上午 */ @Configuration @EnableWebSocket public class WebSocketConfig { @Bean public ServerEndpointExporter serverEndpointExporter() { return new ServerEndpointExporter(); } }
3、 连接访问

连接地址:ws://127.0.0.1:8080/ws/test

注意事项:如果使用了ShiroSpring Security 等认证鉴权框架,一定要将 WebSocket访问路径/ws/test 加入到白名单

如果配置了白名单也就还是连接不上,需要检查 还有没有其他的过滤器拦截问题跨域问题,请考虑下面两个方法

  • 将Websocket服务独立为一个web服务部署
  • 使用第三方socket框架内嵌到项目里面,例如 Netty(推荐)Mina
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable() // 禁用CSRF保护
            .authorizeRequests()
            .antMatchers("/websocket/**").permitAll() // 允许所有对"/ws/**"的请求
            .anyRequest().authenticated() // 所有其他请求都需要认证
            .and()
            .headers().frameOptions().sameOrigin() // 允许同源策略下的iframe加载
            .and()
            .httpBasic(); // 启用基本HTTP身份验证

        // 配置WebSocket握手请求的安全性
        http.headers().frameOptions().disable(); // 必须在生产环境中重新启用frameOptions,以防止点击劫持
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        http.addFilterBefore(new CorsFilter(corsConfigurationSource()), UsernamePasswordAuthenticationFilter.class);
    }

    private CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Arrays.asList("http://your-frontend-origin.com"));
        configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
        configuration.setAllowedHeaders(Arrays.asList("header1", "header2", "header3"));
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    }
}

4、两种方式如何选择
ServerEndpointExporter和registerWebSocketHandlers是两种不同的方式,用于在Spring应用中配置和注册WebSocket端点。它们分别属于不同的技术栈:ServerEndpointExporter用于JSR-356 WebSocket规范,而registerWebSocketHandlers则是在Spring的WebSocket抽象之上。

ServerEndpointExporter
ServerEndpointExporter是Spring框架为了支持JSR-356 WebSocket规范提供的一个组件。它会自动查找带有@ServerEndpoint注解的类,并将它们注册为WebSocket端点。ServerEndpointExporter适用于那些直接使用Java WebSocket API编写WebSocket端点的场景。当使用@ServerEndpoint注解来定义WebSocket端点时,ServerEndpointExporter会处理端点的发现和注册。

registerWebSocketHandlers
registerWebSocketHandlers方法是WebSocketConfigurer接口的一部分,这个接口允许手动配置和注册WebSocket处理器。当使用Spring的WebSocket抽象时,通常会使用这个方法。在这个方法中,可以指定一个WebSocketHandler实例,以及该处理器应当监听的URL映射。此外,还可以添加拦截器,例如HttpSessionHandshakeInterceptor,来处理握手过程中的会话管理。

主要区别
自动化与手动配置:

ServerEndpointExporter自动扫描和注册@ServerEndpoint标注的类,不需要显式配置。
registerWebSocketHandlers要求明确指定处理器和URL映射。
API和编程模型:

ServerEndpointExporter适用于原生的Java WebSocket API,遵循JSR-356规范。
registerWebSocketHandlers则基于Spring的WebSocket抽象,提供更高级别的控制和灵活性。
应用场景:

如果想要使用JSR-356的WebSocket API,并且的WebSocket端点使用@ServerEndpoint注解定义,那么ServerEndpointExporter是一个合适的选择。
如果更倾向于使用Spring的WebSocket抽象,或者需要更精细的控制,如添加自定义的拦截器或使用Spring的注解驱动(如@MessageMapping),那么应该使用registerWebSocketHandlers。
选择哪一种方法取决于的具体需求和对WebSocket API的偏好。在Spring Boot中,通常会更倾向于使用registerWebSocketHandlers方法,因为它更好地集成了Spring的特性。

你可能感兴趣的:(spring,boot,websocket,后端)