本文随便写了点自己对WebSoket通讯协议理解,在两种框架上玩的Demo,然后踩了几个坑还有没填上的坑(欢迎评论指导一下)。
WebSocket是什么?使用WebSocket的原因?
WebSocket是网络通讯协议的一种。
提到网络通讯协议,我第一个就想到了HTTP协议,但是HTTP协议的一些特性我想不用多说,大家也都是了解的,像无法保持长连接(由于功能需要,已有大佬整出保持长连接的方式);发起端只能是客户端;这些特性让我们在实际开发某些功能遇到了极大的麻烦,所以在HTML5推出WebSocket标准,让浏览器和服务器建立了无限制的双全工通信,双方可以互发消息。
WebSocket框架上使用
angular(7.2.2)+ ionic(4.0.0)
这是一个移动端应用程序,在angular框架中,我惯用服务(service)来处理业务,因此直接在服务管理的文件夹创建一个WebSocket的服务(ng generate service WebSocket)。WebSocket服务里包含创建连接,重连机制,心跳检测,计算运行时间等基础功能(详细写法可见代码)。
接下来可以在app全局新增一个WebSocket组件,ngOnInit生命钩子去建立连接,往组件中写入收发消息代码。会解决网页刷新导致WebSocket实例被清除,WebSocket组件在生命周期再次连接。
问题1:我在ionic中创建了WebSocket组件,用于刷新重连(app没有刷新,实际操作只会在浏览器调试中出现),在浏览器上调试可以正常使用并且不会断开连接。但是当我将代码打包编译成apk后,打开程序会出现白屏?
问题2:因为我脱离了组件使用WebSocket,单纯的调用服务。我实际组件中需要使用的数据也保存在服务之中,导致消息返回数据不会更新视图?
1 import { Injectable } from '@angular/core';
2 import { interval, Subject } from 'rxjs';
3
4 @Injectable({
5 providedIn: 'root'
6 })
7 export class WebsocketService {
8 public websocket: WebSocket; // websocket通讯对象
9 url: string = null; // websocket连接地址
10 isConnectSuccess: boolean = false; // 当前连接状态
11 isReconnect: boolean = false; // 是否正在重连
12 reconnectSubscription: any = null; // 定时重新连接对象
13 reconnectPeriod: number = 20 * 1000; // 重连失败,定时重新连接的时间刻度,20s
14 heartCheckSubscription: any = null; // 定时心跳检查对象
15 heartCheckPeriod: number = 10 * 60 * 1000; // 定时心跳检测的时间刻度,10min
16 runTimeSubscription: any = null; // 记录运行时间对象
17 runTimePeriod: number = 10 * 60 * 1000; // 记录运行时间的时间刻度,10min
18
19 constructor(
20 private messageService: MessageService,
21 ) { }
22
23 /**
24 * @description 更新连接地址,创建WebSocket实例,添加连接打开,连接关闭,连接异常,接收消息事件
25 * @method Connect
26 * @author chenkun
27 */
28 Connect(url?: string) {
29 const ip = localStorage.getItem('ipAddress');
30 if (ip) {
31 this.url = "ws://" + ip + ":40100";
32 } else {
33 this.messageService.ErrorToast('当前设备没有服务器地址');
34 }
35 if (!!url) {
36 this.url = url;
37 }
38 if (this.url) {
39 this.websocket = new WebSocket(this.url);
40 }
41 this.websocket.onopen = (event) => {
42 this.OnOpen(event);
43 }
44 this.websocket.onclose = (event) => {
45 this.OnClose(event);
46 }
47 this.websocket.onerror = (event) => {
48 this.OnError(event);
49 }
50 this.websocket.onmessage = (event) => {
51 this.OnMessage(event);
52 }
53 }
54
55 /**
56 * @description 检测当前websocket服务状态
57 * @method CheckWebSocket
58 * @author chenkun
59 */
60 CheckWebSocket() {
61 const websocket = this.websocket;
62 if (websocket) {
63 switch (websocket.readyState) {
64 case 0:
65 // 没有连接
66 break;
67 case 1:
68 // 连接成功
69 break;
70 case 2:
71 // 连接正在关闭
72 break;
73 case 3:
74 // 连接关闭
75 break;
76 }
77 } else {
78 // WebSocket实例对象没有,刷新浏览器会导致这种情况
79 }
80 }
81
82 /**
83 * @description WebSocket连接成功时触发事件,当前连接状态改为成功,如果当前正在重连则停止重新连接,开启心跳检测和计算连接运行时间
84 * @param event 连接成功时,服务端发回的事件对象
85 * @method OnOpen
86 * @author chenkun
87 */
88 OnOpen(event: any) {
89 // 连接成功
90 this.isConnectSuccess = true;
91 if (this.isReconnect) {
92 this.StopReconnect();
93 this.StartHeartCheck();
94 this.StartCalcRunTime();
95 }
96 }
97
98 /**
99 * @description WebSocket连接关闭时触发事件,当前连接状态改为失败,开始尝试重新连接,停止计算运行时间
100 * @param event 连接失败时,服务端发回的事件对象
101 * @method OnClose
102 * @author chenkun
103 */
104 OnClose(event: any) {
105 // 连接关闭
106 this.isConnectSuccess = false;
107 this.websocket.close();
108 this.StartReconnect();
109 this.StopRunTime();
110 }
111
112 /**
113 * @description WebSocket连接异常时触发事件,出现异常会同时触发连接关闭事件
114 * @param event 连接异常时,服务端发回的事件对象
115 * @method OnError
116 * @author chenkun
117 */
118 OnError(event: any) {
119 // 连接异常
120 this.isConnectSuccess = false;
121 }
122
123 /**
124 * @description WebSocket服务端发回消息接收事件
125 * @param event 服务端发回消息的事件对象
126 * @method OnMessage
127 * @author chenkun
128 */
129 OnMessage(event: any) {
130 // 服务器返回的消息
131 console.log(event);
132 }
133
134 /**
135 * @description WebSocket客户端发送消息给服务端,发送消息前先检查打印服务是否连接
136 * @param message 客户端发送的消息
137 * @method SendMessage
138 * @author chenkun
139 */
140 SendMessage(message: any) {
141 // 检查WebSocket的状态,连接存在时才能发送消息
142 this.CheckWebSocket();
143 if (this.websocket) {
144 if (this.websocket.readyState === 1) {
145 this.websocket.send(message);
146 }
147 }
148 }
149
150 /**
151 * @description 开始定时重连WebSocket服务端,如果连接成功,停止重连并且退出,如果正在重连直接退出
152 * 如果都没有,改为正在重连状态,订阅计时器循环发送调用连接
153 * @method StartReconnect
154 * @author chenkun
155 */
156 StartReconnect() {
157 if (this.isConnectSuccess) {
158 this.StopReconnect();
159 return;
160 }
161 if (this.isReconnect) {
162 return;
163 }
164 this.isReconnect = true;
165 this.reconnectSubscription = interval(this.reconnectPeriod).subscribe(async (value) => {
166 console.log(`重连:${value}次`);
167 const url = this.url;
168 this.Connect(url);
169 });
170 }
171
172 /**
173 * @description 更改不再重连状态,取消订阅计时器循环发送重复连接
174 * @method StopReconnect
175 * @author chenkun
176 */
177 StopReconnect() {
178 this.isReconnect = false;
179 // 取消订阅定时重新连接事件
180 if (typeof this.reconnectSubscription !== 'undefined' && this.reconnectSubscription != null) {
181 this.reconnectSubscription.unsubscribe();
182 }
183 }
184
185 /**
186 * @description 订阅计时器查询心跳检测,如果当前处于连接成功状态不做处理。如果没有连接,就停止心跳检测,开始重新连接
187 * @method StartHeartCheck
188 * @author chenkun
189 */
190 StartHeartCheck() {
191 this.heartCheckSubscription = interval(this.heartCheckPeriod).subscribe((value) => {
192 if (this.websocket != null && this.websocket.readyState === 1) {
193 console.log(value, '连接状态成功,发送消息保持连接');
194 } else {
195 this.StopHeartCheck();
196 this.StartReconnect();
197 }
198 });
199 }
200
201 /**
202 * @description 取消订阅计时器查询心跳检测
203 * @method StopHeartCheck
204 * @author chenkun
205 */
206 StopHeartCheck() {
207 if (typeof this.heartCheckSubscription !== 'undefined' && this.heartCheckSubscription != null) {
208 this.heartCheckSubscription.unsubscribe();
209 }
210 }
211
212 /**
213 * @description 订阅计时器计算连接运行时间
214 * @method StartCalcRunTime
215 * @author chenkun
216 */
217 StartCalcRunTime() {
218 this.runTimeSubscription = interval(this.runTimePeriod).subscribe(value => {
219 console.log('运行时间', `${value}分钟`);
220 });
221 }
222
223 /**
224 * @description 取消订阅计时器计算连接运行时间
225 * @method StopRunTime
226 * @author chenkun
227 */
228 StopRunTime() {
229 if (typeof this.runTimeSubscription !== 'undefined' && this.runTimeSubscription !== null) {
230 this.runTimeSubscription.unsubscribe();
231 }
232 }
233 }
vue(2.5.2)+ element-ui(2.4.11)
Vue项目中,直接创建一个SocketHelper.vue的子组件,并且直接在App.vue引入组件。借助Vuex来传递数据,借助eventBus收发消息事件。
<template>
<div id="app" class="app">
<socket />
<router-view />
</div>
</template>
<script>
import socket from "./public-components/SocketHelper.vue";
export default {
name: "App",
components: {
socket
},
};
</script>
<template>
<div></div>
</template>
<script>
import store from "../vuex/store";
export default {
data() {
return {
websocket: null,
eventBus: this.store.state.eventBus
};
},
created() {
this.initWebSocket();
},
destroyed() {
this.websocketclose();
},
methods: {
//初始化weosocket
initWebSocket() {
const url = "ws:" + this.configs.ServiceAddress + ":40100"; //ws地址
this.websocket = new WebSocket(url);
this.websocket.onopen = this.websocketonopen;
this.websocket.onerror = this.websocketonerror;
this.websocket.onclose = this.websocketclose;
this.websocket.onmessage = this.websocketonmessage;
this.eventBus.$off("WebSocketSendContent");
this.eventBus.$on("WebSocketSendContent", res => {
this.websocketsend(res);
});
},
websocketonopen() {
// 连接成功
},
websocketonerror(e) {
// 连接异常
},
websocketclose(e) {
// 连接关闭
this.initWebSocket();
},
websocketonmessage(e) {
// 接收消息
},
websocketsend(agentData) {
// 发送消息
if (this.websocket.readyState === 1) {
this.websocket.send(agentData);
}
},
}
};
</script>
参考来自
[angular整合websocket] https://www.jianshu.com/p/b04c34df128d
来源:oschina
链接:https://my.oschina.net/u/4342169/blog/3274351