引自維基百科上的介紹,用的是谷歌翻譯。。。
受約束的應(yīng)用協(xié)議(COAP)是一種軟件協(xié)議旨在以非常簡單的電子設(shè)備,使他們能夠在互聯(lián)網(wǎng)上進(jìn)行交互式通信中使用。它特別針對小型低功率傳感器,開關(guān),閥門和需要被控制或監(jiān)督遠(yuǎn)程,通過標(biāo)準(zhǔn)的Internet網(wǎng)絡(luò)類似的組件。 COAP是一個(gè)應(yīng)用層協(xié)議,該協(xié)議是用于在資源受限的網(wǎng)絡(luò)連接設(shè)備,例如無線傳感器網(wǎng)絡(luò)節(jié)點(diǎn)使用。 COAP被設(shè)計(jì)為容易地轉(zhuǎn)換為HTTP與Web簡化集成,同時(shí)也能滿足特殊的要求,例如多播支持,非常低的開銷,和簡單性。多播,低開銷,以及簡單性是因特網(wǎng)極其重要物聯(lián)網(wǎng)(IOT)和機(jī)器對機(jī)器(M2M)設(shè)備,這往往是積重難返,有太多的內(nèi)存和電源,比傳統(tǒng)的互聯(lián)網(wǎng)設(shè)備有。因此,效率是非常重要的。 COAP可以在支持UDP或UDP的模擬大多數(shù)設(shè)備上運(yùn)行。
簡單地來說,CoAP是簡化了HTTP協(xié)議的RESTful API,因而也只提供了REST的四個(gè)方法,即PUT,GET,POST和DELETE。對于微小的資源受限,在資源受限的通信的IP的網(wǎng)絡(luò),HTTP不是一種可行的選擇。它占用了太多的資源和太多的帶寬。而對于物聯(lián)網(wǎng)這種嵌入式設(shè)備來說,關(guān)于資源與帶寬,是我們需要優(yōu)先考慮的內(nèi)容。
為了測試測試我們的代碼是否是正確工作,我們需要一個(gè)CoAP的命令行工具。目前有兩個(gè)不錯(cuò)的工具可以使用。
安裝命令如下
npm install coap-cli -g
在coap-cli中,一共有四個(gè)方法。分別表示REST的四種不同的方式:
Commands:
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/來作一個(gè)簡單的測試
coap get coap://vs0.inf.ethz.ch/
(2.05) ************************************************************
I-D
測試一下現(xiàn)在的最小的物聯(lián)網(wǎng)系統(tǒng)CoAP版
coap get coap://iot-coap.phodal.com/id/1
(2.05) [{"id":1,"value":"is id 1","sensors1":19,"sensors2":20}]
Mac OS下可以直接用
brew install libcoap
Ubuntu GNU/Linux下
Windows 下
安裝完libcoap,我們可以直接用自帶的兩個(gè)命令
coap-client
coap-server
1.用coap-server啟一個(gè)CoAP服務(wù)
coap-server
2.客戶端獲取數(shù)據(jù)
coap-client -m get coap://localhost
返回結(jié)果
v: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>
為了能訪問coap://localhost/,于是我們便需要安裝一個(gè)Firefox并安裝一個(gè)名為Copper的插件。
下載地址:?https://addons.mozilla.org/en-US/firefox/addon/copper-270430/
接著我們便開始試試做一個(gè)簡單的CoAP協(xié)議的應(yīng)用:
這里用到的是一個(gè)Nodejs的擴(kuò)展Node-CoAP
node-coap is a client and server library for CoAP modelled after the http module.
Node-CoAP是一個(gè)客戶端和服務(wù)端的庫用于CoAP的模塊建模。創(chuàng)建一個(gè)package.json文件,添加這個(gè)庫
{
"dependencies":{
"coap": "0.7.2"
}
}
接著執(zhí)行
npm install
就可以安裝好這個(gè)庫。如果遇到權(quán)限問題,請用
sudo npm install
接著,創(chuàng)建這樣一個(gè)app.js
const 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')
})
執(zhí)行
node app.js
便可以在瀏覽器上訪問了,因?yàn)楝F(xiàn)在什么也沒有,所以什么也不會(huì)返回。
接著下來再創(chuàng)建一個(gè)client端的js,并運(yùn)行之
const coap = require('coap')
, req = coap.request('coap://localhost/World')
req.on('response', function(res) {
res.pipe(process.stdout)
})
req.end()
就可以在console上輸出
Hello World
也就達(dá)到了我們的目的,用CoAP協(xié)議創(chuàng)建一個(gè)服務(wù),接著我們應(yīng)該用它創(chuàng)建更多的東西,如產(chǎn)生JSON數(shù)據(jù),以及RESTful。和HTTP版的最小物聯(lián)網(wǎng)系統(tǒng)一樣,CoAP版的最小物聯(lián)網(wǎng)系統(tǒng)也是要返回JSON的。
這說里NodeJS Module的意義是因?yàn)槲覀冃枰趧e的地方引用到db_helper這個(gè)庫,也就是下一小節(jié)要的講的內(nèi)容。
這樣我們就可以在server.js類似于這樣去引用這個(gè)js庫。
var DBHelper = require('./db_helper.js');
DBHelper.initDB();
而這樣調(diào)用的前提是我們需要去聲明這樣的module,為了方便地導(dǎo)出函數(shù)功能調(diào)用。
function DBHelper(){
}
DBHelper.initDB = function(){};
module.exports = DBHelper;
這次我們用的是SQLite3(你可以用MySQL,出于安全考慮用SQLite3,SQLite3產(chǎn)生的是一個(gè)文件)。一個(gè)簡單的initDB函數(shù)
var 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,接著創(chuàng)建table,然后調(diào)用underscore的each方法,創(chuàng)建幾個(gè)數(shù)據(jù)。配置如下所示
config = {
"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查詢所做的事情便是
DBHelper.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傳進(jìn)來,便解析這個(gè)參數(shù),接著再放到數(shù)據(jù)庫中查詢,再回調(diào)回結(jié)果。這樣我們就可以構(gòu)成之前所說的查詢功能,而我們所謂的post功能似乎也可以用同樣的方法加進(jìn)去。
簡單地記錄一下在IoT-CoAP中一次獲取數(shù)據(jù)地過程。
先看看在示例中的Get.js的代碼,這關(guān)乎在后面server端的代碼。
const 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定義數(shù)據(jù)的方法,和我們在其他語言中有點(diǎn)像。只是這的const主要是為了程序的健壯型,減少程序出錯(cuò),當(dāng)然這不是javascript的用法。
我們構(gòu)建了一個(gè)請求的URL
coap://localhost/id/1/
我們對我們的請求添加了一個(gè)Header,內(nèi)容是Accept,值是'application/json'也就是JSON格式。接著,便是等待請求回來,再處理返回的內(nèi)容。
判斷請求的方法
在這里先把一些無關(guān)的代碼刪除掉,并保證其能工作,so,下面就是簡要的邏輯代碼。
var 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');
});
創(chuàng)建一個(gè)CoAP服務(wù),判斷req.method,也就是請求的方法,如果是GET的話,就調(diào)用request_handler.getHandler(req, res)。而在getHandler里,判斷了下請求的Accept
request_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剛調(diào)用returnJSON,
Database與回調(diào)
而這里為了處理回調(diào)函數(shù)剛分為了兩部分
query_helper.returnJSON = function(req, res) {
DBHelper.urlQueryData(req.url, function (result) {
QueryData.returnJSON(result, res);
});
};
而這里只是調(diào)用了
DBHelper.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));
});
};
這里調(diào)用了node sqlite3去查詢對應(yīng)id的數(shù)據(jù),用回調(diào)處理了數(shù)據(jù)無法到外部的問題,而上面的returnJSON則只是返回最后的結(jié)果,code以及其他的內(nèi)容。
QueryData.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);
}
};
當(dāng)resulst的結(jié)果為空時(shí),返回一個(gè)404,因?yàn)闆]有數(shù)據(jù)。這樣我們就構(gòu)成了整個(gè)的鏈,再一步步返回結(jié)果。
在IoT-CoAP中我們使用到了一個(gè)Block2的東西,于是便整理相關(guān)的一些資料,作一個(gè)簡單的介紹,以及在代碼中的使用。
CoAP是一個(gè)RESTful傳輸協(xié)議用于受限設(shè)備的節(jié)點(diǎn)和網(wǎng)絡(luò)?;镜腃oAP消息是一個(gè)不錯(cuò)的選擇對于小型載荷如
然而,有時(shí)我們的應(yīng)用需要傳輸更大的有效載荷,如——更新固件。與HTTP,TCP做繁重工作將大型有效載荷分成多個(gè)數(shù)據(jù)包,并確保他們所有到達(dá)并以正確的順序被處理。
CoAP是同UDP與DLTS一樣是基于數(shù)據(jù)報(bào)傳輸?shù)?,這限制了資源表示(resource representation)的最大大小,使得傳輸不需要太多的分割。雖然UDP支持通過IP分片傳輸更大的有效載荷,且僅限于64KiB,更重要的是,并沒有真正很好地約束應(yīng)用和網(wǎng)絡(luò)。
而不是依賴于IP分片,這種規(guī)范基本COAP了對“塊”選項(xiàng),用于傳輸信息從多個(gè)資源區(qū)塊的請求 - 響應(yīng)對。在許多重要的情況下,阻止使服務(wù)器能夠真正無狀態(tài):服務(wù)器可以處理每塊分開傳輸,而無需建立連接以前的數(shù)據(jù)塊傳輸?shù)钠渌?wù)器端內(nèi)存。
綜上所述,塊(Block)選項(xiàng)提供了傳送一個(gè)最小的在分塊的方式更大的陳述。
看看在IoT CoAP中的post示例。
const 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中一共有四個(gè)數(shù)據(jù),相應(yīng)的數(shù)據(jù)結(jié)果應(yīng)該是
{ 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,簡單地可以用
_.values(e).toString()
將結(jié)果轉(zhuǎn)換為
Block2,1 Block2,'must' Block2,23 Block2,12
接著按","分開,
_.values(e).toString().split(',')[1]
就有
[ '1', '\'must\'', '23', '12' ]
便可以很愉快地將其post到數(shù)據(jù)庫中了,
在做IoT-CoAP的過程中只支持JSON,查閱CoAP的草稿時(shí)發(fā)現(xiàn)支持了諸多的Content Types。
以下文字來自谷歌翻譯:
互聯(lián)網(wǎng)媒體類型是通過HTTP字符串標(biāo)識(shí),如“application/xml”。該字符串是由一個(gè)頂層的類型“applicaion”和子類型的“XML”。為了盡量減少使用這些類型的媒體類型來表示的開銷消息有效載荷,COAP定義一個(gè)標(biāo)識(shí)符編碼方案互聯(lián)網(wǎng)媒體類型的子集。預(yù)計(jì)這桌將可擴(kuò)展標(biāo)識(shí)符的值的IANA維護(hù)。內(nèi)容類型選項(xiàng)被格式化為一個(gè)8位無符號(hào)整數(shù)。初始映射到一個(gè)合適的互聯(lián)網(wǎng)媒體類型標(biāo)識(shí)符表所示。復(fù)合型高層次類型(multipart和不支持消息)。標(biāo)識(shí)符值是從201-255保留的特定于供應(yīng)商的,應(yīng)用程序特定的或?qū)嶒?yàn)使用和不由IANA。
下面是HTTP的標(biāo)識(shí)符及類型
|
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中只有簡單地幾個(gè)
|
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中應(yīng)該是50
。如上表所示的結(jié)果是其對應(yīng)的結(jié)果,這樣的話可以減少傳遞的信息量。
于是在一開始的時(shí)候首先支持的便是"application/json"這樣的類型。
首先判斷請求的header
request_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;
}
};
再轉(zhuǎn)至相應(yīng)的函數(shù)處理,而判斷的依據(jù)則是Accept是不是"application/json"。
registerFormat('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)
對應(yīng)地我們需要在一發(fā)出請求的時(shí)候設(shè)置好Accept,要不就沒有辦法返回我們需要的結(jié)果。
req.setHeader("Accept", "application/json");
返回JSON
在給IoT CoAP添加了JSON支持之后,變得非常有意思,至少我們可以獲得我們想要的結(jié)果。在上一篇中我們介紹了一些常用的工具——CoAP 命令行工具集。
CoAP客戶端代碼
開始之前我們需要有一個(gè)客戶端代碼,以便我們的服務(wù)端可以返回正確的數(shù)據(jù)并解析
var 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();
代碼有點(diǎn)長內(nèi)容也有點(diǎn)多,但是核心是這句話:
req.setHeader("Accept", "application/json");
這樣的話,我們只需要在我們的服務(wù)端一判斷,
if(req.headers['Accept'] == 'application/json') {
//do something
};
這樣就可以返回?cái)?shù)據(jù)了
CoAP Server端代碼
Server端的代碼比較簡單,判斷一下
if (req.headers['Accept'] == 'application/json') {
parse_url(req.url, function(result){
res.end(result);
});
res.code = '2.05';
}
請求的是否是JSON格式,再返回一個(gè)205,也就是Content,只是這時(shí)設(shè)計(jì)是請求一個(gè)URL返回對應(yīng)的數(shù)據(jù)。如
coap://localhost/id/1/
這時(shí)應(yīng)該請求的是ID為1的數(shù)據(jù),即
[ { id: 1, value: 'is id 1', sensors1: 19, sensors2: 20 }]
而parse_url只是從數(shù)據(jù)庫從讀取相應(yīng)的數(shù)據(jù)。
function 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));
})
}
并且全部都顯示出來,設(shè)計(jì)得真是有點(diǎn)不行,不過現(xiàn)在已經(jīng)差不多了。
(注意
:windows系統(tǒng)npm install失敗時(shí),需要自己建立一個(gè)C:\Documents and Settings[USERNAME]\Application Data\npm 文件)
npm install iot-coap
1.新建index.js
注意
: 如果已經(jīng)存在一個(gè)index.js文件,請將下面內(nèi)容添加到文件末尾(create index.js, and add)
var iotcoap = require('iot-coap');
iotcoap.run();
iotcoap.rest.run();
注意
:在db配置可以選擇mongodb和sqlite3,替換所需要的數(shù)據(jù)庫即可。(you can choice db on iot.js with 'sqlite' or 'mongodb')
2.創(chuàng)建iot.js
exports.config = {
"db_name": "iot.db",
"mongodb_name": "iot",
"mongodb_documents": "iot",
"db": "mongodb",
"table_name": "basic",
"keys":[
"id",
"value",
"sensors1",
"sensors2"
],
"db_table": "id integer primary key, value text, sensors1 float, sensors2 float",
"mongodb_init":[
{
id: 1,
value: "is id 1",
sensors1: 19,
sensors2: 20
},
{
id: 2,
value: "is id 2",
sensors1: 20,
sensors2: 21
}
],
"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;",
"rest_url": "/id/:id",
"rest_post_url": "/",
"rest_port": 8848
};
3.運(yùn)行(run)
node index.js
show:
coap listening at coap://0.0.0.0:5683
restify listening at http://0.0.0.0:8848
更多建議: