阿里云-云小站(无限量代金券发放中)
【腾讯云】云服务器、云数据库、COS、CDN、短信等热卖云产品特惠抢购

完美解决WebSocket 服务器 The WebSocket session [0] has been closed and no method…异常信息

686次阅读
没有评论

共计 2599 个字符,预计需要花费 7 分钟才能阅读完成。

最近项目需要 web 客户端与服务器保持长链接的场景并需要服务器向所有链接的客户端推送消息,于是自然使用了 WebSocket 技术,自然要考虑到服务器于多个客户端线程安全的问题。于是乎,想当然的在 WebSocket 服务器端通过一个线程安全的队列来保持所有客户端的 Session.

private volatile static List<Session> sessions = Collections.synchronizedList(new ArrayList());
private  Session session;

    /*
     * 客户端链接成功后讲其保存在线程安全的集合中
     */
    @OnOpen
    public void onOpen(Session session) throws IOException {this.session = session;
        sessions.add(this);
    }
 /*
     * 客户端断开链接后将其从线程安全的集合中移除
     */
@OnClose
    public void onClose() {sessions.remove(this);
    }
    // 给所有客户端发送消息
public static void sendMessage(String clientInfoJson) {try {if (sessions.size() != 0) {for (session s : sessions) {if (s != null) {s.getBasicRemote().sendText(clientInfoJson);
                    }
                }
            }
        } catch (IOException e) {// TODO Auto-generated catch block
            e.printStackTrace();}
    }

上述代码感觉上好像没问题。Session 信息是保存在线程安全的集合,又通过 volatile 变量来修饰保证了内存可见性,但实际运行时却发现并没有想象的那么好。当客户端断开链接,时服务器需要发送消息给客户端时. 服务端抛出异常:

IllegalStateException: The WebSocket session [0] has been closed and no method (apart from close()) may be called on a closed session

不难看出, 是服务端在关闭 Session 即将 Session 从线程安全的队列移除时,在发送消息的方法里应该被移除的 Session 消息却进入了发送消息的环节,在执行 getBasicRemote().sendText(clientInfoJson); 操作时发生了异常。

解决方法:

Google 了大量资料后发现如果要解决这种线程安全的问题, 不能通过线程安全的集合来保存 Session 解决。而应该保存整个类,并通过 CopyOnWriteArraySet 容器来操作。

@ServerEndpoint("/getLocation")
@Component
public class TransmissionLocationWebSocket {

    @Autowired
    public TerminalService terminalServiceInWebSocket;

    private static CopyOnWriteArraySet<TransmissionLocationWebSocket> sessions = new CopyOnWriteArraySet<TransmissionLocationWebSocket>();
    /*
     * 线程不安全
     */
    //private volatile static List<Session> sessions = Collections.synchronizedList(new ArrayList());
    private  Session session;

    /*
     * 链接成功后的回掉
     */
    @OnOpen
    public void onOpen(Session session) throws IOException {System.out.println("链接成功");
        this.session = session;
        sessions.add(this);
    }

    public static void sendUserLocal(String clientInfoJson,) {try {if (sessions.size() != 0) {for (TransmissionLocationWebSocket s : sessions) {if (s != null) {// 判断是否为终端信息。如果是终端信息则查询数据库获取 detail
                            s.session.getBasicRemote().sendText(clientInfoJson);
                     }
            }
        }
        } catch (IOException e) {// TODO Auto-generated catch block
            e.printStackTrace();}
    }

    @OnClose
    public void onClose() {System.out.println("设置离线");
        sessions.remove(this);
    }

}

完美解决
备注:虽然我上面贴出来的代码是在 COW 中保存了整个类,但我测试的时候发生,保存 Session 也是可以的。

Copy-On-Write 简称 COW,是一种用于程序设计中的优化策略。其基本思路是,从一开始大家都在共享同一个内容,当某个人想要修改这个内容的时候,才会真正把内容 Copy 出去形成一个新的内容然后再改,这是一种延时懒惰策略。从 JDK1.5 开始 Java 并发包里提供了两个使用 CopyOnWrite 机制实现的并发容器, 它们是 CopyOnWriteArrayList 和 CopyOnWriteArraySet。CopyOnWrite 容器非常有用,可以在非常多的并发场景中使用到。

CopyOnWrite 容器即写时复制的容器。通俗的理解是当我们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行 Copy,复制出一个新的容器,然后新的容器里添加元素,添加完元素之后,再将原容器的引用指向新的容器。这样做的好处是我们可以对 CopyOnWrite 容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。所以 CopyOnWrite 容器也是一种读写分离的思想,读和写不同的容器。

本文永久更新链接地址:http://www.linuxidc.com/Linux/2016-10/136356.htm

正文完
星哥玩云-微信公众号
post-qrcode
 0
星锅
版权声明:本站原创文章,由 星锅 于2022-01-21发表,共计2599字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
【腾讯云】推广者专属福利,新客户无门槛领取总价值高达2860元代金券,每种代金券限量500张,先到先得。
阿里云-最新活动爆款每日限量供应
评论(没有评论)
验证码
【腾讯云】云服务器、云数据库、COS、CDN、短信等云产品特惠热卖中

星哥玩云

星哥玩云
星哥玩云
分享互联网知识
用户数
4
文章数
19351
评论数
4
阅读量
7998142
文章搜索
热门文章
星哥带你玩飞牛NAS-6:抖音视频同步工具,视频下载自动下载保存

星哥带你玩飞牛NAS-6:抖音视频同步工具,视频下载自动下载保存

星哥带你玩飞牛 NAS-6:抖音视频同步工具,视频下载自动下载保存 前言 各位玩 NAS 的朋友好,我是星哥!...
星哥带你玩飞牛NAS-3:安装飞牛NAS后的很有必要的操作

星哥带你玩飞牛NAS-3:安装飞牛NAS后的很有必要的操作

星哥带你玩飞牛 NAS-3:安装飞牛 NAS 后的很有必要的操作 前言 如果你已经有了飞牛 NAS 系统,之前...
我把用了20年的360安全卫士卸载了

我把用了20年的360安全卫士卸载了

我把用了 20 年的 360 安全卫士卸载了 是的,正如标题你看到的。 原因 偷摸安装自家的软件 莫名其妙安装...
再见zabbix!轻量级自建服务器监控神器在Linux 的完整部署指南

再见zabbix!轻量级自建服务器监控神器在Linux 的完整部署指南

再见 zabbix!轻量级自建服务器监控神器在 Linux 的完整部署指南 在日常运维中,服务器监控是绕不开的...
飞牛NAS中安装Navidrome音乐文件中文标签乱码问题解决、安装FntermX终端

飞牛NAS中安装Navidrome音乐文件中文标签乱码问题解决、安装FntermX终端

飞牛 NAS 中安装 Navidrome 音乐文件中文标签乱码问题解决、安装 FntermX 终端 问题背景 ...
阿里云CDN
阿里云CDN-提高用户访问的响应速度和成功率
随机文章
我把用了20年的360安全卫士卸载了

我把用了20年的360安全卫士卸载了

我把用了 20 年的 360 安全卫士卸载了 是的,正如标题你看到的。 原因 偷摸安装自家的软件 莫名其妙安装...
让微信公众号成为 AI 智能体:从内容沉淀到智能问答的一次升级

让微信公众号成为 AI 智能体:从内容沉淀到智能问答的一次升级

让微信公众号成为 AI 智能体:从内容沉淀到智能问答的一次升级 大家好,我是星哥,之前写了一篇文章 自己手撸一...
240 元左右!五盘位 NAS主机,7 代U硬解4K稳如狗,拓展性碾压同价位

240 元左右!五盘位 NAS主机,7 代U硬解4K稳如狗,拓展性碾压同价位

  240 元左右!五盘位 NAS 主机,7 代 U 硬解 4K 稳如狗,拓展性碾压同价位 在 NA...
如何免费使用强大的Nano Banana Pro?附赠邪修的用法

如何免费使用强大的Nano Banana Pro?附赠邪修的用法

如何免费使用强大的 Nano Banana Pro?附赠邪修的用法 前言 大家好,我是星哥,今天来介绍谷歌的 ...
CSDN,你是老太太喝粥——无齿下流!

CSDN,你是老太太喝粥——无齿下流!

CSDN,你是老太太喝粥——无齿下流! 大家好,我是星哥,今天才思枯竭,不写技术文章了!来吐槽一下 CSDN。...

免费图片视频管理工具让灵感库告别混乱

一言一句话
-「
手气不错
开发者福利:免费 .frii.site 子域名,一分钟申请即用

开发者福利:免费 .frii.site 子域名,一分钟申请即用

  开发者福利:免费 .frii.site 子域名,一分钟申请即用 前言 在学习 Web 开发、部署...
浏览器自动化工具!开源 AI 浏览器助手让你效率翻倍

浏览器自动化工具!开源 AI 浏览器助手让你效率翻倍

浏览器自动化工具!开源 AI 浏览器助手让你效率翻倍 前言 在 AI 自动化快速发展的当下,浏览器早已不再只是...
每年0.99刀,拿下你的第一个顶级域名,详细注册使用

每年0.99刀,拿下你的第一个顶级域名,详细注册使用

每年 0.99 刀,拿下你的第一个顶级域名,详细注册使用 前言 作为长期折腾云服务、域名建站的老玩家,星哥一直...
手把手教你,购买云服务器并且安装宝塔面板

手把手教你,购买云服务器并且安装宝塔面板

手把手教你,购买云服务器并且安装宝塔面板 前言 大家好,我是星哥。星哥发现很多新手刚接触服务器时,都会被“选购...
星哥带你玩飞牛NAS-13:自动追番、订阅下载 + 刮削,动漫党彻底解放双手!

星哥带你玩飞牛NAS-13:自动追番、订阅下载 + 刮削,动漫党彻底解放双手!

星哥带你玩飞牛 NAS-13:自动追番、订阅下载 + 刮削,动漫党彻底解放双手! 作为动漫爱好者,你是否还在为...