本文隨便寫了點自己對WebSoket通訊協議理解,在兩種框架上玩的Demo,然後踩了幾個坑還有沒填上的坑(歡迎評論指導一下)。 WebSocket是什麼?使用WebSocket的原因? WebSocket是網路通訊協議的一種。 提到網路通訊協議,我第一個就想到了HTTP協議,但是HTTP協議的一些特 ...
本文隨便寫了點自己對WebSoket通訊協議理解,在兩種框架上玩的Demo,然後踩了幾個坑還有沒填上的坑(歡迎評論指導一下)。
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後,打開程式會出現白屏?
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項目中,直接創建一個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