欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-06-15 22:57:31
前言之前写了一篇微信小程序使用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 over WebSocket连接阿里云IoT物联网平台
支付宝小程序使用MQTT over WebSocket连接阿里云IoT物联网平台
至此支付宝小程序使用mqtt.js连接服务器已完成,更多信息可以参考

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

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

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