您的位置:首页 > 其它

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

2017-06-20 09:58 429 查看
 线程安全的队列来保持所有客户端的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();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

上述代码感觉上好像没问题。Session信息是保存在线程安全的集合,又通过volatile变量来修饰保证了内存可见性,但实际运行时却发现并没有想象的那么好。当客户端断开链接,时服务器需要发送消息给客户端时.服务端抛出异常:
IllegalStateException: The WebSocket session [0] has been closed and no method (apart from close()) may be called on a closed session
1
1

不难看出,是服务端在关闭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);
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

完美解决 

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

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

CopyOnWrite容器即写时复制的容器。通俗的理解是当我们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行Copy,复制出一个新的容器,然后新的容器里添加元素,添加完元素之后,再将原容器的引用指向新的容器。这样做的好处是我们可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加任何元素。所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐