引自維基百科上的介紹,用的是谷歌翻譯。。。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的命令行工具。目前有兩個不錯的工具可使用。node
安裝命令以下git
bashnpm install coap-cli -g
在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}]
Mac OS下能夠直接用npm
bashbrew install libcoap
Ubuntu GNU/Linux下json
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>
爲了能訪問coap://localhost/,因而咱們便須要安裝一個Firefox並安裝一個名爲Copper的插件。
下載地址: https://addons.mozilla.org/en-US/firefox/addon/copper-270430/
做爲測試咱們一樣能夠訪問 coap://vs0.inf.ethz.ch:5683/
接着咱們便開始試試作一個簡單的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的。
這說裏NodeJS Module的意義是由於咱們須要在別的地方引用到db_helper這個庫,也就是下一小節要的講的內容。
這樣咱們就能夠在server.js相似於這樣去引用這個js庫。
javascriptvar DBHelper = require('./db_helper.js'); DBHelper.initDB();
而這樣調用的前提是咱們須要去聲明這樣的module,爲了方便地導出函數功能調用。
javascriptfunction DBHelper(){ } DBHelper.initDB = function(){}; module.exports = DBHelper;
此次咱們用的是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是一個RESTful傳輸協議用於受限設備的節點和網絡。基本的CoAP消息是一個不錯的選擇對於小型載荷如
然而,有時咱們的應用須要傳輸更大的有效載荷,如——更新固件。與HTTP,TCP作繁重工做將大型有效載荷分紅多個數據包,並確保他們全部到達並以正確的順序被處理。
CoAP是同UDP與DLTS同樣是基於數據報傳輸的,這限制了資源表示(resource representation)的最大大小,使得傳輸不須要太多的分割。雖然UDP支持經過IP分片傳輸更大的有效載荷,且僅限於64KiB,更重要的是,並無真正很好地約束應用和網絡。
而不是依賴於IP分片,這種規範基本COAP了對「塊」選項,用於傳輸信息從多個資源區塊的請求 - 響應對。在許多重要的狀況下,阻止使服務器可以真正無狀態:服務器能夠處理每塊分開傳輸,而無需創建鏈接之前的數據塊傳輸的其餘服務器端內存。
綜上所述,塊(Block)選項提供了傳送一個最小的在分塊的方式更大的陳述。
看看在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。
如下文字來自谷歌翻譯:
互聯網媒體類型是經過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
。如上表所示的結果是其對應的結果,這樣的話能夠減小傳遞的信息量。
因而在一開始的時候首先支持的即是"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)); }) }
而且所有都顯示出來,設計得真是有點不行,不過如今已經差很少了。
在線查看:一步步搭建物聯網系統
圖靈-電子書版一步步搭建物聯網系統