基于SpringBoot搭建WebSocker

发布时间 2023-06-30 11:41:17作者: SpringCore

1.添加pom依赖

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

2.添加Socket Handler,用于处理Socker消息的请求和发送。

package cn.coreqi.handler;

import org.springframework.web.socket.*;

import java.util.concurrent.ConcurrentHashMap;

/**
 * 创建WebSocketHandler的实现类
 * Spring提供了AbstractWebSocketHandler类、TextWebSocketHandler类、BinaryWebSocketHandler类,看自己需求进行继承。
 * 该类主要是用来处理消息的接收和发送
 */
public class MyWebSocketHandler implements WebSocketHandler {

    //保存用户链接
    private static ConcurrentHashMap<String, WebSocketSession> users = new ConcurrentHashMap<String, WebSocketSession>();

    //连接就绪时
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        users.put(session.getId(), session);
    }

    //处理消息
    @Override
    public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
        System.err.println(session + "---->" + message + ":"+ message.getPayload().toString());
        // message.getPayload().toString() 获取消息具体内容
        String msg = message.getPayload().toString();
        System.out.println("handleMessage......." + msg + "..........." + msg);
        // 处理消息 msgContent消息内容
        TextMessage textMessage = new TextMessage(msg, true);
        //session.sendMessage(message);
        // 调用方法(发送消息给所有人)
        sendMsgToAllUsers(textMessage);
    }

    //处理传输时异常
    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
        if(session.isOpen()){
            session.close();
        }
        System.err.println("websocket chat connection closed......");
        users.remove(session.getId());
    }

    //关闭连接时
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
        System.out.println("用户: " + session.getRemoteAddress() + " is leaving, because:" + closeStatus);
        users.remove(session.getId());
    }

    //是否支持分包
    @Override
    public boolean supportsPartialMessages() {
        return false;
    }

    //给所有用户发送消息
    public void sendMsgToAllUsers(WebSocketMessage<?> message) throws Exception{
        for (WebSocketSession user : users.values()) {
            user.sendMessage(message);
        }
    }
}

3.添加Socket Interceptor,用于拦截握手前和握手后的一些操作

package cn.coreqi.interceptor;

import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;

import java.util.Map;

/**
 * 握手拦截器,继承HttpSessionHandshakeInterceptor类,也可以实现HandshakeInterceptor接口
 * 这个的主要作用是可以在握手前做一些事,把所需要的东西放入到attributes里面,然后可以在WebSocketHandler的session中取到相应的值,具体可参考HttpSessionHandshakeInterceptor
 */
public class MyHandshakeInterceptor extends HttpSessionHandshakeInterceptor {

    /**
     * 在调用handler前处理方法。常用在注册用户信息,绑定WebSocketSession,在handler里根据用户信息获取WebSocketSession发送消息。
     * @param request
     * @param response
     * @param wsHandler
     * @param attributes
     * @return
     * @throws Exception
     */
    @Override
    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
        System.out.println("++++++++++++++++ HandshakeInterceptor: beforeHandshake  ++++++++++++++" + attributes);
        return super.beforeHandshake(request, response, wsHandler, attributes);
    }

    //握手后
    @Override
    public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception ex) {
        System.out.println("++++++++++++++++ HandshakeInterceptor: afterHandshake  ++++++++++++++");
        super.afterHandshake(request, response, wsHandler, ex);
    }
}

4.添加WebSocker配置类,用于指定路由与处理类及拦截器的映射关系

package cn.coreqi.config;

import cn.coreqi.handler.MyWebSocketHandler;
import cn.coreqi.interceptor.MyHandshakeInterceptor;
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.HandshakeInterceptor;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
import org.springframework.web.socket.server.standard.ServletServerContainerFactoryBean;

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

    /**
     * 配置websocket入口,允许访问的域、注册Handler、SockJs支持和拦截器
     * @param registry
     */
    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {

        //允许连接的域,只能以http或https开头
        String[] allowsOrigins = {"http://localhost:8080"};


        //前台 可以使用websocket环境
        //registry.addHandler注册和路由的功能,当客户端发起websocket连接,把/path交给对应的handler处理
        registry.addHandler(myWebSocketHandler(),"/coreqi")
                .addInterceptors(myInterceptor())
                .setAllowedOrigins(allowsOrigins);

        //前台 不可以使用websocket环境,则使用sockjs进行模拟连接
        registry.addHandler(myWebSocketHandler(), "/sockjs/coreqi")  //配置路径对应的处理类
                .addInterceptors(myInterceptor())   //配置拦截器
                .setAllowedOrigins(allowsOrigins)   //允许连接的域
                .withSockJS();  //提供SockJs支持
    }

    // websocket 拦截器
    @Bean
    public HandshakeInterceptor myInterceptor(){
        return new MyHandshakeInterceptor();
    }

    // websocket 处理类
    @Bean
    public WebSocketHandler myWebSocketHandler(){
        return new MyWebSocketHandler();
    }


    /**
     * 作用:检测所有带有@serverEndpoint注解的bean并注册他们。
     * 因为使用Spring的Websocket支持而非Servlet的Websocket支持,因此无需注册
     * @return
     */
//    @Bean
//    public ServerEndpointExporter serverEndpointExporter(){
//        return new ServerEndpointExporter();
//    }

}

5.测试

image