当前位置: 移动技术网 > IT编程>开发语言>JavaScript > 支付宝小程序使用MQTT over WebSocket连接阿里云IoT物联网平台

支付宝小程序使用MQTT over WebSocket连接阿里云IoT物联网平台

2020年08月01日  | 移动技术网IT编程  | 我要评论
前言之前写了一篇微信小程序使用MQTT over WebSocket连接阿里云IoT物联网平台,介绍了如何使用mqtt.js在微信小程序上连接mqtt服务器,文中顺带提了mqtt.js是支持支付宝小程序的,但是我本人没有实际编写过,后来有小伙伴来问我相关的问题,正好有空,于是稍微研究了一下,踩了不少坑,最后连接上了,以此记录,希望能给后来人一点帮助。坑点支付宝小程序和开发工具环境目前差距挺大,有时候开发工具能跑到真机就GG支付宝mqtt.js连接参数要传入一个my(mqtt.js文档里没写这点,看

前言

之前写了一篇微信小程序使用MQTT over WebSocket连接阿里云IoT物联网平台,介绍了如何使用mqtt.js在微信小程序上连接mqtt服务器,文中顺带提了mqtt.js是支持支付宝小程序的,但是我本人没有实际编写过,后来有小伙伴来问我相关的问题,正好有空,于是稍微研究了一下,踩了不少坑,最后连接上了,以此记录,希望能给后来人一点帮助。

坑点

  1. 支付宝小程序和开发工具环境目前差距挺大,有时候开发工具能跑到真机就GG
  2. 支付宝mqtt.js连接参数要传入一个my(mqtt.js文档里没写这点,看了源码才懂)
  3. 由于支付宝小程序底层变动,以及最新mqtt.js(v4.2.0)版本的改动,导致mqtt.js当前版本v4.2.0未支持支付宝小程序(有人提交了PR,但是没有后续,没合并)

综上所述,如果当前你想在支付宝小程序上使用mqtt.js,只能等合并pr发布的新版本,或者自己下载源码修改编译

修改源码步骤:

  1. 首先git clone https://github.com/mqttjs/MQTT.js.git 把源码下到本地
  2. 修改入口的判断,及PR#1135改动的部分
    对应文件 :lib/connect/index.js
if ((typeof process !== 'undefined' && process.title !== 'browser') || typeof __webpack_require__ === 'function') {
  protocols.mqtt = require('./tcp')	  protocols.mqtt = require('./tcp')
  protocols.tcp = require('./tcp')	  protocols.tcp = require('./tcp')
  protocols.ssl = require('./tls')	  protocols.ssl = require('./tls')

改成(即把 typeof webpack_require === ‘function’ 条件去掉)

if ((typeof process !== 'undefined' && process.title !== 'browser') ) {
  protocols.mqtt = require('./tcp')	  protocols.mqtt = require('./tcp')
  protocols.tcp = require('./tcp')	  protocols.tcp = require('./tcp')
  protocols.ssl = require('./tls')	  protocols.ssl = require('./tls')

对应文件:lib/connect/ws.js

// eslint-disable-next-line camelcase

var IS_BROWSER = (typeof process !== 'undefined' && process.title === 'browser') || typeof __webpack_require__ === 'function'

改成(同样去掉 typeof webpack_require === ‘function’ 条件),这个地方其实改不改都可以

// eslint-disable-next-line camelcase

var IS_BROWSER = (typeof process !== 'undefined' && process.title === 'browser')
  1. 修改支持支付宝小程序协议部分(对照PR修改即可)
    对应文件:lib/connect/ali.js 修改完成如下
'use strict'

var Transform = require('readable-stream').Transform
var duplexify = require('duplexify')
var base64 = require('base64-js')

/* global FileReader */
var my
var proxy
var stream
var isInitialized = false

function buildProxy () {
  var proxy = new Transform()
  proxy._write = function (chunk, encoding, next) {
    const _data = chunk.toString('base64'); //订正mqttjs支付宝小程序使用错误,支付宝data需要传入base64 string
    my.sendSocketMessage({
      data: _data,
      isBuffer: 1,
      success: function () {
        next()
      },
      fail: function () {
        next(new Error())
      }
    })
  }
  proxy._flush = function socketEnd (done) {
    my.closeSocket({
      success: function () {
        done()
      }
    })
  }

  return proxy
}

function setDefaultOpts (opts) {
  if (!opts.hostname) {
    opts.hostname = 'localhost'
  }
  if (!opts.path) {
    opts.path = '/'
  }

  if (!opts.wsOptions) {
    opts.wsOptions = {}
  }
}

function buildUrl (opts, client) {
  var protocol = opts.protocol === 'alis' ? 'wss' : 'ws'
  var url = protocol + '://' + opts.hostname + opts.path
  if (opts.port && opts.port !== 80 && opts.port !== 443) {
    url = protocol + '://' + opts.hostname + ':' + opts.port + opts.path
  }
  if (typeof (opts.transformWsUrl) === 'function') {
    url = opts.transformWsUrl(url, opts, client)
  }
  return url
}

function bindEventHandler () {
  if (isInitialized) return

  isInitialized = true

  my.onSocketOpen(function () {
    stream.setReadable(proxy)
    stream.setWritable(proxy)
    stream.emit('connect')
  })

  my.onSocketMessage(function (res) {
    if (typeof res.data === 'string') {
      var array = base64.toByteArray(res.data)
      var buffer = Buffer.from(array)
      proxy.push(buffer)
    } else {
      var reader = new FileReader()
      reader.addEventListener('load', function () {
        var data = reader.result

        if (data instanceof ArrayBuffer) data = Buffer.from(data)
        else data = Buffer.from(data, 'utf8')
        proxy.push(data)
      })
      reader.readAsArrayBuffer(res.data)
    }
  })

  my.onSocketClose(function () {
    stream.end()
    stream.destroy()
  })

  my.onSocketError(function (res) {
    stream.destroy(res)
  })
}

function buildStream (client, opts) {
  opts.hostname = opts.hostname || opts.host

  if (!opts.hostname) {
    throw new Error('Could not determine host. Specify host manually.')
  }

  var websocketSubProtocol =
    (opts.protocolId === 'MQIsdp') && (opts.protocolVersion === 3)
      ? 'mqttv3.1'
      : 'mqtt'

  setDefaultOpts(opts)

  var url = buildUrl(opts, client)
  my = opts.my
  my.connectSocket({
    url: url,
    headers : {
      "Sec-WebSocket-Protocol" : "mqtt"
    }
  })

  proxy = buildProxy()
  stream = duplexify.obj()

  bindEventHandler()

  return stream
}

module.exports = buildStream

修改完成之后,执行npm install等待编译完成即可在dist目录下看到两个文件:mqtt.js 和mqtt.min.js,前一个是未压缩版本,有日志输出,可以用来调试,后一个是压缩版本,线上环境使用

当然你要是嫌麻烦,可以用我已经编译好的mqtt

准备工作(这一步基本就和微信小程序没啥大的区别了)

  1. 自己编译或者下载我上面已经编译好的mqtt.js包
  2. 官方库aliyun-iot-client-sdk下载hmac-sha1算法库hex_hmac_sha1.js(当然也可以使用其他的库,比如crypto-js),点击打开链接然后右键另存为即可
  3. 下载支付宝小程序开发工具,新建任意项目
  4. 拷贝mqtt.min.js和hex_hmac_sha1.js到utils目录中去
  5. 可能支付宝还有其他配置,具体自己看文档了

开始编码

随便在一个页面的js文件中加入以下代码,注意替换参数为自己产品和设备的参数

const crypto = require('../../utils/hex_hmac_sha1.js'); //根据自己存放的路径修改
import {connect} from '../../utils/mqtt.min.js' //根据自己存放的路径修改

// 获取全局 app 实例
const app = getApp();
// 数据管理器
let conn = null;

Page({
  // 声明页面数据
  data: {
    dataLoaded: false,
    tasks: [],
    taskHandlers: [],
    taskCheckers: [],
    info:'看看',
    message:''
  },

  // 监听生命周期回调 onLoad
  onLoad() {
    
  },
  // 监听生命周期回调 onShow
  onShow() {
    // 同步全局数据到本地
    //this.loadData();
  },

  onHide() {
    // TODO: 清理注册事件
  },
onTap(){
  this.doConnect();
},
 doConnect(){
    const deviceConfig = {
      productKey: "替换",
      deviceName: "替换",
      deviceSecret: "替换",
      regionId: "替换"
    };
    const options = this.initMqttOptions(deviceConfig);
    console.log(options)
    //替换productKey为你自己的产品的(注意这里是wxs,不是wss,否则你可能会碰到ws不是构造函数的错误)
    const client = connect(`alis://${deviceConfig.productKey}.iot-as-mqtt.${deviceConfig.regionId}.aliyuncs.com`,options)

    this.setData({
      info:"开始连接.."
    })
    client.on('connect',  ()=> {
      console.log('连接服务器成功')
      this.setData({
        info:"连接服务器成功"
      })
      //订阅主题,替换productKey和deviceName(这里的主题可能会不一样,具体请查看后台设备Topic列表或使用自定义主题)
      client.subscribe(`/${deviceConfig.productKey}/${deviceConfig.deviceName}/get`, function (err) {
        if (!err) {
           console.log('订阅成功!');
        }
      })
    })
	//接收消息监听
    client.on('message',  (topic, message) =>{
      // message is Buffer
      console.log('收到消息:'+message.toString())
      this.setData({
        message:message.toString()
      })
     //关闭连接 client.end()
    })
  },
  //IoT平台mqtt连接参数初始化
 initMqttOptions(deviceConfig) {

    const params = {
      productKey: deviceConfig.productKey,
      deviceName: deviceConfig.deviceName,
      timestamp: Date.now(),
      clientId: Math.random().toString(36).substr(2),
    }
    //CONNECT参数
    const options = {
      keepalive: 60, //60s
      clean: true, //cleanSession不保持持久会话
      protocolVersion: 4 ,//MQTT v3.1.1
      my:my //注意这里的my
    }
    //1.生成clientId,username,password
    options.password = this.signHmacSha1(params, deviceConfig.deviceSecret);
    options.clientId = `${params.clientId}|securemode=2,signmethod=hmacsha1,timestamp=${params.timestamp}|`;
    options.username = `${params.deviceName}&${params.productKey}`;

    return options;
  },

/*
  生成基于HmacSha1的password
  参考文档:https://help.aliyun.com/document_detail/73742.html?#h2-url-1
*/
 signHmacSha1(params, deviceSecret) {

    let keys = Object.keys(params).sort();
    // 按字典序排序
    keys = keys.sort();
    const list = [];
    keys.map((key) => {
      list.push(`${key}${params[key]}`);
    });
    const contentStr = list.join('');
    return crypto.hex_hmac_sha1(deviceSecret, contentStr);
  }
});


运行代码,点击连接即可看到
在这里插入图片描述
在这里插入图片描述
至此支付宝小程序使用mqtt.js连接服务器已完成,更多信息可以参考

微信小程序使用MQTT over WebSocket连接阿里云IoT物联网平台

有疑问可以加我QQ:343672271 (备注mqtt)

本文地址:https://blog.csdn.net/ngl272/article/details/108164127

如您对本文有疑问或者有任何想说的,请 点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网