目录
一、WebSocket背景
二、WebSocket实现
系统或者平台的一些功能,如聊天功能、消息推送、消息提醒等,需要在不刷新页面的情况下与服务器进行持续同步,达到实时通讯的效果。
实现实时通讯的方案有多种:例如
1、轮询
客户端定时向服务器发送请求,服务器接到请求后马上返回响应信息并关闭连接。 前端写setTimeOut设置在每隔一段时间不断请求后端,这种方式也能满足需求;但是这样的弊端也很明显,服务器压力加大,再者,当网络不稳定的时候,请求和响应的时间会超出setTimeOut设置的时间,从而出现不同步的情况
2、长轮询
客户端向服务器发送请求,服务器接到请求后hold住连接,直到有新消息才返回响应信息并关闭连接,客户端处理完响应信息后再向服务器发送新的请求。长轮询同样会给服务器带来很大的压力,而且当连接数达到一定数的时候,服务器可能会出现不再接收的新增连接
WebSocket是实现实时通讯的理想方式。它把客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。
在java中WebSocket有多种实现方式,如
使用Spring提供的低层级WebSocket API实现
使用STOMP消息实现
本文介绍的是使用@ServerEndpoint注解的实现方式。
添加依赖
org.springframework.boot
spring-boot-starter-websocket
2.0.4.RELEASE
@ServerEndpoint是webSocket的核心类。首先要注入ServerEndpointExporter,这个bean会自动注册使用了@ServerEndpoint注解声明的Websocket endpoint。
这里主要展示三个类,一个是配置类WebSocketConfig,一个是WebSocketEndPoint,声明方法,另外一个是SessionPool,用来放会话
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter(){
return new ServerEndpointExporter();
}
}
/**
* @Date 2020/12/13
* @Author
* @Description ServerEndpoint value则是给前端请求websocket的api
**/
@ServerEndpoint(value = "/api/websocket/{userId}")
@Component
public class WebSocketEndPoint {
/**
* 与某个客户端的连接会话,需要通过它来给客户端发送数据
*/
private Session session;
/**
* 连接建立成功需要调用的方法
*
* @param session
* @param userId
*/
@OnOpen
public void onOpen(Session session, @PathParam("userId") String userId) {
// 把会话存入到连接池中
this.session = session;
SessionPool.sessionMap.put(userId, session);
}
/**
* 关闭连接
*
* @param session
*/
@OnClose
public void onClose(Session session) throws IOException {
SessionPool.close(session.getId());
session.close();
}
/**
* 收到客户端消息后调用的方法
* @param message
* @param session
*/
@OnMessage
public void onMessage(String message,Session session){
Map params = JSON.parseObject(message,new HashMap().getClass());
SessionPool.sendMessage(params);
}
}
public class SessionPool {
public static Map sessionMap = new ConcurrentHashMap<>();
public static void close(String sessionId) throws IOException {
for (String userId : SessionPool.sessionMap.keySet()) {
Session session = sessionMap.get(sessionId);
if (session.getId().equals(sessionId)) {
sessionMap.remove(userId);
break;
}
}
}
public static void sendMessage(String sessionId, String message) {
sessionMap.get(sessionId).getAsyncRemote().sendText(message);
}
public static void sendMessage(String message) {
for (String sessionId : SessionPool.sessionMap.keySet()) {
sessionMap.get(sessionId).getAsyncRemote().sendText(message);
}
}
public static void sendMessage(Map params) {
// 接收消息进行封装,以及写入数据库也在这边实现
Session session = sessionMap.get(params.get("userId").toString());
if (session != null) {
session.getAsyncRemote().sendText(params.get("message").toString());
}
}
}
前端代码:
initWebSocket() {
const { userId } = this.$store.state.user.userInfo;
this.userId = userId;
console.log(this.userId);
const websocketHeartbeatJs = new WebsocketHeartbeatJs({
url: `ws://localhost:39888/api/websocket/${userId}`,
pingTimeout: 15000,
pongTimeout: 15000,
reconnectTimeout: 2000,
pingMsg: "heartBeat",
repeatLimit: 5
});
this.websocket = websocketHeartbeatJs;
this.websocket.onmessage = this.websocketonmessage;
this.websocket.onopen = this.websocketonopen;
this.websocket.onerror = this.websocketonerror;
this.websocket.onclose = this.websocketclose;
},
websocketonopen() {
if (this.userId !== "1315208483947286528") {
const message = {
userId: "1315208483947286528",
message: "hello123123214"
};
this.websocketsend(JSON.stringify(message));
}
},
websocketonerror() {
// 连接建立失败重连
this.initWebSocket();
},
websocketonmessage(e) {
// 数据接收
// const redata = JSON.parse(e.data);
console.log(e.data, "data");
},
websocketsend(Data) {
// 数据发送
this.websocket.send(Data);
},
websocketclose(e) {
// 关闭
console.log("断开连接", e);
}
由于时间问题,写的比较仓促,后续会补充完善