引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
配置文件
package cn.jt.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
*/
@Configuration
publicclass WebSocketConfig {
/**
* 初始化Bean,它会自动注册使用了 @ServerEndpoint 注解声明的 WebSocket endpoint
*
* @return
*/
@Bean
public ServerEndpointExporter serverEndpointExporter() {
returnnew ServerEndpointExporter();
}
}
在项目中的使用
package com.unistrong.monitor.socket;
import com.alibaba.fastjson.JSONObject;
import com.unistrong.common.utils.JsonUtil;
import com.unistrong.common.utils.SpringUtils;
import com.unistrong.monitor.constant.RedisConstant;
import com.unistrong.monitor.service.RawNavService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
@ServerEndpoint("/socket/rawNav/Field")
@Slf4j
@Component
public class RawNavFieldWebSocket {
/**
* 静态变量,用来记录当前在线连接数。应该把它设计成线程安全的
*/
private static int onlineCount = 0;
/**
* concurrent 包的线程安全Set,用来存放每个客户端对应的 Socket对象
*/
public static ConcurrentHashMap<Session, Map<String, Object>> webSocketMap = new ConcurrentHashMap<>();
/**
* 与某个客户端的连接会话,需要通过它来给客户端发送数据
*/
private Session session;
/**
* 连接建立成功调用的方法
*
* @param session
*/
@OnOpen
public void onOpen(Session session, EndpointConfig config) {
this.session = session;
addOnlineCount(); // 在线数 +1
log.info("有新窗口开始监听,当前在线人数为{}", getOnlineCount());
}
/**
* 关闭连接
*/
@OnClose
public void onClose() {
subOnlineCount(); // 人数 -1
webSocketMap.remove(session);
log.info("有一连接关闭,当前在线人数为:{}", getOnlineCount());
}
/**
* 收到客户端消息后调用的方法
*
* @param message 客户端发送过来的消息
* @param session
*/
@OnMessage
public void onMessage(String message, Session session) {
log.info("收到来自窗口的信息:{}", message);
if (StringUtils.isNotBlank(message)) {
Map<String, Object> map = JSONObject.parseObject(message, Map.class);
//获取前端传递的参数
String stationCode = MapUtils.getString(map, "stationCode");
String mode = MapUtils.getString(map, "mode");
String equ = MapUtils.getString(map, "equ");
String navSystem = MapUtils.getString(map, "navSystem");
String navFrequency = MapUtils.getString(map, "navFrequency");
String serviceType = MapUtils.getString(map, "serviceType");
String prn = MapUtils.getString(map, "prn");
RawNavService service = SpringUtils.getBean(RawNavService.class);
Map<String, Object> initData = null;
String commRecordKey = null;
switch (serviceType) {
case "rawnav_clk_err":
commRecordKey = String.format(RedisConstant.KEY_RAWNAV_COMM + RedisConstant.KEY_RAWNAV_CLKERR
+ "%s:%s:%s:%s:%s", serviceType, mode, navSystem, navFrequency, prn);
initData = service.getInitDataClkErrField(commRecordKey);
break;
case "rawnav_ephemeris":
commRecordKey = String.format(RedisConstant.KEY_RAWNAV_COMM + RedisConstant.KEY_RAWNAV_EPHEMERIS
+ "%s:%s:%s:%s:%s", serviceType, mode, navSystem, navFrequency, prn);
initData = service.getInitDataEphemerisField(commRecordKey);
break;
case "rawnav_iono":
commRecordKey = String.format(RedisConstant.KEY_RAWNAV_COMM + RedisConstant.KEY_RAWNAV_INNO
+ "%s:%s:%s:%s:%s", serviceType, mode, navSystem, navFrequency, prn);
initData = service.getInitDataIonoField(commRecordKey);
break;
case "rawnav_utc_param":
commRecordKey = String.format(RedisConstant.KEY_RAWNAV_COMM + RedisConstant.KEY_RAWNAV_TIMESYNCPARAM
+ "%s:%s:%s:%s:%s", serviceType, mode, navSystem, navFrequency, prn);
initData = service.getInitDataUtcParamField(commRecordKey);
break;
case "rawnav_almanace":
commRecordKey = String.format(RedisConstant.KEY_RAWNAV_COMM + RedisConstant.KEY_RAWNAV_ALMANACE
+ "%s:%s:%s:%s:%s", serviceType, mode, navSystem, navFrequency, prn);
initData = service.getInitDataAlmanaceField(commRecordKey);
break;
}
//构建redisKey
map.put("commRecordKey", commRecordKey);
if (MapUtils.isNotEmpty(initData)) {
sendMessage(session, JsonUtil.toJson(initData));
webSocketMap.put(session, map);
}
}
}
/**
* @param session
* @param error
*/
@OnError
public void onError(Session session, Throwable error) {
log.error("Error:{}", session.getId(), error.getMessage());
error.printStackTrace();
}
/**
* 实现服务器主动推送
*
* @param message
* @throws IOException
*/
public void sendMessage(String message) throws IOException {
this.session.getBasicRemote().sendText(message);
}
public static void sendMessage(Session session, String message) {
try {
session.getBasicRemote().sendText(message);
log.info(message);
} catch (IOException e) {
log.error("ServerDataWebSocket.消息发送失败,失败原因:{}", e.getMessage());
}
}
private static synchronized int getOnlineCount() {
return onlineCount;
}
private static synchronized void addOnlineCount() {
RawNavFieldWebSocket.onlineCount++;
}
private static synchronized void subOnlineCount() {
RawNavFieldWebSocket.onlineCount--;
}
}
1、代码很简单,大家一看就知道逻辑了,这里就解释一下各个注解的含义
-
@ServerEndpoint: 将目前的类定义成一个websocket服务器端,注解的值将被用于监听用户连接的终端访问URL地址,客户端可以通过这个URL来连接到WebSocket服务器端
-
@OnOpen: 当WebSocket建立连接成功后会触发这个注解修饰的方法。
-
@OnClose: 当WebSocket建立的连接断开后会触发这个注解修饰的方法。
-
@OnMessage: 当客户端发送消息到服务端时,会触发这个注解修改的方法。
-
@OnError: 当WebSocket建立连接时出现异常会触发这个注解修饰的方法。
-
若无法自动注入,可使用
-
public class SpringUtil implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
if (SpringUtil.applicationContext == null) {
SpringUtil.applicationContext = applicationContext; // 设置全局应用上下文
}
}
// 获取应用上下文
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
// 通过Bean名称获取Bean实例
public static Object getBean(String name) {
return getApplicationContext().getBean(name);
}
// 通过Bean类型获取Bean实例
public static <T> T getBean(Class<T> clazz) {
return getApplicationContext().getBean(clazz);
}
// 通过Bean名称和类型获取Bean实例
public static <T> T getBean(String name, Class<T> clazz) {
return getApplicationContext().getBean(name, clazz);
}
}
该类在Spring启动时自动注入
ApplicationContext
(通过setApplicationContext
方法),后续所有getBean
调用都基于此上下文2。
SpringBoot 开启 WebSocket
在启动类加上 @EnableWebSocket