当前位置: 首页 > article >正文

微信小程序中使用WebSocket通信

一、在utils文件夹下新建websocket.js文件,用来封装websocket的连接以及生命周期非法:

const app = getApp()
// 域名地址(项目实地址)
const Host = 'wss://fczd.hkbtwx.com/websocket/ws/';
 
// Socket连接成功
var socketOpen = false;
// Socket关闭
var socketClose = false;
// 消息队列
var socketMsgQueue = [];
 
// 判断心跳变量
var heart = null;
// 心跳失败次数
var heartBeatFailCount = 0;
// 终止心跳
var heartBeatTimeout = null;
// 终止重连
var connectSocketTimeout = null;
 
var webSocket = {
  // 连接Socket
  connectSocket:function(options) {
    if (socketOpen) return
    // wx.showLoading({
    //   title: 'Socket连接中...',
    //   mask: true
    // });
    socketOpen = false;
    socketClose = false;
    socketMsgQueue = [];
    let url = Host + app.globalData.userInfo.code
    wx.connectSocket({
      url: url,
      header:{
        'content-type': 'application/json'
      },
      success:function(res) {
        console.log('链接成功')
        if (options) {
          options.success && options.success(res);
        }
      },
      fail:function(res) {
        if (options) {
          options.fail && options.fail(res);
        }
      }
    })
  },
  // 发送消息
  sendSocketMessage:function(options) {
    if (socketOpen) {
      wx.sendSocketMessage({
        data: options.msg,
        success: function(res) {
          if (options) {
            options.success && options.success(res);
          }
        },
        fail: function(res) {
          if (options) {
            options.fail && options.fail(res);
          }
        }
      })
    } else {
      socketMsgQueue.push(options.msg)
    }
  },
  // 关闭Socket
  closeSocket: function(options) {
    if (connectSocketTimeout) {
      clearTimeout(connectSocketTimeout);
      connectSocketTimeout = null;
    };
    socketClose = true;
    this.stopHeartBeat();
    wx.closeSocket({
      success: function(res) {
        if (options) {
          options.success && options.success(res);
        }
      },
      fail: function(res) {
        if (options) {
          options.fail && options.fail(res);
        }
      }
    })
  },
  // 收到消息
  onSocketMessageCallback: function(msg) {},

  // 开始心跳
  startHeartBeat: function() {
    heart = true;
    this.heartBeat();
  },

  // 正在心跳
  heartBeat: function() {
    var that = this;
    if (!heart) {
      return;
    };
    that.sendSocketMessage({
      msg: JSON.stringify({
        // 与后端约定,传点消息,保持链接
        'message': 'ping',
        'toUserId': app.globalData.userInfo.code
      }),
      success: function(res) {
        if (heart) {
          heartBeatTimeout = setTimeout(() => {
            that.heartBeat();
          }, 8000);
        }
      },
      fail: function(res) {
        if (heartBeatFailCount > 2) {
          that.connectSocket();
        };
        if (heart) {
          heartBeatTimeout = setTimeout(() => {
            that.heartBeat();
          }, 8000);
        };
        heartBeatFailCount++;
      }
    });
  },

  // 结束心跳
  stopHeartBeat: function() {
    heart = false;
    if (heartBeatTimeout) {
      clearTimeout(heartBeatTimeout);
      heartBeatTimeout = null;
    };
    if (connectSocketTimeout) {
      clearTimeout(connectSocketTimeout);
      connectSocketTimeout = null;
    }
  }
};
 
// 监听WebSocket打开连接
wx.onSocketOpen(function(res) {
  wx.hideLoading();
  // 如果已经关闭socket
  if (socketClose) {
    webSocket.closeSocket();
  } else {
    socketOpen = true
    for (var i = 0; i < socketMsgQueue.length; i++) {
      webSocket.sendSocketMessage(socketMsgQueue[i])
    };
    socketMsgQueue = []
    webSocket.startHeartBeat();
  }
});
 
// 监听WebSocket错误
wx.onSocketError(function(res) {
  console.log('WebSocket连接打开失败,请检查!', res);
  wx.hideLoading();
  // wx.showToast({
  //   title: 'Socket连接失败:' + JSON.stringify(res),
  //   icon: 'none',
  //   duration: 3000
  // })
});

// 监听WebSocket接受到服务器的消息
wx.onSocketMessage(function(res) {
  console.log(res.data)
  webSocket.onSocketMessageCallback(res.data);
});
 
// 监听WebSocket关闭连接后重连
wx.onSocketClose(function(res) {
  if (!socketClose) {
    clearTimeout(connectSocketTimeout);
    connectSocketTimeout = setTimeout(() => {
      webSocket.connectSocket();
    }, 10000);
  }
});
 
module.exports = webSocket;

二、在需要接收websocket消息的页面引入:

const app = getApp()
const $api = require("../../utils/api.js")
const myRequest = require('../../utils/request.js')
let navBarTitleText = ''
const WebSocket = require('../../utils/websocket.js')
Page({
  data: {
    isBindWx: false,
    showLoginPop: false,
    userInfo: {},
    statisticsData: {}, //统计数据
    avatarUrl: '',
    // 登录页面相关参数
    avHeight: '',
    navTop: '',
    navHeight: '',
    loading: false,
    isOut: false,
    isLogin: false,
    message: '',
    messageCode: ''
  },
 
  onLoad: function(options) {
    if (wx.getStorageSync('isOut')) {
      this.setData({
        isOut: true
      })
      wx.setStorageSync('isOut', '')
    }
  },

  onShow: function() {
    if (typeof this.getTabBar === 'function' && this.getTabBar()) {
      this.getTabBar().setData({
        selected: 0,
        show: app.globalData.isLogin
      })
    }
    // 打开调试
    wx.setEnableDebug({
      enableDebug: false
    })
    wx.setNavigationBarTitle({
      title: navBarTitleText
    })
    this.setData({
      isBindWx: app.globalData.isBindWx,
      userInfo: app.globalData.userInfo,
      avatarUrl: app.globalData.avatarUrl,
      navHeight: app.globalData.navHeight,
      navTop: app.globalData.navTop,
      loading: false,
      isLogin: app.globalData.isLogin,
      navBarTitleText: app.globalData.isLogin ? '首页' : '登录'
    })
    if (app.globalData.isLogin) {
      this.getStatistics()
    }
    // 设置接收消息回调
    WebSocket.onSocketMessageCallback = this.onSocketMessageCallback
  },

  // 用户信息
  onUserInfo() {
    let that = this
    if (app.globalData.avatarUrl) {
      wx.navigateTo({
        url: '/pages/userInfo/index'
      })
      return
    }
    wx.getUserProfile({
      desc: '用于展示用户信息',
      success: (res) => {
        that.setData({
          avatarUrl: res.userInfo.avatarUrl
        })
        app.globalData.avatarUrl = res.userInfo.avatarUrl
        wx.navigateTo({
          url: '/pages/userInfo/index'
        })
        console.log('---------------' + res.userInfo.nickName)
        that.uploadPhoto(that.data.avatarUrl, res.userInfo.nickName)
      }
    })
  },

  // 上传微信头像
  uploadPhoto(url, nickName) {
    myRequest.request({
      url: $api.baseUrl + $api.uploadPhoto,
      method: 'POST',
      data: {
        photo: url,
        wxName: nickName,
        way: '1'
      },
      success: res => {
        if (res.data.code != '0') {
          wx.showToast({
            title: res.data.msg,
            duration: 3000,
            icon: 'none'
          })
        }
      }
    })
  },

  // 订阅消息
  onSubscribeMessage() {
    wx.requestSubscribeMessage({
      tmplIds: ['hkKPWSfweqkac20HAD2rlvweeV6vsuNysxKqibtECl8', 'eJ5j3aLYIO_rRax1dcKPrIaXAz99kmdxurSYX29CDbY'],
      complete: messageData => {
        app.globalData.isFirstOperation = false
        wx.showToast({
          title: '消息订阅成功',
          duration: 2000,
          icon: 'success'
        })
      }
    })
  },

  // 拒绝绑定微信
  onRefuse() {
    this.setData({
      showLoginPop: false
    })
  },

  // 同意绑定微信
  onAgree() {
    let that = this
    wx.login({
      success(res) {
        if (res.code) {
          // todo 发送 res.code 到后台换取 openId, sessionKey, unionId
          app.globalData.isBindWx = true
          that.setData({
            showLoginPop: false,
            isBindWx: true
          })
        } else {
          wx.showToast({
            title: '登录失败!' + res.errMsg,
            duration: 3000,
            icon: 'none'
          })
        }
      }
    })
  },

  /**
   * 登录相关
   */
  loginSucccess(e) {
    this.getTabBar().setData({
      show: true
    })
    // 创建WebSocket连接
    WebSocket.connectSocket()
    if (this.data.isOut) {
      wx.switchTab({
        url: '/pages/news/index'
      })
      this.setData({
        isOut: false
      })
    } else {
      this.setData({
        loading: false,
        isLogin: true,
        userInfo: e.detail,
        navBarTitleText: '首页'
      })
      // 获取数据
      // sthis.getStatistics()
      wx.setNavigationBarTitle({
        title: '首页'
      })
    }
  },

  // Socket收到的信息
  onSocketMessageCallback: function(res) {
    let message = JSON.parse(res)
    if (message.message == '连接成功' || message.message == 'ping') return
    this.setData({
      message: message.message,
      messageCode: message.code
    })
    setTimeout(() => {
      this.setData({
        message: '',
        messageCode: ''
      })
    }, 10000)
  },

  // 页面销毁时关闭连接
  onUnload: function(options) {
    WebSocket.closeSocket();
  }
})

三、以上便是微信小程序中WebSocket的封装和使用;下面再介绍单Activity多Fragment的Android项目中使用WebSocket进行通信:

3.1 编写WebSocket工具类:

public class MyWsManager {

    private static final String URL = SystemConst.WS_URL + "?appKey=" + CommonUtils.getDeviceNumber();
    private static MyWsManager myWsManager;
    private static WsManager wsManager;
    protected Logger logger = Logger.getLogger(MyWsManager.class);
    private Handler.Callback wsConnectCallBack;
    private static final int WS_OPEN_STATUS = 1;
    private static final int WS_MESSAGE_STATUS = 2;
    private static final int WS_MESSAGE_2_STATUS = 3;
    private static final int WS_RECONNECT_STATUS = 4;
    private static final int WS_CLOSING_STATUS = 5;
    private static final int WS_CLOSED_STATUS = 6;
    private static final int WS_FAILURE_STATUS = 7;

    public static MyWsManager getInstance() {
        if (myWsManager == null) {
            synchronized (MyWsManager.class) {
                if (myWsManager == null) {
                    myWsManager = new MyWsManager();
                }
            }
        }
        return myWsManager;
    }

    public void setWsConnectCallBack(Handler.Callback wsCallBack){
        if (wsCallBack != null){
            wsConnectCallBack = wsCallBack;
        }
    }

    private void onCallBack(Message message){
        if (wsConnectCallBack != null){
            wsConnectCallBack.handleMessage(message);
        }
    }

    public void initWS(Context context) {
        try {
            wsManager = new WsManager.Builder(context).client(
                new OkHttpClient().newBuilder()
                    .pingInterval(15, TimeUnit.SECONDS)
//                        .retryOnConnectionFailure(true)
                    .build())
                    .needReconnect(true)
                    .wsUrl(URL)
                    .build();
            wsManager.setWsStatusListener(wsStatusListener);
            wsManager.startConnect();
        } catch (Exception e) {
            logger.error("WebSocket连接异常:" + e.getMessage());
        }
    }

    //状态监听
    private WsStatusListener wsStatusListener = new WsStatusListener() {

        Message message = new Message();
        @Override
        public void onOpen(Response response) {
            logger.info("WebSocket服务器连接成功");
            EventBus.getDefault().postSticky("connect");
            message.what = WS_OPEN_STATUS;
            onCallBack(message);
        }

        @Override
        public void onMessage(String text) {
            message.what = WS_MESSAGE_STATUS;
            message.obj = text;
            onCallBack(message);
        }

        @Override
        public void onMessage(ByteString bytes) {
            message.what = WS_MESSAGE_2_STATUS;
            message.obj = bytes.toString();
            onCallBack(message);
        }

        @Override
        public void onReconnect() {
//            logger.debug("WebSocket服务器重连接中...");
            message.what = WS_RECONNECT_STATUS;
            onCallBack(message);
        }

        @Override
        public void onClosing(int code, String reason) {
//            logger.debug("WebSocket服务器连接关闭中:" + reason);
            message.what = WS_CLOSING_STATUS;
            onCallBack(message);
            //上面提及了设备会出现断开后无法连接的情况,那这种无法连接的情
            //况我发现有可能会卡在这个关闭过程中,因为如果是确实断开后会确实的启动重连机制
            //这里主要的目的就死让他跳出这个关闭中的状态,确实的关闭了ws
            if (wsManager != null) {
                wsManager.stopConnect();
                wsManager.startConnect();
            }
        }

        @Override
        public void onClosed(int code, String reason) {
            logger.debug("WebSocket服务器连接已关闭:" + reason);
            message.what = WS_CLOSED_STATUS;
            onCallBack(message);
        }

        @Override
        public void onFailure(Throwable t, Response response) {
            logger.error("WebSocket服务器连接失败:" +  t.getMessage());
            message.what = WS_FAILURE_STATUS;
            onCallBack(message);
        }
    };

    //发送ws数据
    public void sendData(String content) {
        if (wsManager != null && wsManager.isWsConnected()) {
            boolean isSend = wsManager.sendMessage(content);
            if (isSend) {
                logger.info("WebSocket发送数据成功");
            } else {
                logger.error("WebSocket发送数据失败");
            }
        } else {
            ToastUtils.showToast("WebSocket连接已断开");
        }
    }

    //断开ws
    public void disConnect() {
        if (wsManager != null)
            wsManager.stopConnect();
    }

    //判断WS是否断开了
    public boolean wsIsConnect(){
        if (wsManager == null) {
            return false;
        }
        return wsManager.isWsConnected();
    }
}

3.2 在activity中初始化WebSocket的连接、接收WebSocket消息,处理后分发给对应的fragment:

在processLogic中初始化WebSocket的连接

private void initWebSocket() {
    MyWsManager.getInstance().initWS(this);
    time = new TimeCount(2000, 1000);
    //WS状态监听
    MyWsManager.getInstance().setWsConnectCallBack(new Handler.Callback() {
        @Override
        public boolean handleMessage(@NonNull Message message) {
            switch (message.what) {
                case 1: //连接成功
                    timerCount = 1;
                    time.cancel();
                    homeModel.websocketStatus.set(1);
                    break;
                case 2: //接收string类型数据
                    String data = (String) message.obj;
                    if (!data.equals("连接建立成功")){
                        showWebSocketData(data);
                    }
                    break;
                case 3: //接收ByteString类型数据
                    String data2 = (String) message.obj;
                    break;
                case 4: //websocket连接中
                    break;
                case 5: //连接关闭中
                    break;
                case 6: //连接已关闭
                case 7: //连接失败
                    if (time != null){
                        time.cancel();
                        time.start();
                    }
                    homeModel.websocketStatus.set(0);
                    break;
            }
            return false;
        }
    });
}
// 重连机制
public class TimeCount extends CountDownTimer {

    public TimeCount(long millisInFuture, long countDownInterval) {
        super(millisInFuture, countDownInterval);
    }

    @Override
    public void onTick(long millisUntilFinished) {  // 计时过程
    }

    @Override
    public void onFinish() {// 计时完毕
        isTimer = false;
        //判断WebSocket是否断开
        if (!MyWsManager.getInstance().wsIsConnect()) {
            MyWsManager.getInstance().disConnect();  //断开socket
            MyWsManager.getInstance().initWS(MyApplication.getContext());
        }
    }
}

3.2 处理WebSocket消息,分发给对应的fragment:

private WebsocketPushBean data = null;
private void showWebSocketData(String pushData) {
    try {
        data = JSON.parseObject(pushData, WebsocketPushBean.class);
    } catch (Exception e){
        logger.error("WebSocket推送数据解析失败");
    }
    if (data != null){
        if (data.getType() == 6 || data.getType() == 8){ //6-主扫支付
            fragment.setWebSocketData(data);
        }else if (data.getType() == 7) { //主扫跳主动支付
            websocketPushBeans.add(data);
            fragment.setWebSocketData(data);
            payPopupView();
        } else if (data.getType() == 2){ //主动支付
            websocketPushBeans.add(data);
            fragment.setWebSocketData(data);
            payPopupView();
        } else if (data.getType() == 1){ //网约车支付
            websocketAutoPayBeans.add(data);
            autoPayPopupView();
        } else if (data.getType() == 9){ //车牌支付
            fragment.setWebSocketData(data);
        } else if (data.getType() == 10) { //流水绑定
            fragment.setWebSocketData(data);
        } else { //换购、赠品提示
            websocketPushBeans.add(data);
            payPopupView();
        }
    }
}

3.3 activity销毁时断开WebSocket连接

@Override
protected void onDestroy() {
    super.onDestroy();
    EventBus.getDefault().unregister(this);
    MyWsManager.getInstance().disConnect();
    if (time != null){
        time.cancel();
        time = null;
    }
}

3.4 项目中那么多fagmen,上面的fagmen指哪个?因为所有的fagmenl都继承BaseFragment,所有上面的fagmen是BaseFragment,至于如何确认是哪个具体的fagment,后面会说明。

3.5 创建一个IBusinessInterface接口,用来把当前显示的fragment设置给activity,内容如下:

public interface IBusinessInterface {
    void setSelectedFragment(BaseFragment fragment);
}

3.6 Activity实现此接口,并把传递过来的fragment设置给BaseFragment:

@Override
public void setSelectedFragment(BaseFragment fragment) {
    this.fragment = fragment;
}

3.7 fragment显示到前台时,把当前fragment设置给activity:

IBusinessInterface backInterface = (IBusinessInterface)getActivity();
backInterface.setSelectedFragment(this); // 将fragment传递到Activity中

至此,activity中的fragment便确认了。
3.8 fragment中重写接收WebSocket消息的方法setWebSocketData:

/**
 * 接收Websocket返回的数据
 */
public void setWebSocketData(WebsocketPushBean data){
    if (data.getType() == 6 || data.getType() == 7){ //支付成功
        if (customPopupView != null){
            customPopupView.dismiss();
        }
        vm.getOrderInfoByFlowNo(data.getFlowNo());
    } else if (data.getType() == 2){ //主动支付
        if (beScanPopupView != null){
            beScanPopupView.dismiss();
        }
        if (customPopupView != null){
            customPopupView.dismiss();
        }
    } else if (data.getType() == 8){ //扫码开票、扫码兑换关闭弹框
        if (customPopupView != null && customPopupView.isShow()){
            customPopupView.dismiss();
        }
        currentPage = 1;
        oilGunPage = 1;
        lastPage = false;
        oilWaterData.clear();
        //获取当前选中的油枪信息
        vm.getOrderInfoCountPos(model.deptCode.get(), model.selectGunList.get(), "1");
        //获取油品流水信息
        vm.getOrderInfoAndPayFlagPos(model.deptCode.get(), model.selectGunList.get(), 1);
    } else if (data.getType() == 9){ //车牌支付
        if (customPopupView != null){
            customPopupView.dismiss();
        }
        model.flowNo.set(data.getFlowNo());
        vm.getOrderInfoByFlowNo(data.getFlowNo());
    }
}

至此,activity接收WebSocket消息、分发给对应的fragment、fragment处理对应的业务整个链路已经通了。


http://www.kler.cn/a/600808.html

相关文章:

  • 使用Python爬虫获取1688商品(按图搜索)接口
  • 状态空间模型解析 (State-Space Model, SS)
  • 人工智能与区块链融合:开启数字信任新时代
  • (一)LeetCode热题100——哈希
  • 家庭网络结构之局域网通信
  • 监控告警+webhook一键部署
  • PAT乙级1007
  • jvm中每个类的Class对象是唯一的吗
  • 万字C++STL——vector模拟实现
  • Linux中的基本开发工具(上)
  • 基于Spring Boot的党员学习交流平台的设计与实现(LW+源码+讲解)
  • 【微服务架构】SpringCloud(七):配置中心 Spring Cloud Config
  • OpenCV图像拼接(7)根据权重图对源图像进行归一化处理函数normalizeUsingWeightMap()
  • 洛谷 P1351 [NOIP 2014 提高组] 联合权值(树)
  • HTML5 canvas圆形泡泡动画背景特效
  • 最长连续子序列和的所含元素 -- Kadane算法拓展
  • R语言——字符串
  • 一文解读DeepSeek的安全风险、挑战与应对策略
  • C#基础学习(一)复杂数据类型之枚举
  • 【Linux】从开发到系统管理深入理解环境变量