微信小程序> 小程序开发-websocket-网络小程序开发-小程序研发公司

小程序开发-websocket-网络小程序开发-小程序研发公司

浏览量:1935 时间: 来源:h_a_h_ahahah
小程序已经添加了对WebSocket的支持,如果需要长连接和推送的场景来讲可以使用。关于websocket的使用,小程序WebSocketAPI里边已经给了。相比较传统的HTTP接口形式,websocket长连接可实现双向通信,但是用好它可并不简单。

刚开始的时候做这个推送,我选用了Socket.IO协议来实现,服务器端用netty-socketio实现,而小程序用了一个第三方库wxapp-socket-io,但是遇到各种问题,连接自动断掉,一直连不上服务器。

经常出现如下情况,后台发现连接已经断了:

后来查询资料发现,socket.io支持的协议版本为4,参考socket.io-protocol

微信小程序websocket协议版本为13,可以用ws包或者以他为依赖的中间件ws

后来服务器改成SpringBoot集成的WebSocket实现,实现标准协议,客户端采用小程序官方API。不过又出现了其他的问题,踩过很多的坑,最后终于完美解决连接不稳定和断线重连的问题,本篇文章做一个总结。

注:在开发小程序长连接的过程中,在网上也google过大量的文章,但是都是一些皮毛,一个单页面演示一下连接成功例子就完了,都是些玩具级别的例子,对于断线重连机制要么就没做,要么就做的很差。

小程序API介绍具体的参数说明我就不贴出来了。

wx.connectSocket(OBJECT)

创建一个WebSocket连接。

基础库版本1.7.0及以后,支持存在多个WebSokcet连接,每次成功调用wx.connectSocket会返回一个新的SocketTask。

wx.onSocketOpen(CALLBACK)

监听WebSocket连接打开事件。

wx.onSocketError(CALLBACK)

监听WebSocket错误。

wx.sendSocketMessage(OBJECT)

通过WebSocket连接发送数据,需要先wx.connectSocket,并在wx.onSocketOpen回调完成之后才能发送。

wx.onSocketMessage(CALLBACK)

监听WebSocket接受到服务器的消息事件。

wx.closeSocket(OBJECT)

关闭WebSocket连接。

wx.onSocketClose(CALLBACK)

监听WebSocket关闭。

SocketTask这个类我要专门拿出来讲,因为小程序官方文档上面对它的介绍并不完整,导致小程序控制WebSocket的连接时遇到各种坑。

上面讲过,这个对象是通过方法wx.connectSocket(OBJECT)来获取的,它有一个属性值readyState,取下面4个状态值:

CONNECTING:0连接中OPEN:1已连接CLOSING:2关闭中CLOSED:3已关闭刚开始我们的做法是全局一个变量socketOpen,用来表示这个socket是否打开,没有打开就重连,否则就直接调用发送消息接口了。但是经过测试发现网络不稳定,会出现这个变量没有得到及时更新一直是true,然后就不再去连接了,但实际上已经断开了。

所以最后把这个socketOpen变量去掉,直接判断SocketTask对象的属性值readyState,如果是1的话就表示连接可用。

小程序生命周期要写好小程序应用,就必须理解小程序的生命周期,以及每个生命周期发生的时候会调用什么方法。

小程序分为应用和页面两个部分,所以小程序的生命周期就涉及到三个部分,分别是:

应用的生命周期页面的生命周期应用的生命周期对页面生命周期的影响应用的生命周期App()函数用来注册一个小程序。接受一个object参数,其指定小程序的生命周期函数等。

object参数说明:

属性类型描述触发时机onLaunchFunction生命周期函数–监听小程序初始化当小程序初始化完成时,会触发onLaunch(全局只触发一次)onShowFunction生命周期函数–监听小程序显示当小程序启动,或从后台进入前台显示,会触发onShowonHideFunction生命周期函数–监听小程序隐藏当小程序从前台进入后台,会触发onHide前台、后台定义:当用户点击右上角关闭,或者按了设备Home键离开微信,小程序并没有直接销毁,而是进入了后台;当再次进入微信或再次打开小程序,又会从后台进入前台。

用户首次打开小程序,触发onLaunch(全局只触发一次)。小程序初始化完成后,触发onShow方法,监听小程序显示。小程序从前台进入后台,触发onHide方法。小程序从后台进入前台显示,触发onShow方法。小程序后台运行一定时间,或系统资源占用过高,会被销毁。

页面的生命周期Page()函数用来注册一个页面。接受一个object参数,其指定页面的初始数据、生命周期函数、事件处理函数等。

object参数说明:

属性类型描述dataObject页面的初始数据onLoadFunction生命周期函数–监听页面加载onReadyFunction生命周期函数–监听页面初次渲染完成onShowFunction生命周期函数–监听页面显示onHideFunction生命周期函数–监听页面隐藏onUnloadFunction生命周期函数–监听页面卸载说明:

小程序注册完成后,加载页面,触发onLoad方法。页面载入后触发onShow方法,显示页面。首次显示页面,会触发onReady方法,渲染页面元素和样式,一个页面只会调用一次。当小程序后台运行或跳转到其他页面时,触发onHide方法。当小程序有后台进入到前台运行或重新进入页面时,触发onShow方法。当使用重定向方法wx.redirectTo(OBJECT)或关闭当前页返回上一页wx.navigateBack(),触发onUnload。

理解这些后,才能开始编写服务器和客户端代码

服务器端服务器代码就不贴了,请参考我的github源码

服务器端主要修正2个问题,一个是经常出现的IdleTimeout的异常,另外一个是Druid数据库连接池异常。

客户端基本思路是:

全局维护一个SocketTask对象,用来表示websocket连接,判断是否断线,作为重连的依据。同时定义一个全局callback回调函数,每个页面初始化的时候更新这个回调函数,那么在每个页面中收到返回消息就会执行当前页面逻辑。维护一个消息队列,所有消息请求会首先判断连接是否可用,如果可用直接发消息,否则将消息push到这个队列中。在app.js的onShow()函数中判断连接是否连上,如果没有连上就会触发websocket连接SocketTask对象的onOpen()负责从消息队列中取出请求消息,并发送这个请求消息SocketTask对象的onMessage()负责接收返回消息,并调用每个页面自己定义的回调函数SocketTask对象的onClose()监听函数中,触发websocket连接下面是app.js代码:

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103letsocketMsgQueue[]letisLoadingfalseApp({globalData:{userInfo:null,localSocket:{},callback:function(){}},onLaunch:function(options){//展示本地存储能力varlogswx.getStorageSync('logs')||[]logs.unshift(Date.now())wx.setStorageSync('logs',logs)constupdateManagerwx.getUpdateManager()updateManager.onUpdateReady(function(){updateManager.applyUpdate()})letthatthis//登录wx.login({success:res{//发送res.code到后台换取openId,sessionKey,unionId}})//获取用户信息wx.getSetting({success:res{if(res.authSetting['scope.userInfo']){//已经授权,可以直接调用getUserInfo获取头像昵称,不会弹框wx.getUserInfo({success:res{//可以将res发送给后台解码出unionIdthis.globalData.userInfores.userInfo//由于getUserInfo是网络请求,可能会在Page.onLoad之后才返回//所以此处加入回调以防止这种情况if(this.userInfoReadyCallback){this.userInfoReadyCallback(res)}}})}}})},showLoad(){if(!isLoading){wx.showLoading({title:'请稍后...',})isLoadingtrue}},hideLoad(){wx.hideLoading()isLoadingfalse},initSocket(){letthatthisthat.globalData.localSocketwx.connectSocket({//url:'wss://test.enzhico.net/app'url:'wss://mapp.enzhico.net/app'})that.showLoad()that.globalData.localSocket.onOpen(function(res){console.log('WebSocket连接已打开!readyState'+that.globalData.localSocket.readyState)that.hideLoad()while(socketMsgQueue.length0){varmsgsocketMsgQueue.shift();that.sendSocketMessage(msg);}})that.globalData.localSocket.onMessage(function(res){that.hideLoad()that.globalData.callback(res)})that.globalData.localSocket.onError(function(res){console.log('readyState'+that.globalData.localSocket.readyState)})that.globalData.localSocket.onClose(function(res){console.log('WebSocket连接已关闭!readyState'+that.globalData.localSocket.readyState)that.initSocket()})},//统一发送消息sendSocketMessage:function(msg){if(this.globalData.localSocket.readyState1){this.showLoad()this.globalData.localSocket.send({data:JSON.stringify(msg)})}else{socketMsgQueue.push(msg)}},onShow:function(options){if(this.globalData.localSocket.readyState!0this.globalData.localSocket.readyState!1){console.log('开始尝试连接WebSocket!readyState'+this.globalData.localSocket.readyState)this.initSocket()}}})下面是某个页面的onshow()函数:

123456789101112131415161718192021222324252627282930313233/***生命周期函数--监听页面显示*/onShow:function(){varthatthisapp.globalData.callbackfunction(res){letresDataJSON.parse(res.data)letdataresData.resultif(resData.method'list'){that.setData({total:parseInt(data.allAmount),amount:data.orderNum,jdcAmount:parseInt(data.jdcAmount),jszAmount:parseInt(data.jszAmount),list:data.list||[]})if(data.list0){that.setData({isEmpty:true})}that.getNumber(resData.result.allAmount)that.setAmount()}elseif(resData.method'notify'){//服务器推送消息,省略具体逻辑}}setTimeout(function(){app.sendSocketMessage({method:'list'})},300)},

版权声明

即速应用倡导尊重与保护知识产权。如发现本站文章存在版权问题,烦请提供版权疑问、身份证明、版权证明、联系方式等发邮件至197452366@qq.com ,我们将及时处理。本站文章仅作分享交流用途,作者观点不等同于即速应用观点。用户与作者的任何交易与本站无关,请知悉。

产品经理

手机 : 13312967497

擅长 : 小程序流量变现

扫码领取礼包

最新资讯

热门模板

  • 头条
  • 搜狐
  • 微博
  • 百家
  • 一点资讯
  • 知乎