【消息中间件】原生PHP对接Uni H5、APP、微信小程序实时通讯消息服务

视频演示效果

【uniapp】实现买定离手小游戏

前言

Mqtt不同环境问题太多,新手可以看下

  1. 《【MQTT】Esp32数据上传采集:最新mqtt插件(支持掉线、真机调试错误等问题》
  2. 《一篇就够:uniapp-Mqtt系列问题详细攻略(解决掉线、真机调试错误等问题)》
  3. 《解决微信小程序MQTT真机连接问题与合法域名配置SSL问题》
  4. 《解决微信小程序MQTT通讯真机调试失败的问题附加可用代码》
  5. 《Esp8266-01s、51单片机实现连接MQTT踩坑:附加烧录安信可固件+宝塔搭建MQTT服务器 全套攻略》

以上的就是我所有的Mqtt踩坑记录,相信看完应该能解决了,今天这一篇文章,主要是记录升级Mqtt5.0以及如何适配安卓端,如果不想看,也可以直接去下载插件:【uniapp】【5.0协议】最完整Mqtt示例代码(解决掉线、真机调试错误等问题)

注意:插件代码不含如果要用在app端,请留意评论区的消息,换协议

一、分析

将原APP接入实时通讯,采用MQTT,有很多优点,这里就不列举了。这次对接的是我的打卡平台,

分为三个端:H5、APP、微信小程序

要保证三个端都通,我这里也不绕圈子了,协议我会放在本节底部,通过uniapp中的app.vue文件,将mqtt连接为全局状态,无论哪个页面都不会掉线,那么如何一对一接收呢?这里我做的思路是将客户端的订阅号订阅名改为自己的登陆账号,也就是说,用户未登录时不连接,检测到用户登录后将账户结合一些制定字符串作为onTopic,服务端指定发送过去即可,分析完之后我们开始实现客户端的连接。

连接案例:

代码语言:javascript
复制
var hosts = '';
// #ifdef APP-PLUS
hosts = 'wx://' + that.globalData.serve.host + ':443/mqtt';
// #endif

// #ifdef H5
hosts = 'wss://' + that.globalData.serve.host + ':443/mqtt';
// hosts = 'wss://' + that.globalData.serve.host + ':443/mqtt';

// hosts = 'tcp://' + this.globalData.serve.host + ':' + this.globalData.serve.wsport + this.globalData.serve.path;
//#endif
// #ifdef MP-WEIXIN
// wx仅仅可以使用体验版
hosts = 'wxs://' + that.globalData.serve.host + ':443/mqtt';
//#endif

二、全局注入MQTT连接

1.引入库

代码语言:javascript
复制
import mqtt from '@/utils/mqtt3.0.0.js'; // 导入MQTT库

库直接在插件中下载即可用:

【uniapp】【5.0协议】最完整Mqtt示例代码(解决掉线、真机调试错误等问题)

2.写入全局连接代码

App.vue是uni-app的主组件,所有页面都是在App.vue下进行切换的,是页面入口文件。但App.vue本身不是页面,这里不能编写视图元素,也就是没有。

这个文件的作用包括:调用应用生命周期函数、配置全局样式、配置全局的存储globalData

代码语言:javascript
复制
<script>
export default {
globalData: {
text: 'text'
}
}
</script>

所以我们的代码:

代码语言:javascript
复制
<script>
import mqtt from '@/utils/mqtt3.0.0.js'; // 导入MQTT库
export default {
globalData: {
serve: {
host: 'mqtt.taila.club',
wsport: '8083',
wssport:'443',
path: 'mqtt',
},
onTopic: '',//订阅发送来的消息
onSub: '',//
Qos: 2,
sendMassage: '',
time:0,
receiveMessage: '',
client: null,
//MQTT连接的配置
options: {
wsOptions: {},
protocolVersion: 5, //MQTT连接协议版本
clientId: '',
keepalive: 60,
clean: false,
username: '',
password: '',
reconnectPeriod: 1000, //1000毫秒,两次重新连接之间的间隔
connectTimeout: 30 * 1000, //1000毫秒,两次重新连接之间的间隔
resubscribe: true //如果连接断开并重新连接,则会再次自动订阅已订阅的主题(默认true)
},
},
onLaunch: function() {
let that=this;
console.log('======'+that.globalData.Qos)
// console.log('======'+that.globalData.Qos)
// 先断开
that.unconnect();
console.log('App Launch')

    //版本检查
    //调用示例 配置参数, 默认如下,其中api是接口地址,必须填写
    // #ifdef APP-PLUS
  
    //
  
    //版本检查
  
    
    //mqtt
    // 检查本地存储是否存在登录状态的信息
    that.check_account_mqtt_connect();
    
  },
  methods: {
    subscribe: function() {
        // 判断是否已成功连接
        if (!this.globalData.client || !this.globalData.client.connected) {
          this.showToast(&#39;客户端未连接&#39;, 1000)
          return;
        }
    
        this.globalData.client.subscribe(this.globalData.onTopic, {
          qos: this.globalData.Qos
        }, error =&gt; {
          if (!error) {
            this.showToast(&#39;订阅成功&#39;, 1000, &#39;success&#39;)
            console.log(&#39;订阅成功&#39;);
          }
        });
    
        
      },
      publish: function() {
        // 判断是否已成功连接
        if (!this.globalData.client || !this.globalData.client.connected) {
          this.showToast(&#39;客户端未连接&#39;, 1000)
          return;
        }
        if (this.globalData.sendMassage != &#39;&#39;) {
          // var send = &#39;{&#34;code&#34;: 200, &#34;msg&#34;: &#34;发送打1111指令&#34;, &#34;data&#34;: &#34;2.doc&#34;}&#39;;
          
           // 定义JSON对象
            const messageq = {
              code: 200,
              msg: &#39;发送打印指令&#39;,
              data: &#39;2.doc&#39;
            }
          
            // 将JSON对象转换为JSON字符串
            const message1 = JSON.stringify(messageq)
          this.globalData.client.publish(this.globalData.onSub,message1, error =&gt; {
            console.log(error || &#39;消息发布成功&#39;);
            this.showToast(&#39;消息发布成功&#39;, 1000, &#39;success&#39;)
          });
        } else {
          this.showToast(&#39;发布消息为空&#39;, 1000)
        }
    
      },
      unsubscribe: function() {
        this.globalData.client.unsubscribe(
          
          this.globalData.onTopic,
          err =&gt; {
            console.log(err || &#39;取消订阅成功&#39;);
            this.showToast(&#39;取消订阅成功&#39;, 1000, &#39;success&#39;)
          }
        );
      },
      unconnect: function() {
        if (!this.globalData.client || !this.globalData.client.connected) {
          this.showToast(&#39;客户端未连接&#39;, 1000)
          return;
        }
        this.client.end();
        this.client = null
        this.showToast(&#39;成功断开连接&#39;, 1000, &#39;success&#39;)
        console.log(&#39;断开连接&#39;);
      },
      showToast: function(title, time, icon = &#39;none&#39;) {
        uni.showToast({
          title: title,
          icon: icon,
        });
        setTimeout(function() {
          uni.hideToast();
        }, time);
      },
    
    check_account_mqtt_connect:function(){
      let that=this;
      const openid = uni.getStorageSync(&#39;openid&#39;);
      
       if (openid==&#39;&#39;) {
        uni.showToast({
          title:&#39;订阅消息连接失败&#39;,
        icon:&#39;none&#39;
        })
       }else{
         // 如果存在登录状态的信息,直接进行MQTT连接
         //构造必要数据
         let clientId = &#34;mqtt_&#34; + Math.random().toString(16).substr(2, 8)+openid;
         console.log(&#34;生成的随机clientId为:&#34; + clientId);
          this.globalData.options.clientId=clientId;
         this.globalData.onTopic=openid;//定义订阅消息
         that.connect();
       }
    },
  connect: function() {
    let that = this;
    var hosts = &#39;&#39;;
    // #ifdef APP-PLUS
  hosts = &#39;wx://&#39; + that.globalData.serve.host + &#39;:443/mqtt&#39;;
    // #endif
    
    // #ifdef H5
    hosts = &#39;wss://&#39; + that.globalData.serve.host + &#39;:443/mqtt&#39;;
    // hosts = &#39;wss://&#39; + that.globalData.serve.host + &#39;:443/mqtt&#39;;
   
     // hosts = &#39;tcp://&#39; + this.globalData.serve.host + &#39;:&#39; + this.globalData.serve.wsport + this.globalData.serve.path;
    //#endif
    // #ifdef MP-WEIXIN
    // wx仅仅可以使用体验版
  hosts = &#39;wxs://&#39; + that.globalData.serve.host + &#39;:443/mqtt&#39;;
    //#endif
    console.log(hosts);
    if (that.globalData.client == null || that.globalData.client.connented == false) {
      uni.showLoading({
        title: &#39;连接中···&#39;
      });
      that.globalData.client = mqtt.connect(
        hosts,
        that.globalData.options
      );
  
      that.globalData.client.on(&#39;connect&#39;, () =&gt; {
        uni.hideLoading();
        that.showToast(&#39;连接成功&#39;, 1000, &#39;success&#39;);
        that.subscribe();
      });
  
      that.globalData.client.on(&#39;message&#39;, (topic, message) =&gt; {
        console.log(&#39;收到来自&#39; + topic + &#39;的消息&#39; + message.toString());
        uni.showToast({
          title:&#39;收到一条消息:请在主页查收&#39;,
      duration:4000,
      icon:&#39;none&#39;
        })
        // 在收到消息时调用onMessageArrived回调函数进行处理
     
      });
    }
  
    that.globalData.client.on(&#39;reconnect&#39;, error =&gt; {
      uni.hideLoading();
      that.showToast(&#39;正在重连···&#39;, 1000);
    });
  
    that.globalData.client.on(&#39;error&#39;, error =&gt; {
      uni.hideLoading();
      that.showToast(&#39;连接失败!&#39;, 1000);
    });
  },

    

  },
  onShow: function() {
    console.log(&#39;App Show&#39;)
  },
  onHide: function() {
    console.log(&#39;App Hide&#39;)
  }
}

</script>

<style lang="scss">
/* ==== App.vue 文件 ==== /
/
为了避免电脑浏览器中的滚动条影响到布局,可在 style 标记中添加如下 CSS 代码*/

/* 条件编译,仅在H5平台生效 */
// #ifdef H5
body::-webkit-scrollbar,html::-webkit-scrollbar {
    display: none;
}
// #endif
  /*每个页面公共css */
  @import &#34;@/uni_modules/b-ui/css/main.bundle.scss&#34;;

</style>

注意:

App.vue和其他页面不一样,我也是弄了好久才弄清楚,另外使用了全局globalData才编译成小程序时最新版本会报错,获取不到,应该时BUG,我当时用的是基础组件2.33版本就解决了

二、PHP环境建立

下载文章顶部的配套资源到服务器

修改封装的代码里面的连接信息,以及数据持久化

代码语言:javascript
复制
<?php
require_once("php_mqtt/mqtt.class.php");//基础组件
function send_mqtt_message(receiver, content,conn,type)
{
$server = "mqtt.taila.club"; // 服务代理地址(mqtt服务端地址)
$port = 1883; // 通信端口
$username = ""; // 用户名(如果需要)
$password = ""; // 密码(如果需要)
$client_id = "clientx9293670xxctr_492344"; // 设置你的连接客户端id

$mqtt = new Mqtt($server, $port, $client_id); // 实例化MQTT
if ($mqtt-&gt;connect(true, NULL, $username, $password)) {
    // 如果创建链接成功
    $message = array(
        &#39;message_id&#39; =&gt; uniqid(), // 使用uniqid生成唯一的消息ID
        &#39;sender&#39; =&gt; &#39;153***9&#39;, // 消息的发送者,可以是用户ID或用户名
        &#39;receiver&#39; =&gt; $receiver, // 消息的接收者,可以是用户ID或用户名
        &#39;content&#39; =&gt; $content, // 消息的内容,可以是文本、图片、文件等
        &#39;timestamp&#39; =&gt; time(), // 消息的时间戳,记录消息的发送时间
        &#39;type&#39; =&gt; $type, // 消息的类型,用于区分不同类型的消息0系统消息
        &#39;status&#39; =&gt; &#39;0&#39;//0未读1已读
    );

    $json_message = json_encode($message); // PHP数组转换为JSON字符串

    $mqtt-&gt;publish(&#34;$receiver&#34;, $json_message, 2); // 发送JSON消息到主题 &#34;gg&#34;
    //持久化
   // $sql=&#34;INSERT INTO `message` (`message_id`, `sender`, `receiver`, `content`, `type`, `status`, `create_time`) VALUES (NULL, &#39;15368666279&#39;, &#39;$receiver&#39;, &#39;$content&#39;, &#39;$type&#39;, &#39;0&#39;, CURRENT_TIMESTAMP)&#34;;
   // $conn-&gt;query($sql);
    $mqtt-&gt;close(); // 发送后关闭链接
} else {
    echo &#34;Time out!\n&#34;;
}

}
?>

调用方式非常简单,新建index.php

代码语言:javascript
复制
<?php
include '../../api/conn.php';//连接数据库根据你情况来定
require_once("../../api/Message_push/mqtt_sender.php");

//消息发送
receiver = _GET["openid"];//发送手机号
content = _GET["msg"];//发送的消息
send_mqtt_message(receiver, content,$conn,'1');
?>

总结

以上就是今天要讲的内容,本文仅仅简单介绍了【MQTT5】原生PHP对接Uni H5、APP、微信小程序实时通讯消息服务的使用