SpringBoot整合WebSocket服务 springboot整合websocket springboot使用websocket springboot使用websocket

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依赖

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

2.2 (推荐方式) 使用 WebSocketConfigurer方式创建 WebSocket端点 (二选一)

2.2.1 创建一个 测试 WebSocket处理器

类名称:TestSocketHandler

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

/**
 * <h2>测试 - socket连接处理器</h2>
 * <p>
 *
 * </p>
 *
 * @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;

/**
 * <h2></h2>
 * <p>
 *
 * </p>
 *
 * @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;


/**
 * <h2></h2>
 *
 * @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;

/**
 * <h2></h2>
 * <p>
 *
 * </p>
 *
 * @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 加入到白名单

@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;
    }
}

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

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

4、两种方式如何选择

ServerEndpointExporterregisterWebSocketHandlers是两种不同的方式,用于在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的特性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值