一步步搭建物聯網系統——RESTful的CoAP協議

RESTful的CoAP協議

CoAP: 嵌入式系統的REST

引自維基百科上的介紹,用的是谷歌翻譯。。。javascript

受約束的應用協議(COAP)是一種軟件協議旨在以很是簡單的電子設備,使他們可以在互聯網上進行交互式通訊中使用。它特別針對小型低功率傳感器,開關,閥門和須要被控制或監督遠程,經過標準的Internet網絡相似的組件。 COAP是一個應用層協議,該協議是用於在資源受限的網絡鏈接設備,例如無線傳感器網絡節點使用。 COAP被設計爲容易地轉換爲HTTP與Web簡化集成,同時也能知足特殊的要求,例如多播支持,很是低的開銷,和簡單性。多播,低開銷,以及簡單性是因特網極其重要物聯網(IOT)和機器對機器(M2M)設備,這每每是積重難返,有太多的內存和電源,比傳統的互聯網設備有。所以,效率是很是重要的。 COAP能夠在支持UDP或UDP的模擬大多數設備上運行。html

簡單地來講,CoAP是簡化了HTTP協議的RESTful API,於是也只提供了REST的四個方法,即PUT,GET,POST和DELETE。對於微小的資源受限,在資源受限的通訊的IP的網絡,HTTP不是一種可行的選擇。它佔用了太多的資源和太多的帶寬。而對於物聯網這種嵌入式設備來講,關於資源與帶寬,是咱們須要優先考慮的內容。java

  • CoAP採用了二進制報頭,而不是文本報頭(text header)
  • CoAP下降了頭的可用選項的數量。
  • CoAP減小了一些HTTP的方法
  • CoAP能夠支持檢測裝置

CoAP 命令行工具

爲了測試測試咱們的代碼是不是正確工做,咱們須要一個CoAP的命令行工具。目前有兩個不錯的工具可使用。node

  • CoAP-cli,一個基於NodeJS的CoAP命令行工具,其核心是基於Node-CoAP庫。
  • libcooap,一個用C寫的CoAP命令行工具。
  • FireFox Copper, 一個Firefox的插件。

Node CoAP CLI

安裝命令以下git

bashnpm install coap-cli -g

CoAP命令行

在coap-cli中,一共有四個方法。分別表示REST的四種不一樣的方式:github

bashCommands:

get                    performs a GET request
put                    performs a PUT request
post                   performs a POST request
delete                 performs a DELETE request

在這裏,咱們用coap://vs0.inf.ethz.ch/來做一個簡單的測試sql

bashcoap get coap://vs0.inf.ethz.ch/
(2.05)  ************************************************************
I-D

測試一下如今的最小的物聯網系統CoAP版數據庫

bashcoap get coap://iot-coap.phodal.com/id/1
(2.05)  [{"id":1,"value":"is id 1","sensors1":19,"sensors2":20}]

libcoap

mac os libcoap安裝

Mac OS下能夠直接用npm

bashbrew install libcoap

Ubuntu libcoap安裝

Ubuntu GNU/Linux下json

Windows libcoap安裝

Windows 下

安裝完libcoap,咱們能夠直接用自帶的兩個命令

bashcoap-client 
coap-server

1.用coap-server啓一個CoAP服務

bashcoap-server

2.客戶端獲取數據

bashcoap-client -m get coap://localhost

返回結果

bashv:1 t:0 tkl:0 c:1 id:37109
This is a test server made with libcoap (see http://libcoap.sf.net) 
Copyright (C) 2010--2013 Olaf Bergmann <bergmann@tzi.org>

Firefox Copper

爲了能訪問coap://localhost/,因而咱們便須要安裝一個Firefox並安裝一個名爲Copper的插件。

  1. 下載地址: https://addons.mozilla.org/en-US/firefox/addon/copper-270430/

  2. 做爲測試咱們一樣能夠訪問 coap://vs0.inf.ethz.ch:5683/

CoAP Hello,World

接着咱們便開始試試作一個簡單的CoAP協議的應用:

這裏用到的是一個Nodejs的擴展Node-CoAP

node-coap is a client and server library for CoAP modelled after the http module.

Node-CoAP是一個客戶端和服務端的庫用於CoAP的模塊建模。建立一個package.json文件,添加這個庫

javascript{
  "dependencies":{
    "coap": "0.7.2"
  }
}

接着執行

bashnpm install

就能夠安裝好這個庫。若是遇到權限問題,請用

bashsudo npm install

接着,建立這樣一個app.js

javascriptconst coap        = require('coap')
    , server  = coap.createServer()

server.on('request', function(req, res) {
  res.end('Hello ' + req.url.split('/')[1] + '\n')
})

server.listen(function() {
  console.log('server started')
})

執行

bashnode app.js

即可以在瀏覽器上訪問了,由於如今什麼也沒有,因此什麼也不會返回。

接着下來再建立一個client端的js,並運行之

javascriptconst coap  = require('coap') 
    , req   = coap.request('coap://localhost/World')

req.on('response', function(res) {
  res.pipe(process.stdout)
})

req.end()

就能夠在console上輸出

bashHello World

也就達到了咱們的目的,用CoAP協議建立一個服務,接着咱們應該用它建立更多的東西,如產生JSON數據,以及RESTful。和HTTP版的最小物聯網系統同樣,CoAP版的最小物聯網系統也是要返回JSON的。

CoAP 數據庫查詢

Node Module

這說裏NodeJS Module的意義是由於咱們須要在別的地方引用到db_helper這個庫,也就是下一小節要的講的內容。

這樣咱們就能夠在server.js相似於這樣去引用這個js庫。

javascriptvar DBHelper = require('./db_helper.js');
DBHelper.initDB();

而這樣調用的前提是咱們須要去聲明這樣的module,爲了方便地導出函數功能調用。

javascriptfunction DBHelper(){
}
DBHelper.initDB = function(){};
module.exports = DBHelper;

Node-Sqlite3

此次咱們用的是SQLite3(你能夠用MySQL,出於安全考慮用SQLite3,SQLite3產生的是一個文件)。一個簡單的initDB函數

javascriptvar db = new sqlite3.Database(config["db_name"]);
var create_table = 'create table if not exists basic (' + config["db_table"] + ');';

db.serialize(function() {
    db.run(create_table);
    _.each(config["init_table"], function(insert_data) {
        db.run(insert_data);
    });
});
db.close();

首先從配置中讀取db_name,接着建立table,而後調用underscore的each方法,建立幾個數據。配置以下所示

javascriptconfig = {
    "db_name": "iot.db",
    "db_table": "id integer primary key, value text, sensors1 float, sensors2 float",
    "init_table":[
        "insert or replace into basic (id,value,sensors1,sensors2) VALUES (1, 'is id 1', 19, 20);",
        "insert or replace into basic (id,value,sensors1,sensors2) VALUES (2, 'is id 2', 20, 21);"
    ],
    "query_table":"select * from basic;"
};

而以前所提到的url查詢所作的事情即是

javascriptDBHelper.urlQueryData = function (url, callback) {
    var db = new sqlite3.Database("iot.db");
    var result = [];
    console.log("SELECT * FROM basic where " + url.split('/')[1] + "=" + url.split('/')[2]);
    db.all("SELECT * FROM basic where " + url.split('/')[1] + "=" + url.split('/')[2], function(err, rows) {
        db.close();
        callback(JSON.stringify(rows));
    });
};

將URL傳進來,便解析這個參數,接着再放到數據庫中查詢,再回調回結果。這樣咱們就能夠構成以前所說的查詢功能,而咱們所謂的post功能彷佛也能夠用一樣的方法加進去。

查詢數據

簡單地記錄一下在IoT-CoAP中一次獲取數據地過程。

先看看在示例中的Get.js的代碼,這關乎在後面server端的代碼。

javascriptconst coap       = require('coap')
     ,requestURI = 'coap://localhost/'
     ,url        = require('url').parse(requestURI + 'id/1/')
     ,req        = coap.request(url)
     ,bl         = require('bl');

req.setHeader("Accept", "application/json");
req.on('response', function(res) {
  res.pipe(bl(function(err, data) {
    var json = JSON.parse(data);
    console.log(json);
  }));

});
req.end();

const定義數據的方法,和咱們在其餘語言中有點像。只是這的const主要是爲了程序的健壯型,減小程序出錯,固然這不是javascript的用法。

咱們構建了一個請求的URL

bashcoap://localhost/id/1/

咱們對咱們的請求添加了一個Header,內容是Accept,值是'application/json'也就是JSON格式。接着,即是等待請求回來,再處理返回的內容。

判斷請求的方法

在這裏先把一些無關的代碼刪除掉,並保證其能工做,so,下面就是簡要的邏輯代碼。

javascriptvar coap            = require('coap');
var server          = coap.createServer({});
var request_handler = require('./request_handler.js');

server.on('request', function(req, res) {
    switch(req.method){
        case "GET": request_handler.getHandler(req, res);
            break;
    }
});

server.listen(function() {
    console.log('server started');
});

建立一個CoAP服務,判斷req.method,也就是請求的方法,若是是GET的話,就調用request_handler.getHandler(req, res)。而在getHandler裏,判斷了下請求的Accept

javascriptrequest_helper.getHandler = function(req, res) {
    switch (req.headers['Accept']) {
        case "application/json":
            qh.returnJSON(req, res);
            break;
        case "application/xml":
            qh.returnXML(req, res);
            break;
    }
};

若是是json剛調用returnJSON,

Database與回調

而這裏爲了處理回調函數剛分爲了兩部分

javascriptquery_helper.returnJSON = function(req, res) {
    DBHelper.urlQueryData(req.url, function (result) {
        QueryData.returnJSON(result, res);
    });
};

而這裏只是調用了

javascriptDBHelper.urlQueryData = function (url, callback) {
    var db = new sqlite3.Database(config["db_name"]);
    console.log("SELECT * FROM basic where " + url.split('/')[1] + "=" + url.split('/')[2]);
    db.all("SELECT * FROM basic where " + url.split('/')[1] + "=" + url.split('/')[2], function(err, rows) {
        db.close();
        callback(JSON.stringify(rows));
    });
};

這裏調用了node sqlite3去查詢對應id的數據,用回調處理了數據沒法到外部的問題,而上面的returnJSON則只是返回最後的結果,code以及其餘的內容。

javascriptQueryData.returnJSON = function(result, res) {
    if (result.length == 2) {
        res.code = '4.04';
        res.end(JSON.stringify({
            error: "Not Found"
        }));
    } else {
        res.code = '2.05';
        res.end(result);
    }
};

當resulst的結果爲空時,返回一個404,由於沒有數據。這樣咱們就構成了整個的鏈,再一步步返回結果。

IoT-CoAP中咱們使用到了一個Block2的東西,因而便整理相關的一些資料,做一個簡單的介紹,以及在代碼中的使用。

CoAP Block

CoAP是一個RESTful傳輸協議用於受限設備的節點和網絡。基本的CoAP消息是一個不錯的選擇對於小型載荷如

  • 溫度傳感器
  • 燈光開關
  • 樓宇自動化設備

然而,有時咱們的應用須要傳輸更大的有效載荷,如——更新固件。與HTTP,TCP作繁重工做將大型有效載荷分紅多個數據包,並確保他們全部到達並以正確的順序被處理。

CoAP是同UDP與DLTS同樣是基於數據報傳輸的,這限制了資源表示(resource representation)的最大大小,使得傳輸不須要太多的分割。雖然UDP支持經過IP分片傳輸更大的有效載荷,且僅限於64KiB,更重要的是,並無真正很好地約束應用和網絡。

而不是依賴於IP分片,這種規範基本COAP了對「塊」選項,用於傳輸信息從多個資源區塊的請求 - 響應對。在許多重要的狀況下,阻止使服務器可以真正無狀態:服務器能夠處理每塊分開傳輸,而無需創建鏈接之前的數據塊傳輸的其餘服務器端內存。

綜上所述,塊(Block)選項提供了傳送一個最小的在分塊的方式更大的陳述。

CoAP POST

看看在IoT CoAP中的post示例。

javascriptconst coap     = require('coap')
      ,request  = coap.request
      ,bl       = require('bl')
      ,req = request({hostname: 'localhost',port:5683,pathname: '',method: 'POST'});

req.setOption('Block2',  [new Buffer('1'),new Buffer("'must'"), new Buffer('23'), new Buffer('12')]);
req.setHeader("Accept", "application/json");
req.on('response', function(res) {
    res.pipe(bl(function(err, data) {
        console.log(data);
        process.exit(0);
    }));

});

req.end();

Block2中一共有四個數據,相應的數據結果應該是

javascript{ name: 'Block2', value: <Buffer 31> }
{ name: 'Block2', value: <Buffer 27 6d 75 73 74 27> }
{ name: 'Block2', value: <Buffer 32 33> }
{ name: 'Block2', value: <Buffer 31 32> }

這是沒有解析的Block2,簡單地能夠用

javascript_.values(e).toString()

將結果轉換爲

Block2,1
Block2,'must'
Block2,23
Block2,12

接着按","分開,

javascript_.values(e).toString().split(',')[1]

就有

bash[ '1', '\'must\'', '23', '12' ]

即可以很愉快地將其post到數據庫中了,

在作IoT-CoAP的過程當中只支持JSON,查閱CoAP的草稿時發現支持了諸多的Content Types。

CoAP Content Types

如下文字來自谷歌翻譯:

互聯網媒體類型是經過HTTP字符串標識,如「application/xml」。該字符串是由一個頂層的類型「applicaion」和子類型的「XML」。爲了儘可能減小使用這些類型的媒體類型來表示的開銷消息有效載荷,COAP定義一個標識符編碼方案互聯網媒體類型的子集。預計這桌將可擴展標識符的值的IANA維護。內容類型選項被格式化爲一個8位無符號整數。初始映射到一個合適的互聯網媒體類型標識符表所示。複合型高層次類型(multipart和不支持消息)。標識符值是從201-255保留的特定於供應商的,應用程序特定的或實驗使用和不禁IANA。

下面是HTTP的標識符及類型

Internet media type Identifier
text/plain (UTF-8) 0
text/xml (UTF-8) 1
text/csv (UTF-8) 2
text/html (UTF-8) 3
image/gif 21
image/jpeg 22
image/png 23
image/tiff 24
audio/raw 25
video/raw 26
application/link-format [I-D.ietf-core-link-format] 40
application/xml 41
application/octet-stream 42
application/rdf+xml 43
application/soap+xml 44
application/atom+xml 45
application/xmpp+xml 46
application/exi 47
application/x-bxml 48
application/fastinfoset 49
application/soap+fastinfoset 50
application/json 51

而在CoAP中只有簡單地幾個

Media type Encoding Id. Reference
text/plain; - 0 [RFC2046][RFC3676][RFC5147]
charset=utf-8
application/ - 40 [RFC6690]
link-format
application/xml - 41 [RFC3023]
application/ - 42 [RFC2045][RFC2046]
octet-stream
application/exi - 47 [EXIMIME]
application/json - 50 [RFC4627]

簡單地說就是:

諸如application/json的Content Types在CoAP中應該是50。如上表所示的結果是其對應的結果,這樣的話能夠減小傳遞的信息量。

CoAP JSON

因而在一開始的時候首先支持的即是"application/json"這樣的類型。

首先判斷請求的header

javascriptrequest_helper.getHandler = function(req, res) {
    switch (req.headers['Accept']) {
        case "application/json":
            qh.returnJSON(req, res);
            break;
        case "application/xml":
            qh.returnXML(req, res);
            break;
    }
};

再轉至相應的函數處理,而判斷的依據則是Accept是否是"application/json"。

javascriptregisterFormat('text/plain', 0)
registerFormat('application/link-format', 40)
registerFormat('application/xml', 41)
registerFormat('application/octet-stream', 42)
registerFormat('application/exi', 47)
registerFormat('application/json', 50)

對應地咱們須要在一發出請求的時候設置好Accept,要不就沒有辦法返回咱們須要的結果。

javascriptreq.setHeader("Accept", "application/json");

返回JSON

在給IoT CoAP添加了JSON支持以後,變得很是有意思,至少咱們能夠得到咱們想要的結果。在上一篇中咱們介紹了一些經常使用的工具——CoAP 命令行工具集

CoAP客戶端代碼

開始以前咱們須要有一個客戶端代碼,以便咱們的服務端能夠返回正確的數據並解析

javascriptvar coap = require('coap');
var requestURI = 'coap://localhost/';
var url = require('url').parse(requestURI + 'id/1/');
console.log("Request URL: " + url.href);
var req = coap.request(url);
var bl = require('bl');

req.setHeader("Accept", "application/json");
req.on('response', function(res) {
  res.pipe(bl(function(err, data) {
    var json = JSON.parse(data);
    console.log(json);
  }));

});

req.end();

代碼有點長內容也有點多,可是核心是這句話:

javascriptreq.setHeader("Accept", "application/json");

這樣的話,咱們只須要在咱們的服務端一判斷,

javascriptif(req.headers['Accept'] == 'application/json') {
     //do something
 };

這樣就能夠返回數據了

CoAP Server端代碼

Server端的代碼比較簡單,判斷一下

javascriptif (req.headers['Accept'] == 'application/json') {
        parse_url(req.url, function(result){
            res.end(result);
        });
        res.code = '2.05';
    }

請求的是不是JSON格式,再返回一個205,也就是Content,只是這時設計是請求一個URL返回對應的數據。如

bashcoap://localhost/id/1/

這時應該請求的是ID爲1的數據,即

javascript[ { id: 1, value: 'is id 1', sensors1: 19, sensors2: 20 }]

而parse_url只是從數據庫從讀取相應的數據。

javascriptfunction parse_url(url ,callback) {
    var db = new sqlite3.Database(config["db_name"]);
    var result = [];
    db.all("SELECT * FROM basic;", function(err, rows) {
        callback(JSON.stringify(rows));
    })
}

而且所有都顯示出來,設計得真是有點不行,不過如今已經差很少了。

在線查看:一步步搭建物聯網系統

圖靈-電子書版一步步搭建物聯網系統

相關文章
相關標籤/搜索