注:本文是在已有的spring boot框架 引用webSocket框架。
1 引用依赖
<!-- websocket -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
2 实现webSocket处理器
/**
* 处理器
*
* @author terry
*/
@Component
@Slf4j
public class NetgateHandler extends TextWebSocketHandler {
/**
* 网关连接集合
*/
public static ConcurrentHashMap<Integer, ConcurrentHashMap<String, WebSocketSession>> netgates = new ConcurrentHashMap<>();
@Resource
private SysOrgCsMsgService sysOrgCsMsgService;
/**
* 处理前端发送的文本信息
* js调用websocket.send时候,会调用该方法
*/
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
if (!session.isOpen()) {
log.info("连接已关闭,不再处理该连接的消息!");
return;
}
String msg = message.getPayload();
if (message == null || "".equals(msg)) {
//收到空消息,不予处理
return;
} else if (msg.equals("Ping")) {
//心跳消息过滤掉
session.sendMessage(new TextMessage("Pong"));
return;
} else {
System.out.println("收到消息======"+msg);
SysOrgCsMsgDo sysOrgCsMsgDo = JSON.parseObject(msg, SysOrgCsMsgDo.class);
sysOrgCsMsgDo.setIsRead(YesNoEnum.NO);
if (!this.checkMsg(sysOrgCsMsgDo)) {
session.sendMessage(new TextMessage("消息体异常"));
return;
}
//发送消息
Integer value = sysOrgCsMsgDo.getToUserType().getValue();
String toUserId = sysOrgCsMsgDo.getToUserId();
ConcurrentHashMap<String, WebSocketSession> netgate = netgates.get(value);
if (netgate != null && netgate.containsKey(toUserId)) {
WebSocketSession webSocketSession = netgate.get(toUserId);
webSocketSession.sendMessage(new TextMessage(msg));
sysOrgCsMsgDo.setIsRead(YesNoEnum.YES);
}
sysOrgCsMsgService.saveSysOrgCsMsg(sysOrgCsMsgDo, KelpConstants.adminId);
}
}
/**
* 消息检查
*
* @param sysOrgCsMsgDo 消息体
* @return 结果
*/
private Boolean checkMsg(SysOrgCsMsgDo sysOrgCsMsgDo) {
if (StringUtil.isBlank(sysOrgCsMsgDo.getProviderId())) {
return false;
}
if (StringUtil.isBlank(sysOrgCsMsgDo.getFromUserId())) {
return false;
}
if (StringUtil.isBlank(sysOrgCsMsgDo.getToUserId())) {
return false;
}
if (StringUtil.isBlank(sysOrgCsMsgDo.getMsgContent())) {
return false;
}
if (sysOrgCsMsgDo.getFromUserType() == null) {
return false;
}
if (sysOrgCsMsgDo.getToUserType() == null) {
return false;
}
if (sysOrgCsMsgDo.getMsgContentType() == null) {
return false;
}
return true;
}
/**
* 当新连接建立的时候,被调用
* 连接成功时候,会触发页面上onOpen方法
*
* @param session
* @throws Exception
*/
@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
log.info("时间:{} ,正在初始化连接: {}",getSysDate(), session.getId());
try {
//初始化连接,把session存储起来
this.initUsers(session);
} catch (Exception e) {
e.printStackTrace();
}
log.info("初始化连接完成:{},时间{}",session.getId(),getSysDate());
}
/**
* 当连接关闭时被调用
*
* @param session
* @param status
* @throws Exception
*/
@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
log.info("时间:{} ,正在关闭连接: {}",getSysDate(), session.getId());
try {
log.info("断开连接状态值{}" , status.getCode());
this.removeSession(session);
} catch (Exception e) {
e.printStackTrace();
}
log.info("正在关闭完成{}:" , session.getId() );
}
/**
* 传输错误时调用
*
* @param session
* @param exception
* @throws Exception
*/
@Override
public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
log.error("发生传输错误:{};exception:{} ; 时间{}" , session.getId(), exception.getMessage(),getSysDate());
exception.printStackTrace();
if (session.isOpen()) {
//try { session.close(); } catch (Exception e) {e.printStackTrace();}
} else {
try {
this.removeSession(session);
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 连接接入的处理方法
*
* @param session session
*/
private synchronized void initUsers(WebSocketSession session) {
WebSocketParamDo webSocketParamDo = new WebSocketParamDo(session.getUri().toString());
if (webSocketParamDo.getUserTypeEnum() != null && StringUtils.isNotEmpty(webSocketParamDo.getUserId())) {
ConcurrentHashMap<String, WebSocketSession> netgate = netgates.get(webSocketParamDo.getUserTypeEnum().getValue());
if (netgate == null) {
netgate = new ConcurrentHashMap<>(KelpConstants.EIGHTEEN);
}
WebSocketSession session_exist = netgate.get(webSocketParamDo.getUserId());
if (session_exist != null) {
log.info("检测到相同SN重复连接,SN:{},连接ID:{},准备清理失效的连接。。。" , webSocketParamDo.getUserTypeEnum().getValue() ,session_exist.getId() );
try {
session_exist.close();
} catch (IOException e) {
e.printStackTrace();
}
}
netgate.putIfAbsent(webSocketParamDo.getUserId(), session);
netgates.put(webSocketParamDo.getUserTypeEnum().getValue(), netgate);
}
}
/**
* 连接被关闭时处理集合
*
* @param session
*/
private synchronized void removeSession(WebSocketSession session) {
String string = session.getUri().toString();
WebSocketParamDo webSocketParamDo = new WebSocketParamDo(string);
if (netgates.get(webSocketParamDo.getUserTypeEnum().getValue()).containsKey(webSocketParamDo.getUserId())) {
WebSocketSession exist_session = netgates.get(webSocketParamDo.getUserTypeEnum().getValue()).get(webSocketParamDo.getUserId());
//确保是同一个session 不是同一个session则不应该进行下一步的处理
if (exist_session.getId() != null && exist_session.getId().equals(session.getId())) {
netgates.get(webSocketParamDo.getUserTypeEnum().getValue()).remove(webSocketParamDo.getUserId());
log.info("有一网关连接关闭!SN:{},当前在线数量为{}" , webSocketParamDo.getUserTypeEnum(), netgates.get(webSocketParamDo.getUserTypeEnum().getValue()).keySet().size());
} else {
log.error("检测到关闭session异常,程序中断处理,关闭sessionId:{},当前实际sessionId: {}" , session.getId() , exist_session.getId());
}
} else {
log.error("检测到关闭session异常,程序中断处理,系统中未找到对应的session,Sn={} , openid= {}" , webSocketParamDo.getUserTypeEnum().getValue() ,webSocketParamDo.getUserId());
}
}
private String getSysDate() {
//设置日期格式
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return df.format(new Date());
}
}
3 实现webSocket过滤器
/**
* webSocket过滤器
* @author terry
*/
public class WebSocketHandshakeInterceptor implements HandshakeInterceptor {
/**
* 鉴权逻辑
* @param url 请求路径
* @return
*/
private boolean requestIsValid(String url){
//在这里可以写上具体的鉴权逻辑
return true;
}
@Override
public boolean beforeHandshake(org.springframework.http.server.ServerHttpRequest serverHttpRequest, org.springframework.http.server.ServerHttpResponse serverHttpResponse, org.springframework.web.socket.WebSocketHandler webSocketHandler, Map<String, Object> map) throws Exception {
if (serverHttpRequest instanceof ServletServerHttpRequest) {
String path = serverHttpRequest.getURI().getPath();
WebSocketParamDo webSocketParamDo = WebSocketParamDo.getWebSocketParamDo(path);
if ( StringUtil.isBlank(webSocketParamDo.getUserId()) || webSocketParamDo.getUserTypeEnum() == null) {
Magic.throwException("链接参数不合法");
return false;
}
String[] tokens = ((ServletServerHttpRequest) serverHttpRequest).getServletRequest().getParameterValues("token");
String token =tokens[0];
if(requestIsValid(token)){
((ServletServerHttpRequest) serverHttpRequest).getServletRequest().setAttribute("token",token);
((ServletServerHttpRequest) serverHttpRequest).getServletRequest().setAttribute("userId",webSocketParamDo.getUserId());
((ServletServerHttpRequest) serverHttpRequest).getServletRequest().setAttribute("linkType",webSocketParamDo.getUserTypeEnum().getValue());
}else {
//鉴权失败 断开链接
return false;
}
}
System.out.println("================Before Handshake================");
return true;
}
@Override
public void afterHandshake(org.springframework.http.server.ServerHttpRequest serverHttpRequest, org.springframework.http.server.ServerHttpResponse serverHttpResponse, org.springframework.web.socket.WebSocketHandler webSocketHandler, Exception e) {
System.out.println("================After Handshake================");
if(e!=null) {
e.printStackTrace();
}
System.out.println("================After Handshake================");
}
}
4 配置类的实现
/**
* 配置类
* @author terry
*/
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry
.addHandler(webSocketHandler(), "/WebSocket/clothing/{type}/{userId}")
.addInterceptors(new WebSocketHandshakeInterceptor())
//允许跨域,方便本地调试,生产建议去掉
.setAllowedOrigins("*");
}
@Bean
public TextWebSocketHandler webSocketHandler() {
return new NetgateHandler();
}
}