springboot webscoket示例:增加定时心跳逻辑

websocket服务端增加定时发送心跳机制

@ServerEndpoint(value = "/websocket/{uuid}")
@Component
public class DevMessageHandleController {

    private static final Logger logger = LoggerFactory.getLogger(DevMessageHandleController.class);

    //concurrent包的线程安全Set,用来存放每个客户端对应的WebSocketController对象。
    public static CopyOnWriteArraySet<DevMessageHandleController> webSocketSet = new CopyOnWriteArraySet<>();

    // 使用ConcurrentHashMap来存储用户ID和WebSocket会话对象的映射。
    private static ConcurrentHashMap<String, DevMessageHandleController> webSocketMap = new ConcurrentHashMap<>();

    //与某个客户端的连接会话,需要通过它来给客户端发送数据
    private Session session;
    private String uuid;
    // 心跳尝试次数
    private AtomicInteger heartbeatAttempts;

    @OnOpen
    public void onOpen(@PathParam("uuid") String uuid, Session session) {
        logger.info("uuid: {}, sessionId: {}", uuid, session.getId());
        // 将新建立的会话添加到webSocketMap中
        try {
            if (webSocketMap.containsKey(uuid)) {
                //如果有旧连接就先断开
                webSocketMap.get(uuid).session.close();
                webSocketSet.remove(webSocketMap.get(uuid));
            }
            this.session = session;
            this.uuid = uuid;
            heartbeatAttempts = new AtomicInteger(0);
            webSocketSet.add(this); //加入set中
            webSocketMap.put(uuid, this); //加入map中
            // 其他代码...
        } catch (Exception e) {
            logger.error("onOpen error:" + e.getMessage());
        }
    }

    @OnClose
    public void onClose(@PathParam("uuid") String uuid, Session session) {
        logger.info("会话关闭");
        // 当会话关闭时,从webSocketSet中移除该会话
        webSocketSet.remove(this);
        // 当会话关闭时,从webSocketMap中移除该会话
        webSocketMap.remove(uuid);
    }

    // 收到客户端消息后调用的方法
    @OnMessage
    public void onMessage(String message, Session session) {
        logger.info("Message from client: " + message);
        if ("pong".equals(message)) {
            // 重置为0
            this.heartbeatAttempts.set(0);
            System.out.println("Received pong from: " + session.getId());
        }
    }

    // 发生错误时调用
    @OnError
    public void onError(Session session, Throwable error) {
        logger.error("发生错误 session:" + session.getId() + ",error:" + error);
        try {
            session.close();
            // 当会话关闭时,从webSocketSet中移除该会话
            webSocketSet.remove(this);
            // 当会话关闭时,从webSocketMap中移除该会话
            webSocketMap.remove(this.uuid);
        } catch (IOException e) {
            logger.error("onError error:" + e.getMessage());
        }
    }

    public void sendMessage(Session session, String msg) {
        logger.info("发送消息");
        try {
            //判断当前人员是否连接websocket
            if (session.isOpen()) {
                session.getAsyncRemote().sendText(msg);
            } else {
                session.close();
                webSocketSet.remove(this); //从set中删除
                webSocketMap.remove(this.uuid); //从map中删除
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static CopyOnWriteArraySet<DevMessageHandleController> getWebSocketSet() {
        return webSocketSet;
    }

    public static void setWebSocketSet(CopyOnWriteArraySet<DevMessageHandleController> webSocketSet) {
        DevMessageHandleController.webSocketSet = webSocketSet;
    }

    public static ConcurrentHashMap<String, DevMessageHandleController> getWebSocketMap() {
        return webSocketMap;
    }

    public static void setWebSocketMap(ConcurrentHashMap<String, DevMessageHandleController> webSocketMap) {
        DevMessageHandleController.webSocketMap = webSocketMap;
    }

    public Session getSession() {
        return session;
    }

    public void setSession(Session session) {
        this.session = session;
    }

    public String getUuid() {
        return uuid;
    }

    public void setUuid(String uuid) {
        this.uuid = uuid;
    }

    public AtomicInteger getHeartbeatAttempts() {
        return heartbeatAttempts;
    }

    public void setHeartbeatAttempts(AtomicInteger heartbeatAttempts) {
        this.heartbeatAttempts = heartbeatAttempts;
    }
}

每间隔10s向客户端发送一次心跳

    private static final int MAX_HEARTBEAT_ATTEMPTS = 3;

	@Scheduled(fixedDelay = 10000)
    public void sendHeartBeat() {
        CopyOnWriteArraySet<DevMessageHandleController> webSocketSet;
        try {
            webSocketSet = DevMessageHandleController.getWebSocketSet();
            logger.info("连接数量:" + webSocketSet.size());
            if(webSocketSet.size() == 0){
                return;
            }
            logger.info("定时发送心跳");
            // 遍历所有连接,发送心跳
            webSocketSet.forEach(obj -> {
                Session session = obj.getSession();
                logger.info("sessionId:" + session.getId() +" 心跳ping发送次数:" + obj.getHeartbeatAttempts().get());
                if(obj.getHeartbeatAttempts().get() >= MAX_HEARTBEAT_ATTEMPTS) {
                    try {
                        session.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                        logger.error("session close error:" + e.getMessage());
                    }
                } else {
                    obj.getHeartbeatAttempts().incrementAndGet();
                    if (session.isOpen()) {
                        session.getAsyncRemote().sendText("ping");
                    }
                }
            });
        }catch (Exception e){
            logger.error("发送心跳 error:" + e.getMessage());
        }
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/602044.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

目标检测CNN 目标检测发展历程 应用场景 智慧交通 自动驾驶 工业生产 智慧医疗

目标检测 目标检测是计算机视觉领域中的一个重要任务,其主要目的是让计算机能够自动识别图像或视频帧中所有目标的类别,并在目标周围绘制边界框以标示出每个目标的位置。 目标检测的过程通常包括两个主要步骤:目标定位和目标分类。目标定位是确定图像中是否存在感兴趣的目…

【功耗问题排查】

一、如何处理具体功耗case 在手机功耗测试中&#xff0c;因为我们在功耗测试中&#xff08;电源电压&#xff09;为固定值&#xff08;老手机一般为3.8V左右&#xff0c;现在的大多项目采用4V左右&#xff09;&#xff0c;那么的大小直接由决定&#xff0c;所以&#xff0c;在沟…

在线音视频下载

https://cobalt.tools/ 支持 bilibili 等网站

顺序表的实现(迈入数据结构的大门)(1)

上一节我们认识到了什么是数据结构 这一节我们就来实现第一个数据结构的实现 思考一个问题&#xff1a; 假定一个数组&#xff0c;空间为10&#xff0c;已经使用了5个&#xff0c;向其中插入数据的步骤&#xff1a; 1.插入数据&#xff0c;我们先要求数组长度&#xff0c;其…

做抖音小店怎么选品?这几种实用性选品方式,新手一看就会

大家好&#xff0c;我是电商笨笨熊 做抖音小店&#xff0c;最重要的是选品&#xff0c;最让玩家头疼的还是选品。 选品该怎么选才能选中爆品&#xff0c;怎么做才能让店铺爆单&#xff1f; 笨笨熊做抖店已经四年多的时间&#xff0c;因此也总结出来一套最适合新手玩家去做的…

Stable Diffusion 指定模型,Lora 训练全流程

简介 在使用 Stable Diffusion 的时候&#xff0c;可以选择别人训练好的 Lora&#xff0c;那么如何训练自己的 Lora呢&#xff1f; 本篇文章介绍了如何训练Lora&#xff0c;如何筛选模型&#xff0c;如何在 Stable Diffusion 中使用。 闲话不多说&#xff0c;直接实际操作吧。…

【EI会议|投稿优惠】2024年物理化学与应用数学国际会议(IACPCAM 2024)

2024 International Conference on Physical Chemistry and Applied Mathematics 一、大会信息 会议名称&#xff1a;2024年物理化学与应用数学国际会议会议简称&#xff1a;IACPCAM 2024收录检索&#xff1a;提交Ei Compendex,CPCI,CNKI,Google Scholar等会议官网&#xff1a;…

Debian——安装syzkaller——2024

系统:Debian 远程连接——我是不想安装tools没有办法复制黏贴,所以远程,根据个人情况选择是否远程连接 就是说使用Windows自带的远程mstsc,使用的不是ssh22端口,是TCP 3389端口 mkdir debian cd debian 二:安装go编译器 打开终端。使用wget命令从官方网站或可信的镜像…

SAP-ABAP-视图

1、什么是视图&#xff1f; 当需要查询多个表中的某些字段的数据时&#xff0c;就可以使用视图。视图不影响数据库中的数据&#xff0c;仅作为查询手段或工具。 2、视图类型&#xff1a; 数据库视图和维护视图经常使用。 3、创建视图SE11 3.1、数据库视图 可以直接输入表名…

js实现json数据可编辑

背景 项目中有低代码平台&#xff0c;由于历史脏数据和非同步编辑的问题&#xff0c;偶尔会出现数据错乱的问题&#xff0c;希望有一个快捷的方式修改数据 之前在用Formily的时候有注意到designable/react 里面的json数据编辑功能非常不错如果能应用到项目里就完美了 design…

UE灯光:点光和聚光灯的强度单位(cd、lm)

在虚幻引擎&#xff08;UE&#xff09;中&#xff0c;点光和聚光灯的光强使用两种不同的单位进行度量&#xff1a; 坎德拉&#xff08;cd&#xff09;&#xff1a;坎德拉是光强度的国际单位&#xff08;SI单位&#xff09;。它代表光源在特定方向上每单位立体角发出的光通量。…

Chromium编译指南2024 Windows11篇-获取 Chromium 的源代码(五)

前言 在《Chromium编译指南2024&#xff08;四&#xff09;》中&#xff0c;我们完成了Git 的初始化配置。 现在&#xff0c;我们将进一步讨论如何获取 Chromium 的源代码&#xff0c;并准备构建所需的文件。 1. 获取Chromium的源代码 在合适的位置准备一个文件夹&#xff…

47. UE5 RPG 实现角色死亡效果

在上一篇文章中&#xff0c;我们实现了敌人受到攻击后会播放受击动画&#xff0c;并且还给角色设置了受击标签。并在角色受击时&#xff0c;在角色身上挂上受击标签&#xff0c;在c里&#xff0c;如果挂载了此标签&#xff0c;速度将降为0 。 受击有了&#xff0c;接下来我们将…

PDF批量编辑技巧:高效PDF转txt批量处理,轻松管理大量文档

随着信息技术的飞速发展&#xff0c;文档管理已成为日常工作中不可或缺的一部分。特别是当我们需要处理大量的PDF文件时&#xff0c;如何高效地进行编辑、转换和管理成为了一个重要的问题。本文将介绍一些PDF批量编辑的技巧&#xff0c;特别是如何将PDF批量转换为txt格式&#…

C语言——文件描述符、系统调用操作文件

文件描述符 在Unix-like操作系统中&#xff0c;文件描述符&#xff08;file descriptor&#xff09;是一个用于标识打开文件或I/O设备的整数值。它是对底层文件系统的抽象&#xff0c;用于在应用程序和操作系统之间传递文件信息。 文件描述符是一个非负整数&#xff0c;通常是…

【MsSQL】数据库基础 库的基本操作

目录 一&#xff0c;数据库基础 1&#xff0c;什么是数据库 2&#xff0c;主流的数据库 3&#xff0c;连接服务器 4&#xff0c;服务器&#xff0c;数据库&#xff0c;表关系 5&#xff0c;使用案例 二&#xff0c;库的操作 1&#xff0c;创建数据库 2&#xff0c;创建…

抖音小店是什么?为什么要去做呢?这几点原因告诉你真相!

大家好&#xff0c;我是电商小V 抖音小店就是抖音平台进军电商行业的踏板&#xff0c;也是抖音内的电商购物业务&#xff0c;咱们就可以理解成可以在抖音平台上面卖货&#xff0c;和淘宝&#xff0c;多多店铺&#xff0c;线下超市都是一个性质的&#xff0c;但是运营的模式不同…

虚拟机镜像文件qcow2格式转vmdk

一、在esxi上虚拟机导出qcow2镜像文件 1、卸载数据盘、网卡 2、登录虚拟机所在物理服务器&#xff0c;查找系统盘名为vm-101-disk-0的文件位置 find / -name "vm-101-disk-0"使用命令导出qcow2镜像&#xff08;进度条走完就完成了&#xff09;&#xff1a; qemu…

ROS服务器通信

目录 一、角色 二、流程 注意 三、例子描述 四、srv文件 编译配置文件 vscode配置 五、Server.cpp编写例子 编写CMakeList 六、观察server的效果 七、Client编写例子 编写CMakeList 八、观察Client的结果 九、Client优化&#xff08;动态输入&#xff09; 了解argc…

linux之ssh

SSH远程连接协议 SSH远程管理 定义 SSH&#xff08;Secure Shell &#xff09;是一种安全通道协议&#xff0c;主要用来实现字符界面的远程的登录、远程复制等功能。 SSH协议对通信双方的数据传输进行了加密处理&#xff0c;其中包括用户登录时输入的用户口令。因此SSH协议具…