必要なjarパッケージの依存関係
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-websocket -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
<version>2.0.4.RELEASE</version>
</dependency>
WebSocket サービス インターフェイスの Java 部分
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import com.alibaba.fastjson.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
/**
* WebSocket服务接口
*
* @author wzw
* @date 2022/08/23
*/
@ServerEndpoint(value = "/websocket/{userId}")
@Component
public class WebSocketServer {
private final static Logger log = LoggerFactory.getLogger(WebSocketServer.class);
/**
* 存储登录用户的 sid 以及 session
*/
private static ConcurrentHashMap<String, Session> connections = new ConcurrentHashMap<>(32);
/**
* 是WebSocket的Session
*/
private Session session;
/**
* 统计在线人数
*/
private static AtomicInteger onlineCount = new AtomicInteger();
/**
* 事件 --登录的人当你登录之后建立连接,此方法便会执行
* @param userId
* @param session
*/
@OnOpen
public void onopen(@PathParam("userId") String userId, Session session) {
this.session = session;
System.out.println("seesionId为" + session.getId());
if (connections.containsKey(userId)) {
connections.remove(userId);
connections.put(userId, session);
}else {
onlineCount.incrementAndGet();
connections.put(userId, session);
System.out.println("用户:"+userId+"-"+session.getId()+"上线了-"+session);
System.out.println("在线人数:"+onlineCount);
}
}
@OnClose
public void onClose(Session session){
for (String userId : connections.keySet()) {
if(connections.get(userId).equals(session)){
System.out.println("用户:"+userId+"-关闭-"+session);
connections.remove(session);
onlineCount.decrementAndGet(); // 在线数减1
}
}
}
@OnMessage
public void onMessage(String msg, Session session) {
System.out.println("服务端收到客户端"+session.getId()+"的消息:"+msg);
// 客户端向服务端发送消息,然后再推送给其他的用户,可以在这里进行设置
try {
sendMessage(msg, session);
} catch (IOException e) {
e.printStackTrace();
} catch (EncodeException e) {
e.printStackTrace();
}
}
@OnError
public void onError(Session session, Throwable error) {
System.out.println("发生错误");
error.printStackTrace();
}
//推送
public static void sendMessage(String msg, Session session) throws IOException, EncodeException {
// for (Session session1 : webSocketSet) {
// if (session != session1)
// session1.getBasicRemote().sendText(msg);
// }
System.out.println("推送:"+msg);
session.getBasicRemote().sendText(msg);
}
// 推送给指定的用户群
public void sendMsgToUsers(List<String> ids,Map<String, Object> map) {
ids.stream().forEach(s -> {
System.out.println("用户:"+s+"是否能够推送:"+connections.containsKey(s));
if (connections.containsKey(s)) {
if (connections.get(s).isOpen()){
try {
sendMessage(new JSONObject(map).toString(),connections.get(s));
} catch (IOException e) {
throw new RuntimeException(e);
} catch (EncodeException e) {
e.printStackTrace();
}
}
}
});
}
// 推送给全部在线用户
public static void sendMsgToAll(Map<String, Object> map){
connections.keySet().stream().forEach(s->{
if (connections.get(s).isOpen()){
try {
sendMessage(String.valueOf(map),connections.get(s));
} catch (IOException e) {
throw new RuntimeException(e);
} catch (EncodeException e) {
e.printStackTrace();
}
}
});
}
}
ユーザー登録、メッセージ接続用の Vue フォアグラウンド
initWebSocket() {
// 初始化weosocket
// 这里的wsuri是根据服务端的ip进行配置(开发环境),生产环境的话可以使用上面的Ip或者是nginx做代理
const wsuri = `ws://127.0.0.1:8099/websocket/001`; // 协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。
this.websocket = new WebSocket(wsuri);
this.websocket.onopen = this.websocketonopen;
this.websocket.onerror = this.websocketonerror;
this.websocket.onmessage = this.setOnmessageMessage;
this.websocket.onclose = this.websocketclose;
},
start() {
// 清除延时器
this.timeoutObj && clearTimeout(this.timeoutObj);
this.serverTimeoutObj && clearTimeout(this.serverTimeoutObj);
this.timeoutObj = setTimeout(() => {
if (this.websocket && this.websocket.readyState == 1) {
// this.websocket.send('检测心跳是否正常');//发送消息,服务端返回信息,即表示连接良好,可以在socket的onmessage事件重置心跳机制函数
} else {
this.reconnect();
}
// 定义一个延时器等待服务器响应,若超时,则关闭连接,重新请求server建立socket连接
// this.serverTimeoutObj = setTimeout(() => {
// this.websocket.close();
// }, this.timeout)
}, this.timeout)
},
resetSocket() { // 重置心跳
// 清除时间
clearTimeout(this.timeoutObj);
clearTimeout(this.serverTimeoutObj);
// 重启心跳
this.start();
},
// 重新连接
reconnect() {
if (this.lockReconnect) return
this.lockReconnect = true;
// 没连接上会一直重连,设置延迟避免请求过多
this.timeoutnum && clearTimeout(this.timeoutnum);
this.timeoutnum = setTimeout(() => {
this.initWebSocket();
this.lockReconnect = false;
}, 5000)
},
async setOnmessageMessage(event) {
console.log(JSON.parse(event.data));
this.$Notice.error({
title: '错误提示',
desc: JSON.parse(event.data).msg
});
this.resetSocket();
// 自定义全局监听事件
window.dispatchEvent(new CustomEvent('onmessageWS', {
detail: {
data: event.data
}
}))
// //发现消息进入 开始处理前端触发逻辑
// if (event.data === 'success' || event.data === 'heartBath') return
},
websocketonopen() {
// 开启心跳
this.start();
console.log('WebSocket连接成功!!!' + new Date() + '----' + this.websocket.readyState);
},
websocketonerror(e) {
console.log('WebSocket连接发生错误' + e);
},
websocketclose(e) {
this.websocket.close();
clearTimeout(this.timeoutObj);
clearTimeout(this.serverTimeoutObj);
console.log('WebSocket连接关闭');
this.start();
},
websocketsend(messsage) {
this.websocket.send(messsage)
},
closeWebSocket() { // 关闭websocket
this.websocket.close()
},
接続が成功した後の効果のスクリーンショット
バックグラウンドでアクティブなプッシュメソッド呼び出し
JSONObject rs = new JSONObject();
rs.put("code","-1");
rs.put("msg","xxxx产生异常,请联系工程师进行排查!!!");
sendMsgToAll(rs);
上記は、Web 側が積極的にメッセージをフロントエンドにプッシュしていることを理解しています~~~