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 }
View Code

 

 

 

posted on 2020-05-06 12:32  fuanfei  阅读(423)  评论(0)    收藏  举报