文章目錄
- 前言
- KOA2框架
- koa2初步使用
- koa示例代碼解釋
- koa-static中間件
- Aedes
- mqtt協議
- 用aedes創建broker并測驗
- MQTT server over WebSocket
- http協議和websocket
- mqtt over websocket
- 后端
- 前端腳本
- 測驗
前言
我們已經搭建好了開發環境,并且已經設計好了UI界面,現在就要正式撰寫后端軟體,來進行資料互動,先來介紹這些第三方依賴包,
KOA2框架
基于nodejs的web框架有很多,討論最多的就是express和koa2,express生態最好,用的最多,而koa2是express的原班人馬打造,相當于express的升級版,我個人的原則是:學就學最新的,除非學不會,所以我選用koa2框架,
推薦B站這個教程,koa教程,說實話因為之前沒接觸過web相關的知識,理解這些路由和中間件等概念,還是讓我頭痛了一陣,就算現在我也不能保證我理解的完全正確,但基本能實作我現在想要的功能,歡迎指正批評,而且想學好KOA和nodejs還是要花費很多功夫的,我目前這樣的淺嘗輒止的程度還是不夠的,有機會繼續深入研究,
在這篇文章中不系統總結(還沒學到位),只解釋我用到的一些東西,
koa2初步使用
我們現在專案目錄中新建一個index.js檔案,然后鍵入以下代碼
//引入Koa
const koa=require('koa');
const app=new koa();
//配置中間件
app.use( async (ctx)=>{
ctx.body='hello koa2'
})
//監聽埠
app.listen(3000);
//本地終端列印log
console.log('[demo] start-quick is starting at port 3000')
然后在瀏覽器中訪問localhost:3000或者127.0.0.1:3000(一個意思),可以看到,字串’hello koa2’就出現在網頁中了,
PS :
- javascript的字串可以用單引號也可以用雙引號,
- javascript每條陳述句可以不加分號,只敲回車,也能正常運行,但最好加,因為不是不需要分好,而是JS剖析器會在執行期間自動幫你插入分號,
koa示例代碼解釋
引入模塊用require,require是CommonJS規范引入模塊的方法,而對于最新的ES6規范,引入模塊的方法是import,關于這兩種規范引入模塊方法的錯雜和歷史以后我有機會再整理,我們只跟著npm官網,包的使用教程來使用,npm官網koa,官網怎么用,我們怎么用,參考模塊后,再用new關鍵字,對模塊進行實體化,

//配置中間件
app.use( async (ctx)=>{
ctx.body='hello koa2'
})
這里的中間件的概念,官方沒給定義,koa中文網給的解釋是
中間件就是匹配路由之前或者匹配路由完成做的一系列的操作,我們就可以把它叫做中間件
我的理解是,中間件就是一個插件,也可以理解為一個函式,當用戶請求時,要經過中間件的處理,生成回應時也要經過中間件的處理,處理順序符合洋蔥模型詳細解釋,

和=>這種匿名函式的寫法,都請移步到B站的視頻學習koa教程,
最后使用app.listen方法,對3000埠進行監聽,這個函式必須呼叫,否則瀏覽器訪問網址時,發出的GET、POST請求都無法回應,那么我的網址也是無法訪問的,
//監聽埠
app.listen(3000);
要注意nodejs的編程,不同于C或其他的一些語言順序性、程序性比較強,nodejs更偏向于事件驅動,函式呼叫也都是異步的,如果想同步,順序的執行某些回呼函式,就得用async和await的組合,
koa-static中間件
koa-static是一個用來加載靜態資源的中間件,我們第一章寫的index.html檔案想要它通過訪問網址,加載到網頁上,就需要用koa-static這個中間件,首先我們去看npm官網的例子
const serve = require('koa-static');
const Koa = require('koa');
const app = new Koa();
// $ GET /package.json
app.use(serve('.'));
// $ GET /hello.txt
app.use(serve('test/fixtures'));
// or use absolute paths
app.use(serve(__dirname + '/test/fixtures'));
app.listen(3000);
console.log('listening on port 3000');
上面的官方例子代表了koa-static中間件訪問靜態資源的三種情況,第一種訪問當前目錄,在當前目錄找index.html這個檔案,然后顯示到網頁中去,第二種訪問相對路徑,test/fixtures檔案夾下找index.html這個檔案,第三種絕對路徑,__dirname是nodejs全域物件,代表的是當前目錄,
Aedes
Aedes是一個Stream-based MQTT broker,基于流的MQTT代理,所謂broker(代理),可以理解為服務器,所有的mqtt節點的話題發布和訂閱都要經過broker,關于MQTT代理,還有一個包是mosca,也能起到相同的作用,mosca和Aedes是同一個人開發的,github網址moscajs,只不過mosca已經不維護了,Aedes是mosca的升級版本,
mqtt協議
MQTT(Message Queuing Telemetry Transport,訊息佇列遙測傳輸協議),是一種基于發布/訂閱(publish/subscribe)模式的"輕量級"通訊協議,該協議構建于TCP/IP協議上,由IBM在1999年發布,MQTT最大優點在于,可以以極少的代碼和有限的帶寬,為連接遠程設備提供實時可靠的訊息服務,作為一種低開銷、低帶寬占用的即時通訊協議,使其在物聯網、小型設備、移動應用等方面有較廣泛的應用,
mqtt的協議的作業方式就如下圖,broker類似于一個平臺,任何機器都可以在平臺上發布話題(Publish),任何機器也可以訂閱想要看到的話題(Subscribe),這要就能使得每個支持mqtt協議的設備網路互聯,實作所謂的萬物互聯,這里有一個Qos(服務質量等級)的概念,有Qos0、Qos1、Qos2三個等級,
| 質量等級 | 解釋 |
|---|---|
| Qos0 | 發送者只發一次 |
| Qos1 | 發送者至少發送一次,確保broker收到 |
| Qos2 | 發送者至少發送一次,確保broker收到,且只收到一次 |
這里的Qos2只是理想,并沒有broker支持,

用aedes創建broker并測驗
參考github的aedes官方庫的示例鏈接,我們直接在index.js敲以下代碼,然后在終端啟動,命令
node .\index.js,
const aedes = require('aedes')()
const server = require('net').createServer(aedes.handle)
const port = 1883
server.listen(port, function () {
console.log('server started and listening on port ', port)
})
這里我們已經啟動了一個broker,我們再利用工具來測驗一下,windows10電腦端,在應用商店下載MQTTBox這個應用,點擊creat mqtt client后,填入我們的Ip地址和埠號,協議選擇mqtt/tcp,

保存設定后,來到下面這個頁面,左邊發布個mytopic話題,填入資料,右邊我們訂閱自己發布的話題,點擊發布,就可以收到資訊了,

當然,我們也可以用其他客戶端進行訂閱,在手機應用商店搜索mqtt,我用的是EasyMQTT這個APP,填入broker的ip和埠號,手機同樣能收到MQTTBox發布的mytopic話題的資訊,

MQTT server over WebSocket
我們已經搭建出了mqtt的broker,現在就要考慮怎么將mqtt的話題里的payload傳遞到web端,
http協議和websocket
web網頁常用的通訊協議是HTTP和websocket,像我們前面使用koa中間件發送"hello koa2"字串,雖然這里面經過了koa2的封裝,我們看不到http協議的請求和回應,但這實際上就是一個基于http協議的通訊,
例如這個例子,我們用瀏覽器訪問3000埠,發現我們的請求時GET,response是"hello koa2",

response是"hello koa2"

ps: 這里的favicon.ico,是去請求小圖示,小圖示就是瀏覽器每個標簽上左側的小圖示,

包括后面koa-static中間件,也是封裝了http協議,去請求一個靜態的頁面,HTTP協議是一個單向協議,客戶端放松請求(GET/POST),服務器發送回應,當連接的速度要求不高和只請求一次的場景下,使用HTTP協議, 當需要服務器主動向客戶端推送訊息,并且連接速度需要比較快,資料需要經常更新的場景下,我們使用的就應該是websocket協議了,
mqtt over websocket
對于我們這個場景,每個nodemcu都是一個mqtt客戶端,web網頁也應該是一個mqtt客戶端,nodejs既是mqtt的broker(代理),也是web網頁的服務器,用nodejs 的好處就凸顯出來了,我們可以將mqtt的broker和web服務器,寫在同一個nodejs腳本里,首先我們看官方例程,

例程中的ws.createServer({server:httpServer},aedes.handle),我在其git倉庫中沒有找到回應的解釋,但通過試驗,其功能應該是將mqtt協議的資訊流傳給websocket的資訊流,
ps:var=require('模塊')()等價于先參考模塊,再實體化,但并不是所有的模塊都支持這種寫法,應該與封裝匯出的方式相關,
//下面兩行等效于
aedes=require('aedes')();
// const Aedes = require('aedes');
// var aedes=new Aedes();
后端
koa、koa-static、aedes、websocket-stream這三個依賴包都講了下,可以開始寫我們的nodejs程式了,
這里我們用到3個埠,1883埠用于mqtt broker,8888用于http模塊創建的websocket,5000埠用于koa2創建的http服務器,其實應該可以直接用koa2框架里的工具來創建websocket的,但是我這里暫時是修改的官方例子,沒有去探究怎么用koa2來實作websocket通訊,
其實這里面有兩個http服務器,一個是8888埠,只用于websoket的,一個是5000埠,用于koa2加載靜態資源的,有時間,會去把他們簡化一些,
const Koa = require('koa');
const static = require('koa-static');
//const path = require('path');
//--------------------------------------------//
//下面兩行等效于
aedes=require('aedes')();
// const Aedes = require('aedes');
// var aedes=new Aedes();
//--------------------------------------------//
//-----------------------MQTT broker creat-----------------------
const broker = require('net').createServer(aedes.handle);
const httpServer = require('http').createServer();
const ws = require('websocket-stream');
const port = 1883;
const wsPort = 8888;
broker.listen(port, function () {
console.log('broker listening on port', port)
});
ws.createServer({
server: httpServer
}, aedes.handle);
httpServer.listen(wsPort, function () {
console.log('websocket server listening on port', wsPort)
});
//身份驗證
aedes.authenticate = function (client, username, password, callback) {
callback(null, (username === 'user' && password.toString() === '123456'));
};
// 客戶端連接
aedes.on('client', function (client) {
console.log('Client Connected: \x1b[33m' + (client ? client.id : client) + '\x1b[0m', 'to broker', aedes.id);
});
// 客戶端斷開
aedes.on('clientDisconnect', function (client) {
console.log('Client Disconnected: \x1b[31m' + (client ? client.id : client) + '\x1b[0m', 'to broker', aedes.id);
});
//-----------------------MQTT broker creat-----------------------
var app = new Koa();
//const staticPath = './static';
//app.use(static(path.join(__dirname, staticPath)));
app.use(static('./static'));
app.listen(5000, () => { console.log('server start on port 5000'); });
至此,我們的后端和前端就都搭建好了,可以先脫離nodemcu硬體,用MQTTBox或者手機端的進行資料互動的測驗,
前端腳本
第一章我們我們搭建好了UI,引入了兩個腳本,其中mqtt.min.js是在npm庫unpkg網站上下載的,當然我們也可以用下面注釋的方式來參考它,這個腳本的作用,是讓我們可以在前端的javascript腳本里使用mqtt協議,
<!-- <script src="https://unpkg.com/mqtt@4.2.8/dist/mqtt.min.js"></script> -->
<script src="./mqtt.min.js"></script>
<script src="./webclient.js" charset="utf-8"></script>
參考完mqtt.min.js的腳本后,我們就可以在javascript腳本里使用mqtt腳本的方法了,
注意,腳本第一行的mqtt.connect函式里填寫的url可以是多種形式的,這里推薦ws://協議的形式,當然也可以是mqtt://192.168.3.113:8888,但如果是大家用的也是mqtt@4.2.8版本的,就會報錯,這個錯是官方的錯,見該博客,其實修改成4.0.0版本的mqtt.min.js版本就不會有問題,其實不管什么url,連的其實還是websocket的埠,

webclient.js
const client = mqtt.connect('ws://192.168.3.113:8888', {
username: "user",
password: '123456'
});
// const client = mqtt.connect('mqtt://192.168.3.113:1883', {
// username: "user",
// password: '123456'
// });
client.subscribe("carinfo");
client.subscribe("steerbutton");
client.on("connect", function () {
console.log("服務器連接成功");
console.log(client.options.clientId);
client.subscribe("carinfo", { qos: 1 }); // 訂閱text訊息
});
client.on("message", function (top, message) {
console.log("當前topic:", top);
console.log(message.toString());
let messagestring=message.toString();
let stringafersplit=messagestring.split(",");
let throttle=stringafersplit[0];
let brake=stringafersplit[1];
let steer=stringafersplit[2];
let rotationspeed=stringafersplit[3]
let SOC=stringafersplit[4]
document.getElementById('throttle').innerHTML = throttle + '%';
document.getElementById('brake').innerHTML = brake + '%';
document.getElementById('steer').innerHTML = steer + "°";
document.getElementById('rotationspeed').innerHTML = rotationspeed + "rpm";
document.getElementById('SOC').innerHTML=SOC+'%';
});
// setInterval(() => {
// steer = steer + 1;
// document.getElementById('throttle').innerHTML = throttle + '%';
// document.getElementById('brake').innerHTML = brake + '%';
// document.getElementById('steer').innerHTML = steer + "°";
// }, 10)
原始碼如上,先連上broker,然后訂閱carinfo這個話題,在message的事件中,我們利用DOM對html中的內容進行賦值DOM教程
測驗
我們先用MQTTBox工具進行測驗,首先在終端開啟服務器,

可以看到,三個埠都在正常監聽的狀態,然后我們在瀏覽器中訪問,192.168.3.113:5000這個地址,當然任何連如同一個網路的電腦和手機都可以訪問,網頁如下,

再打開MQTTBox,配置如下,

此時可以在服務器的終端看到,兩個mqtt客戶端已經連上了broker,

這里我們通過MQTTBox,發布carinfo話題,payload隨便填一些資訊,點擊publish.
網頁端可以看到,我們發送的資訊已經在網頁端更新了,

到此為止我們已經完成了后端和前端的代碼,只剩下嵌入式設備的接入了,原始碼鏈接,
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/295375.html
標籤:其他
