package org.springblade.common.handler; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.corundumstudio.socketio.*; import com.corundumstudio.socketio.annotation.OnConnect; import com.corundumstudio.socketio.annotation.OnDisconnect; import com.corundumstudio.socketio.annotation.OnEvent; import com.corundumstudio.socketio.protocol.Packet; import org.springblade.core.mp.support.Condition; import org.springblade.core.tool.api.R; import org.springblade.modules.modules.entity.FunctionEntity; import org.springblade.modules.modules.entity.ModulesEntity; import org.springblade.modules.modules.service.IFunctionService; import org.springblade.modules.modules.service.IModulesService; import org.springblade.modules.modules.vo.FunctionVO; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.CommandLineRunner; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; import com.corundumstudio.socketio.SocketIOServer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import java.net.ServerSocket; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.function.BiConsumer; /** * SocketIo启动类 */ @Component @Order(value = 1) public class SocketIOService { private final Logger log = LoggerFactory.getLogger(this.getClass()); // 用来存已连接的客户端 private static Map viewMap = new ConcurrentHashMap<>(); // 用来存已连接的客户端 private static Map controllerMap = new ConcurrentHashMap<>(); // // 用来存已连接的视图客户端 // private List> view = new ArrayList<>(); // // // 用来存已连接的控制客户端 // private List> controller = new ArrayList<>(); @Autowired private SocketIOServer socketIOServer; @Autowired private IFunctionService functionService; @Autowired private IModulesService modulesService; /** * Spring IoC容器创建之后,在加载SocketIOConfiguration Bean之后启动 * * @throws Exception */ @PostConstruct private void autoStartup() throws Exception { start(); } /** * Spring IoC容器在销毁SocketIOConfiguration Bean之前关闭,避免重启项目服务端口占用问题 * * @throws Exception */ @PreDestroy private void autoStop() throws Exception { stop(); } public void start() { socketIOServer.start(); log.info("socket.io初始化服务完成"); } /** * 连接时 * * @param client */ @OnConnect public void onConnect(SocketIOClient client) { FunctionVO functionEntity = getFuncVo(client); //判断页面和控制器是否已经连接过了 if (functionEntity.getIsView()) { for (int i = 0; i < viewMap.size(); i++) { if (viewMap.containsKey(functionEntity.getModulesId())) { client.sendEvent("connectError", R.fail("已连接")); return; } } } else { for (int i = 0; i < controllerMap.size(); i++) { if (controllerMap.containsKey(functionEntity.getModulesId())) { client.sendEvent("connectError", R.fail("已连接")); return; } } } List all = functionService.getAll(functionEntity); if (all.size() > 0) { if (functionEntity.getIsView()) { log.info("预览页{}", client.getSessionId().toString()); viewMap.put(functionEntity.getModulesId(), client); } else { log.info("控制页{}", client.getSessionId().toString()); controllerMap.put(functionEntity.getModulesId(), client); } client.sendEvent("connectOk", R.data(all)); } else { client.sendEvent("connectError", R.fail("连接失败")); } } /** * 监听前端订阅相同事件发送过来的信息 * * @param client * @param ackRequest * @param data */ @OnEvent(value = "msg") public void OnEvent(SocketIOClient client, AckRequest ackRequest, String data) { log.info("发来消息:" + data); } /** * 图册上下页控制 */ @OnEvent(value = "changeImgPage") public void OnEventImgChange(SocketIOClient client, AckRequest ackRequest, String data) { log.info("图册变更:" + data); FunctionVO funcVo = getFuncVo(client); viewMap.forEach((e,socketIO)->{ if (data.equals("previous")){ if (e.equals(funcVo.getModulesId())){ socketIO.sendEvent("previousPage","previous"); } }else if (data.equals("next")){ if (e.equals(funcVo.getModulesId())){ socketIO.sendEvent("previousPage","next"); } } }); } /** * 菜单控制 */ @OnEvent(value = "menuChange") public void OnEventMenuChange(SocketIOClient client, AckRequest ackRequest, String data) { log.info("菜单变更:" + data); FunctionVO funcVo = getFuncVo(client); viewMap.forEach((e,socketIOClient)->{ if (e.equals(funcVo.getModulesId())){ log.info("目标客户端:{}", socketIOClient.getSessionId().toString()); socketIOClient.sendEvent("menuChange", data); } }); } /** * 断开连接时 * * @param client */ @OnDisconnect() public void OnEvent(SocketIOClient client) { FunctionVO funcVo = getFuncVo(client); if (funcVo.getIsView()) { viewMap.forEach((e,socketIOClient)->{ if (e.equals(funcVo.getModulesId())){ viewMap.remove(e); log.info("预览页:{}断开连接", socketIOClient.getSessionId()); } }); } else { controllerMap.forEach((e,socketIOClient)->{ if (e.equals(funcVo.getModulesId())){ controllerMap.remove(e); log.info("控制页:{}断开连接", socketIOClient.getSessionId()); } }); } } public void stop() { if (socketIOServer != null) { socketIOServer.stop(); socketIOServer = null; } log.info("socket.io服务已关闭"); } /** * 此方法为获取client连接中的参数,可根据需求更改 * * @param client * @return */ private String getParamsByClient(SocketIOClient client) { // 从请求的连接中拿出参数(这里的sid必须是唯一标识) Map> params = client.getHandshakeData().getUrlParams(); List list = params.get("UID"); if (list != null && list.size() > 0) { return list.get(0); } return null; } /** * 获取连接的客户端ip地址 * * @param client: 客户端 * @return: java.lang.String */ private String getIpByClient(SocketIOClient client) { String sa = client.getRemoteAddress().toString(); return sa.substring(1, sa.indexOf(":")); } private FunctionVO getFuncVo(SocketIOClient client) { String connectInfo = client.getHandshakeData().getSingleUrlParam("connectInfo"); return JSON.parseObject(connectInfo, FunctionVO.class); } }