zoukankan      html  css  js  c++  java
  • WebSocket学习总结

      本文随便写了点自己对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

  • 相关阅读:
    ARM Linux异常处理之data abort(二)【转】
    ARM Linux异常处理之data abort(一)【转】
    arm的mmu学习【转】
    使用Squashfs和Overlayfs提高嵌入式Linux文件系统可靠性【转】
    【iView】100. iView踩坑集锦
    【AOP】【Publish-Event】AOP 切面编程 + Spring的publish-event 实现 监听者模式,实现补充业务逻辑开发
    【IDEA】idea打开新项目,左下角的工作栏中没有显示Services解决办法
    【java】ObjectOutputStream & ObjectInputStream 多次写入发生重复写入相同数据的问题
    【JS】时间工具类
    【MySQL】mysql优化集锦
  • 原文地址:https://www.cnblogs.com/chenkun-code/p/12721093.html
Copyright © 2011-2022 走看看