WebSocket使用
WebSocket推送之路
1、什么是WebSocket?
WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。浏览器通过 JavaScript 向服务器发出建立 WebSocket 连接的请求,连接建立以后,客户端和服务器端就可以通过 TCP 连接直接交换数据。
当你获取 Web Socket 连接后,你可以通过 send() 方法来向服务器发送数据,并通过 onmessage 事件来接收服务器返回的数据。
2、使用方法
a、pom依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency>
b、建立服务端(附下方代码)
@ServerEndpoint("/webSocket/{policeId}")
@Component
public class WebSocketServer {
//有内置方法:@OnOpen、@OnClose、@OnMessage、@OnError
}
c、配置添加websocket支持
@Configuration public class WebConfig { @Bean public ServerEndpointExporter serverEndpointExporter() { return new ServerEndpointExporter(); // 开启webSocket支持 }}
d、前端发起:ws://IP:端口/webSocket/,https内使用wss 【访问神器:http://www.bejson.com/httputil/websocket】
坑点:
1、连接时的数据推送
2、弱网、断网时的数据处理
3、发送失败时的数据处理
4、登录后心跳处理,连接关闭和发送错误的时候重连,打开和接收消息心跳
5、退出可添加方法调用关闭
连接工具类使用如下:
1 /** 2 * WebSocket服务端 3 */ 4 @Slf4j 5 @ServerEndpoint("/webSocket/{number}") 6 @Component 7 public class WebSocketServer { 8 9 //静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。 10 private static int onlineCount = 0; 11 //concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。 12 private static ConcurrentHashMap <String, WebSocketServer> webSocketSet = new ConcurrentHashMap <String, WebSocketServer>(); 13 //与某个客户端的连接会话,需要通过它来给客户端发送数据 14 private Session session; 15 //接收number 16 private String number = ""; 17 18 /** 19 * 连接建立成功调用的方法 20 */ 21 @OnOpen 22 public void onOpen(Session session, @PathParam("number") String number) { 23 try { 24 this.openConnection(session, number); 25 } catch(IOException e) { 26 log.error("webSocket的I/O异常"); 27 } 28 } 29 30 /** 31 * 连接关闭调用的方法 32 */ 33 @OnClose 34 public void onClose() { 35 this.close(number); 36 } 37 38 /** 39 * 收到客户端消息后调用的方法 40 * 41 * @param message 客户端发送过来的消息 42 */ 43 @OnMessage 44 public void onMessage(String message) throws IOException { 45 for(String key : webSocketSet.keySet()) { 46 if(!number.equals(key)) { 47 webSocketSet.get(key).sendMessage(new SimpleDateFormat("MM/dd HH:mm:ss").format(new Date()) + "用户" + number + "发来消息:" + message); 48 } 49 } 50 } 51 52 /** 53 * @param session 54 * @param error 55 */ 56 @OnError 57 public void onError(Session session, Throwable error) { 58 List <String> list = session.getRequestParameterMap().get("number"); 59 String policeNumber = list.get(0); 60 List <String> caCheList = AdminCache.get(policeNumber); 61 if(caCheList != null) { 62 for(int i = 0; i < caCheList.size(); i++) { 63 if(!StringUtil.isEmpty(caCheList.get(i))) { 64 OfflineRecordingService offlineRecordingService = SpringUtil.getBean(OfflineRecordingService.class); 65 OfflineRecording offlineRecording = new OfflineRecording(); 66 offlineRecording.setMessage(caCheList.get(i)); 67 offlineRecording.setPoliceNumber(policeNumber); 68 offlineRecordingService.insert(offlineRecording); 69 caCheList.remove(i); 70 } 71 } 72 } 73 AdminCache.set(policeNumber, caCheList); 74 error.printStackTrace(); 75 } 76 77 /** 78 * 实现服务器主动推送 79 */ 80 public void sendMessage(String message) throws IOException { 81 this.session.getBasicRemote().sendText(message); 82 } 83 84 /** 85 * 推送消息 86 * 87 * @param numberList 编号list或adminList 88 * @param message 消息 89 * @param type 0-推送至APP端、1-推送至web端 、3-推送至小程序端 90 */ 91 public static void sendToUser(List <String> numberList, String message, Integer type) { 92 if(type == 0) { 93 for(int i = 0; i < numberList.size(); i++) { 94 String sendPoliceNumber = numberList.get(i); 95 try { 96 if(webSocketSet.get(sendPoliceNumber) != null) { 97 webSocketSet.get(sendPoliceNumber).sendMessage(message); 98 List <String> list = AdminCache.get(sendPoliceNumber); 99 if(list == null) list = new ArrayList <>(); 100 list.add(message); 101 AdminCache.set(sendPoliceNumber, list); 102 } else { 103 log.info(sendPoliceNumber + "警员不在岗"); 104 OfflineRecordingService offlineRecordingService = SpringUtil.getBean(OfflineRecordingService.class);//注入方式 105 //保存数据库或者redis 106 } 107 } catch(IOException e) { 108 log.error("推送APP端异常"); 109 } 110 } 111 } else if(type == 1) { 112 for(int i = 0; i < numberList.size(); i++) { 113 String sendAdminNumber = numberList.get(i); 114 try { 115 if(webSocketSet.get(sendAdminNumber) != null) { 116 webSocketSet.get(sendAdminNumber).sendMessage(message); 117 } 118 } catch(IOException e) { 119 log.error("推送web端异常"); 120 } 121 } 122 } 123 } 124 125 /** 126 * 打开连接 127 * 128 * @param session 129 * @param number 130 * @throws IOException 131 */ 132 private void openConnection(Session session, String number) throws IOException { 133 this.session = session; 134 this.number = number; 135 if(number.contains(Constant.ADMIN_PREFIX)) {//后台登录admin+id 136 AdminService adminService = SpringUtil.getBean(AdminService.class); 137 String[] split = number.split("&"); 138 if(split.length < 3 || !StringUtil.isNumber(split[1])) return; 139 boolean isKicked = adminService.adminIsKicked(Integer.valueOf(split[1]), split[2]); 140 if(isKicked){ 141 this.sendMessage(JSON.toJSONString(new WebPushVo().setType(2).setMessage("您的账号已在其他设备登录"))); 142 return; 143 }else { 144 webSocketSet.put(number, this); 145 log.info("管理员监听:" + number); 146 } 147 } else { //app警员连接 148 PoliceManService policeManService = SpringUtil.getBean(PoliceManService.class); 149 Boolean isExist = policeManService.isExistPolice(number); 150 if(isExist) {//系统用户是否存在,避免资源浪费 151 webSocketSet.put(number, this);//加入map中 152 log.info("警员监听:" + number); 153 OfflineRecordingService offlineRecordingService = SpringUtil.getBean(OfflineRecordingService.class); 154 List <OfflineRecording> list = offlineRecordingService.selectList(number); 155 Integer[] arr = new Integer[list.size() + 1]; 156 for(int i = 0; i < list.size(); i++) { 157 OfflineRecording offlineRecording = list.get(i); 158 webSocketSet.get(number).sendMessage(offlineRecording.getMessage()); 159 arr[i] = offlineRecording.getId(); 160 } 161 offlineRecordingService.deleteOff(arr); 162 } 163 } 164 log.info("当前在线人数为" + getOnlineCount()); 165 } 166 167 /** 168 * 手动关闭连接 169 * 170 * @param number 171 */ 172 public static void close(String number) { 173 if(webSocketSet.get(number) != null) { 174 webSocketSet.remove(number);//从set中删除 175 if(number.contains(Constant.ADMIN_PREFIX)) { 176 log.info("管理员连接关闭:" + number); 177 } else { 178 log.info("警员连接关闭:" + number); 179 } 180 log.info("当前在线人数为" + getOnlineCount()); 181 } 182 } 183 184 public static synchronized int getOnlineCount() { 185 onlineCount = webSocketSet.size(); 186 return onlineCount; 187 } 188 189 public static synchronized void addOnlineCount() { 190 WebSocketServer.onlineCount++; 191 } 192 193 public static synchronized void subOnlineCount() { 194 WebSocketServer.onlineCount--; 195 } 196 }
浙公网安备 33010602011771号