目的:
一个用户下建立多个连接,例如:一个用户开启多个网页,每个网页都建立一个socket连接。
最终表现:
用户的多个页面,能收到统一的消息:
具体使用方式:
项目地址:
https://gitee.com/brinjaul/fjpdemo.git
前端
客户端页面使用 http://www.websocket-test.com/ 与项目建立连接,充当web客户端
注意下图的红框中是我们服务段地址,其中 /websocket/fjp 是我们服务端注解里的
@ServerEndpoint("/websocket/{userId}")
{userId} 类似于SpringMVC中的 @PathVariable(“param”) 的使用方式,即,restful风格形式的动态传传参
或者使用项目里配置的,WebSocketTest.html,与服务端页面交互。
后端:
代码:
package com.fjp.websocket;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
/**
* @ServerEndpoint 注解是一个类层次的注解,它的功能主要是将目前的类定义成一个websocket服务器端,
* 注解的值将被用于监听用户连接的终端访问URL地址,客户端可以通过这个URL来连接到WebSocket服务器端
*/
@ServerEndpoint("/websocket/{userId}")
public class WebSocketTest {
private static ConcurrentHashMap<String, CopyOnWriteArraySet<WebSocketTest>> userwebSocketMap = new ConcurrentHashMap<String, CopyOnWriteArraySet<WebSocketTest>>();
private static ConcurrentHashMap<String, Integer> count = new ConcurrentHashMap<String, Integer>();
private String userId;
/*
* 与某个客户端的连接会话,需要通过它来给客户端发送数据
*/
private Session session;
/**
* 连接建立成功调用的方法
*
* @param session 可选的参数。session为与某个客户端的连接会话,需要通过它来给客户端发送数据
*/
@OnOpen
public void onOpen(Session session, @PathParam("userId") final String userId) {
this.session = session;
this.userId = userId;
if (!exitUser(userId)) {
initUserInfo(userId);
} else {
CopyOnWriteArraySet<WebSocketTest> webSocketTestSet = getUserSocketSet(userId);
webSocketTestSet.add(this);
userCountIncrease(userId);
}
System.out.println("有" + userId + "新连接加入!当前在线人数为" + getCurrUserCount(userId));
}
/**
* 连接关闭调用的方法
*/
@OnClose
public void onClose() {
CopyOnWriteArraySet<WebSocketTest> webSocketTestSet = userwebSocketMap.get(userId);
//从set中删除
webSocketTestSet.remove(this);
//在线数减1
userCountDecrement(userId);
System.out.println("有一连接关闭!当前在线人数为" + getCurrUserCount(userId));
}
/**
* 收到客户端消息后调用的方法
*
* @param message 客户端发送过来的消息
* @param session 可选的参数
*/
@OnMessage
public void onMessage(String message, Session session) {
CopyOnWriteArraySet<WebSocketTest> webSocketSet = userwebSocketMap.get(userId);
System.out.println("来自客户端" + userId + "的消息:" + message);
//群发消息
for (WebSocketTest item : webSocketSet) {
try {
item.sendMessage(message);
} catch (IOException e) {
e.printStackTrace();
continue;
}
}
}
/**
* 发生错误时调用
*
* @param session
* @param error
*/
@OnError
public void onError(Session session, Throwable error) {
System.out.println("发生错误");
error.printStackTrace();
}
/**
* 这个方法与上面几个方法不一样。没有用注解,是根据自己需要添加的方法。
*
* @param message
* @throws IOException
*/
public void sendMessage(String message) throws IOException {
this.session.getBasicRemote().sendText(message);
//this.session.getAsyncRemote().sendText(message);
}
public boolean exitUser(String userId) {
return userwebSocketMap.containsKey(userId);
}
public CopyOnWriteArraySet<WebSocketTest> getUserSocketSet(String userId) {
return userwebSocketMap.get(userId);
}
public void userCountIncrease(String userId) {
if (count.containsKey(userId)) {
count.put(userId, count.get(userId) + 1);
}
}
public void userCountDecrement(String userId) {
if (count.containsKey(userId)) {
count.put(userId, count.get(userId) - 1);
}
}
public void removeUserConunt(String userId) {
count.remove(userId);
}
public Integer getCurrUserCount(String userId) {
return count.get(userId);
}
private void initUserInfo(String userId) {
CopyOnWriteArraySet<WebSocketTest> webSocketTestSet = new CopyOnWriteArraySet<WebSocketTest>();
webSocketTestSet.add(this);
userwebSocketMap.put(userId, webSocketTestSet);
count.put(userId, 1);
}
}
WebSocketTest 类相当于Controller,@ServerEndpoint(“/websocket/{userId}”) 访问的
核心思想,使用rest 风格动态变化用户id,每个用户建立一次连接的session要保存到容器中,保证每个用户享有自己的session记录表。
后端 推送给前端的数据 ,我们用一个页面来实现,WebSocketTest.html ,即服务端要发送的数据通过这个页面输入,然后 端收到后 发给 客户端 http://www.websocket-test.com/ 页面
动态效果图: 请点击
https://gitee.com/brinjaul/fjpdemo/blob/master/JavaWebSocket/doc/%E6%A8%A1%E7%B3%8A%E7%9A%84%E5%8A%9F%E8%83%BD%E6%BC%94%E7%A4%BA%E5%8A%A8%E5%9B%BE.gif
上传不了那么大的图片,就保留在码云了
项目地址:
https://gitee.com/brinjaul/fjpdemo.git