SpringBoot集成WebSocket

SpringBoot整合WebSocket

Maven依赖

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

Websocket配置类

@Configuration
@EnableWebSocket
public class BootWebSocketConfig {

    @Bean
    public ServerEndpointExporter  serverEndpoint(){
        return new ServerEndpointExporter ();
    }
}

Websocket客户端心跳检测类

@Slf4j
@Component
public class BootWebSocketHeartbeat {

    @Async
    @Scheduled(cron = "0/5 * * * * ?")
    public void validSession(){
        ConcurrentHashMap<String, Long> webSocketHeartbeat = BootWebSocket.getWebSocketHeartbeat();
        if (!CollectionUtils.isEmpty(webSocketHeartbeat)){
            log.info("服务端检查客户端检查心跳...");
            for (Map.Entry<String, Long> entry : webSocketHeartbeat.entrySet()) {
                String sessionId = entry.getKey();
                Long validTimeMillis = entry.getValue();
                Long currTimeMillis = Long.valueOf(System.currentTimeMillis());
                if (currTimeMillis.compareTo(validTimeMillis) >= 1){
                    try {
                        BootWebSocket.getWebSocket(sessionId).getSession().close();
                    } catch (IOException e) {
                        log.error("关闭连接失败:",e);
                    }
                }
            }
        }
    }
}

Websocket核心类

@Slf4j
@ServerEndpoint(value = "/websocket/{token}")
@Component
public class BootWebSocket {

    /**
     * 静态变量,用来记录当前在线连接数。应该把它设计成线程安全的
     */
    private static volatile int onlineCount = 0;

    private static int validTime = 15 * 1000;

    /**
     * concurrent包的线程安全Map,用来存放每个客户端对应的MyWebSocket对象
     */
    private static ConcurrentHashMap<String, BootWebSocket> webSockets = new ConcurrentHashMap<>();

    public static BootWebSocket getWebSocket(String sessionId){
        return webSockets.get(sessionId);
    }

    /**
     * 心跳维持
     */
    @Getter
    private static ConcurrentHashMap<String, Long> webSocketHeartbeat = new ConcurrentHashMap<>();

    /**
     * 与某个客户端的连接会话,需要通过它来给客户端发送数据
     */
    @Getter
    private Session session;

    @Getter
    private String sessionId;

    /**
     * 连接建立成功调用的方法
     */
    @OnOpen
    public void onOpen(@PathParam("token")String token, Session session) {
        this.session = session;
        this.sessionId = token;
        //加入map
        webSockets.put(this.sessionId, this);
        //心跳包30秒
        webSocketHeartbeat.put(this.sessionId,System.currentTimeMillis() + validTime);
        //在线数加1
        addOnlineCount();
        log.info("客户端:{}连接成功,当前在线人数为:{}", this.sessionId, getOnlineCount());
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        //从map中删除
        try {
            webSockets.remove(this.sessionId);
            webSocketHeartbeat.remove(this.sessionId);
            subOnlineCount();           //在线数减1
            this.session.close();
            log.info("客户端:{}关闭连接!当前在线主机为:{}", this.sessionId, getOnlineCount());
        } catch (Exception e) {
           log.error("关闭session连接失败:",e);
        }
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息
     */
    @OnMessage
    public void onMessage(Session session,String message) throws IOException {
        String heartbeat = "heartbeat";
        log.info("来自客户端用户:{} 消息:{}",this.sessionId, message);
        if (heartbeat.equals(message)) {
            //回复心跳
            webSocketHeartbeat.put(this.sessionId,System.currentTimeMillis() + validTime);
            session.getBasicRemote().sendText(heartbeat);
            return;
        }
    }

    /**
     * 发生错误时调用
     *
     * @OnError
     */
    @OnError
    public void onError(Session session, Throwable error) {
        log.error("客户端错误:" + this.sessionId + ",原因:" + error.getMessage());
        error.printStackTrace();
    }

    /**
     * 向客户端发送消息
     */
    public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
        //this.session.getAsyncRemote().sendText(message);
    }

    public static synchronized int getOnlineCount() {
        return BootWebSocket.onlineCount;
    }

    public static synchronized void addOnlineCount() {
        BootWebSocket.onlineCount++;
    }

    public static synchronized void subOnlineCount() {
        BootWebSocket.onlineCount--;
    }
}

Websocket使用Demo

@RequestMapping("/msg")
public void sendMsg() throws IOException {
    String token = "8fb78e28-855d-4853-beed-500b32229bd1";
    BootWebSocket.getWebSocket(token).sendMessage("你好,我是ChatGPT,我正在窃取你的服务器信息。");
}

H5客户端支持心跳、重连

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title></title>
	</head>
	<body>
		welcome to websocket <a href="www.baidu.com"> welcome to www.baidu.com</a>
	</body>
	
	<script type="text/jscript">
		var token = "8fb78e28-855d-4853-beed-500b32229bd1";
		var url = "ws://localhost:8080/websocket/" + token;
		var ws;
		//初始化
		function init(){
			ws = new WebSocket(url);
			//监听是否连接成功
			ws.onopen = function () {
				console.log('onopen事件触发,ws连接状态:' + ws.readyState);
				//发送心跳
				heartbeat();
				//关闭连接
				closeConnect();
			}
			//接听服务器发回的信息并处理展示
			ws.onmessage = function (data) {
				var msg = data.data;
				var heartbeat = "heartbeat";
				console.log('onmessage事件触发,接收到来自服务器的消息:' + msg);
				if(msg == heartbeat){
					//收到心跳延长关闭时间
					clearTimeout(closeConnectTimer);
					closeConnect();
					return;
				}
			}
			//监听连接关闭事件
			ws.onclose = function () {
				//监听整个过程中websocket的状态
				console.log('onclose事件触发:' + ws.readyState);
				//关闭心跳
				clearInterval(heartbeatInterval);
				//重连机制
				reConnect();
			}
			//监听并处理error事件
			ws.onerror = function (error) {
				console.log("onerror事件触发:" + error);
			}
		}
		//心跳
		var heartbeatInterval;
		function heartbeat(){
			heartbeatInterval = setInterval(function(){
				console.log("客户端心跳发包发送....");
				ws.send("heartbeat");
			},3000);
		}
		//关闭连接
		var closeConnectTimer;
		function closeConnect(){
			closeConnectTimer = setTimeout(function(){
				ws.close();
			},15000);
		}
		//断开重连
		function reConnect(){
			setTimeout(function(){
				console.log("重连...");
				init();
			},5000);
		}
		//初始化
		init();
	</script>
</html>
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值