最近在uniapp打包成微信小程序的項目中第一次用到了MQTT。使用比較簡單,可是仍是遇到了一些問題。在此記錄一下。html
官方文檔:MQTT Github前端
官方MQTT測試工具:MQTTX。測試工具使用說明python
MQTT的js文件:mqtt.min.jsgit
先上一點注意事項:github
(1) MQTT.js 一個 MQTT 協議的客戶端庫,用 JavaScript 編寫,可用於 Node.js 和瀏覽器。在 Node.js 端能夠經過全局安裝使用命令行鏈接,同時還支持 MQTT ,MQTT TLS 證書鏈接;值得一提的是 MQTT.js 還對微信小程序有較好的支持。web
(2) MQTT能夠經過三種方式鏈接,TCP直連, TLS和WebSocket,可是JavaScript使用 TCP 只能經過 ws 即 Websocket 連接。因此若是服務器是TCP直連,客戶端是確定連不上的,會報 Error in connection establishment 的錯誤。因此客戶端協議只能是ws,wss,wxs(微信)小程序
(3) WebSocket 是一種在單個 TCP 鏈接上進行全雙工通信的協議。做爲一種通訊協議,其使用 ws(非加密)、wss(SSL 加密) 做爲協議標識。MQTT.js 客戶端支持多種協議,鏈接地址需指明協議類型;微信小程序
(4) ws: 未加密的 WebSocket 鏈接,通常使用8083端口。wss: 加密的 WebSocket 鏈接,通常使用8084端口。mqtt: 未加密的 TCP 鏈接,通常使用1883端口。mqtts: 加密 TCP 鏈接。瀏覽器
貼一下代碼:服務器
import $mqtt from './mqtt.min.js';
const mqttOptions = { keepalive: 30, clean: false, connectTimeout: 5000, // Timeout clientId: uni.getStorageSync('clientId'), // username: 'test', // password: 'test', } const connectUrl = `${mqttHost}:${mqttPort}/mqtt`; // #ifdef H5 var client = $mqtt.connect('wss://' + connectUrl, mqttOptions); // #endif // #ifdef MP-WEIXIN||APP-PLUS var client = $mqtt.connect('wxs://' + connectUrl, mqttOptions); // #endif client.on('connect', () => { console.log('connect')
// 這是爲了給本身發條消息,其它無做用 client.subscribe('test', (err) => { if (!err) { client.publish('test', '{}') } }) }); // 自動重連 client.on('reconnect', (msg) => { console.log('reconnect', msg) }); // 錯誤 client.on('error', () => { console.log('error') }); // 斷開 client.on('end', () => { console.log('end') }); // 掉線 client.on('offline', (msg) => { console.log('offline',msg) }); // 收到消息 client.on('message', (topic, message) => { // 把arrayBuffer轉成字符串 let encodedString = String.fromCharCode.apply(null, new Uint8Array(message));// 全局發送消息 uni.$emit('sendTopicMsg',encodedString);
console.log(encodedString) }) // 全局監聽是否有關閉mqtt的消息的事件 uni.$on('closeMqtt',() => { client.end(true); // 主動斷開鏈接 })
說明:
(1) 配置項裏的 keepAlive 指的是心跳時間。以秒爲單位。定義服務端從客戶端接收到消息的最大間隔時間。能夠設置爲0,表示客戶端一直不斷開,除非主動斷開。
(2) clean 設置爲false,是爲了讓客戶端掉線的時候,服務器必須在客戶端斷開以後繼續存儲/保持客戶端的訂閱狀態。即當爲true的時候,若是掉線了,服務端會清理連接狀態的數據和內容。當爲false的時候,服務端會保存消息發送期間,連接斷開致使發送失敗的消息。這樣連上的時候就會自動推送到訂閱的客戶端。
(3) 若是鏈接須要驗證用戶名和密碼,則須要加上username和password字段。
(4) 微信小程序使用的協議,若是不是加密的,則是 wss,若是是加密的,就是 wxs。web端,通常不加密就是 wx,加密就是wss。
(5) 前端收到的消息是 arrayBuffer 格式的,須要轉成字符串格式,若是帶有中文,可能轉成字符串會亂碼。可使用 let decodeString = decodeURIComponent(escape((encodedString))) 來避免中文亂碼。或者是網上經過移位轉成中文的方法來解決。
(6) clientId 是使用 Math.random().toString(36).substr(3,自定義長度) 來生成自定義長度的的惟一id。
應該是免費的測試MQTT消息的連接: o(∩_∩)o
協議 | 地址 | 端口 | 路徑 | 證書 |
mqtt | broker.hivemq.com | 8000 | 無 | 無 |
mqtt | broker.emqx.io | 1883 | 無 | 無 |
ws | test.mosquitto.org | 8080 | /mqtt | 無 |
wss | test.mosquitto.org | 8081 | /mqtt | CA signed server |
(1) CA signed server 這個直接在MQTTX測試工具裏面 選擇 SSL/TLS 勾選爲true時便可選擇。
(2) 測試的時候,先填好內容,而後連接上的時候,再添加一個訂閱,Topic就填一會你須要發送消息的Topic,這樣當你模擬服務器發送消息的時候,MQTTX裏面也會收到你剛纔發送的內容,這樣就是連通了。
開發過程當中遇到的問題:
(1) 若是出現 failed: Connection closed before receiving a handshake response 這個錯誤。說明 服務端的 mqtt 協議和客戶端的協議不同,就好比 python用的是基於 tcp 的 mqtt ,js是基於websocket 的 mqtt ,都不能達成握手的操做。出現這個的問題應該是:服務端使用了 1883 這個端口,而客戶端也是用這個端口,就致使這個問題。正確的應該是:客戶端(js)應該使用 8083 端口(未加密)。
(2) 最初我設置心跳時間是3秒,在微信開發者工具,或者H5端的時候,是沒有問題的。可是在小程序真機調試裏,當小程序切換到後臺,差很少5s就會自動斷開,而後一直重連,直到切換回小程序,重連成功。並且有時候還會連續斷開和重連。自動斷網這個緣由,小程序官網作了說明的:超過5s斷網說明 在第二條 網絡請求 - 使用限制裏面就說明了,小程序的機制,限制了切換到後臺以後網絡的處理。因此把心跳時間改成30s,讓 mqtt 30秒以後再自動重連。可是這樣只是爲了讓體驗稍微好一點,不會出現切換出去5s就斷開連接,再切回來一直提示重連的問題。不過能夠把心跳時間改成0,表示一直鏈接不斷開。
總結:
(1)以上對mqtt的瞭解也只是只知其一;不知其二,還沒弄明白這裏的心跳時間,和 TCP/IP 的心跳機制是否是同樣的。超過心跳時間,能不能讓客戶端不掉線,或者有沒有更好的解決離線的辦法。
(2)並且還有不少特性都沒用到,好比will遺願標誌,專門用來處理客戶端斷開鏈接的配置項。
(3)因爲這個項目都是用的Qos0的消息,都不知道與Qos1,Qos2的區別是什麼。
先暫時記錄到這裏,後面弄懂了再補充。