All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.github.dengchen2020.websocket.BaseSpringWebSocketHandler Maven / Gradle / Ivy

The newest version!
package io.github.dengchen2020.websocket;

import jakarta.websocket.CloseReason;
import jakarta.websocket.Session;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.socket.*;
import org.springframework.web.socket.adapter.NativeWebSocketSession;
import org.springframework.web.socket.handler.AbstractWebSocketHandler;
import org.springframework.web.socket.handler.ConcurrentWebSocketSessionDecorator;
import org.springframework.web.socket.handler.SessionLimitExceededException;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 基于spring封装的websocket的基础处理器,推荐使用
 *
 * @author dengchen
 * @since 2024/6/26
 */
public class BaseSpringWebSocketHandler extends AbstractWebSocketHandler {

    /**
     * 处理来自基础 WebSocket的 文本消息
     *
     * @param session websocket会话
     * @param message 文本消息
     */
    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) {
        if (log.isDebugEnabled()) log.debug("收到文本消息:{}", message);
    }

    /**
     * 处理来自基础 WebSocket的 二进制数据消息
     *
     * @param session websocket会话
     * @param message 二进制数据消息
     */
    @Override
    protected void handleBinaryMessage(WebSocketSession session, BinaryMessage message) {
        if (log.isDebugEnabled()) log.debug("收到二进制数据消息:{}", message);
    }

    /**
     * 处理来自基础 WebSocket的 pong消息
     *
     * @param session websocket会话
     * @param message pong消息
     */
    @Override
    protected void handlePongMessage(WebSocketSession session, PongMessage message) {
        if (log.isDebugEnabled()) log.debug("收到pong消息:{}", message.getPayload());
    }

    /**
     * 在任一端关闭 WebSocket 连接后调用,或者在发生传输错误后调用。尽管从技术上讲,会话可能仍处于打开状态,但根据基础实现,不鼓励此时发送消息,并且很可能不会成功
     *
     * @param session websocket会话
     * @param status  表示 WebSocket 关闭状态代码和原因。1xxx 范围内的状态代码由协议预定义。或者,可以发送带有原因的状态代码
     */
    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
        getSessions().values().removeIf(s -> s.getId().equals(session.getId()));
        close(session);
        if (CLOSE_CODE.contains(status.getCode())) {
            if (log.isDebugEnabled()) log.debug("连接关闭,原因是:{}", status.getReason());
        } else {
            log.warn("连接关闭,原因是:{}", status);
        }
    }

    /**
     * 处理来自基础 WebSocket 消息传输的错误
     *
     * @param session   websocket会话
     * @param exception 异常对象
     */
    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) {
        if (log.isDebugEnabled()) log.debug("连接发生异常,原因是:{}", exception.toString());
    }

    private final Logger log = LoggerFactory.getLogger(getClass());

    private static final Map sessions = new ConcurrentHashMap<>();

    private final Set CLOSE_CODE = Set.of(CloseReason.CloseCodes.NORMAL_CLOSURE.getCode(), CloseReason.CloseCodes.GOING_AWAY.getCode(), CloseReason.CloseCodes.CLOSED_ABNORMALLY.getCode(), CloseReason.CloseCodes.VIOLATED_POLICY.getCode());

    @SuppressWarnings("unchecked")
    public  Map getSessions() {
        return (Map) sessions;
    }

    /**
     * 在 WebSocket 协商成功且 WebSocket 连接打开并可供使用后调用
     *
     * @param session websocket会话
     */
    @Override
    public void afterConnectionEstablished(WebSocketSession session) {
        if (session.getPrincipal() == null) {
            CloseStatus status = CloseStatus.POLICY_VIOLATION.withReason("获取Token认证信息失败,请重新登录");
            onlineFailEvent(session, status);
            close(session, status);
            return;
        }
        initSessionConfig(session);
        getSessions().put(online(session), wrap(session));
        onlineSuccessEvent(session);
    }

    /**
     * 包装WebSocketSession,例如:
     * 
     * new ConcurrentWebSocketSessionDecorator(session, 10 * 1000, 8 * 1024)
     * 
* * @param session 原WebSocketSession * @return 新WebSocketSession */ public WebSocketSession wrap(WebSocketSession session) { return new ConcurrentWebSocketSessionDecorator(session,1000 * 10,1024 * 8); } /** * 初始化会话配置 * * @param session websocket会话 */ public void initSessionConfig(WebSocketSession session) { if(session instanceof NativeWebSocketSession nativeWebSocketSession){ Session nativeSession = nativeWebSocketSession.getNativeSession(Session.class); if(nativeSession != null){ nativeSession.setMaxIdleTimeout(90 * 1000); nativeSession.getAsyncRemote().setSendTimeout(10 * 1000); } } session.setTextMessageSizeLimit(16 * 1024); session.setBinaryMessageSizeLimit(1024 * 1024); } /** * 上线 * * @param session websocket会话 * @return websocket客户端信息 */ public DefaultWebSocketClientInfo online(WebSocketSession session) { return new DefaultWebSocketClientInfo(session.getId()); } /** * 获取客户端信息 * * @param session websocket会话 * @return 客户端信息 */ public DefaultWebSocketClientInfo getClientInfo(WebSocketSession session) { return new DefaultWebSocketClientInfo(session.getId()); } /** * 上线成功事件 * * @param session websocket会话 */ public void onlineSuccessEvent(WebSocketSession session) { if (log.isDebugEnabled()) log.debug("客户端{}上线成功,当前在线数量:{}", session.getId(), getSessions().size()); } /** * 上线失败事件 * * @param session websocket会话 * @param status 异常原因状态代码 */ public void onlineFailEvent(WebSocketSession session, CloseStatus status) { if (log.isDebugEnabled()) log.debug("客户端{}上线失败", session.getId()); } /** * 关闭连接 * * @param session websocket会话 */ public void close(WebSocketSession session, CloseStatus closeStatus) { try { session.close(closeStatus); } catch (IOException e) { log.error("关闭连接失败,异常信息:{}", e.getMessage()); } } /** * 关闭连接 * * @param session websocket会话 */ public void close(WebSocketSession session) { try { session.close(); } catch (IOException e) { log.error("关闭连接失败,异常信息:{}", e.getMessage()); } } /** * 发送ping消息 * * @param session websocket会话 */ public void sendPing(WebSocketSession session) { try { session.sendMessage(new PingMessage(ByteBuffer.allocate(1))); } catch (SessionLimitExceededException e) { close(session, e.getStatus()); } catch (Exception e) { log.error("发送ping消息失败,异常信息:{}", e.getMessage()); } } /** * 发送pong消息 * * @param session websocket会话 */ public void sendPong(WebSocketSession session) { try { session.sendMessage(new PongMessage(ByteBuffer.allocate(1))); } catch (SessionLimitExceededException e) { close(session, e.getStatus()); } catch (Exception e) { log.error("发送pong消息失败,异常信息:{}", e.getMessage()); } } /** * 向用户发送文本消息 * * @param session websocket会话 * @param message 文本消息 */ public void send(WebSocketSession session, String message) { try { session.sendMessage(new TextMessage(message)); } catch (SessionLimitExceededException e) { close(session, e.getStatus()); } catch (Exception e) { log.error("发送文本消息失败:{},异常信息:{}", message, e.getMessage()); } } /** * 向用户发送二进制数据消息 * * @param session websocket会话 * @param byteBuffer 二进制数据 */ public void send(WebSocketSession session, ByteBuffer byteBuffer) { try { session.sendMessage(new BinaryMessage(byteBuffer)); } catch (SessionLimitExceededException e) { close(session, e.getStatus()); } catch (Exception e) { log.error("发送二进制数据消息失败:{},异常信息:{}", byteBuffer, e.getMessage()); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy