目录
前言
在之前的关于物联网协议的介绍中,我们详细介绍了如何基于java进行coap协议的开发,由于一些项目原因,在项目中采用的不是java的技术栈,而是asp.net core,因此需要基于c#进行coap协议的开发与实现。coap本身是与编程语言无关的,不仅可以用java进行实现,当然也可以使用c#,还可以是python、go等多语言,感兴趣的朋友可以自行问度娘,其实每一种语言都有相应的coap实现。以下是博主分享的博客,大家可以自己熟悉的编程语言所进行的开源实现,coap各编程语言实现传送门。非常感谢他的认真整理。
名称 | 开发语言 | coap版本 | 客户端/服务端 | 实现的coap特征 | 开源协议 | 项目链接地址 |
---|---|---|---|---|---|---|
californium | java | rfc 7252 | client + server | observe, blockwise transfers, dtls | epl+edl | eclipse californium™ |
cantcoap | c++/c | rfc 7252 | client + server | bsd | https://github.com/staropram/cantcoap | |
coap implementation for go | go | rfc 7252 | client + server | core + draft subscribe | mit | https://github.com/dustin/go-coap |
coap.net | c# | rfc 7252, coap-13, coap-08, coap-03 | client + server | core, observe, blockwise transfers | 3-clause bsd | https://github.com/smeshlink/coap.net |
coapsharp | c#, .net | rfc 7252 | client + server | core, observe, block, rd | lgpl | http://www.coapsharp.com |
coapthon | python | rfc 7252 | client + server + forward proxy + reverse proxy | observe, multicast server discovery, core link format parsing, block-wise | mit | https://github.com/tanganelli/coapthon |
copper | javascript (browser plugin) | rfc 7252 | client | observe, blockwise transfers | 3-clause bsd | https://github.com/mkovatsc/copperhttps://addons.mozilla.org/de/firefox/addon/copper-270430/ |
ecoap | c | rfc 7252 | client + server | core | mit | josé bollo / ecoap · gitlab |
erbium for contiki | c | rfc 7252 | client + server | observe, blockwise transfers | 3-clause bsd | contiki: the open source operating system for the internet of things (er-rest-example) |
etri coap | c | rfc 7252 | client + server | core, observe, block | commercial | http://coap.or.kr/index_en.html |
icoap | objective-c | rfc 7252 | client | core, observe, blockwise transfers | mit | https://github.com/stuffrabbit/icoap |
jcoap | java | rfc 7252 | client + server | observe, blockwise transfers | apache license 2.0 | https://code.google.com/p/jcoap/ |
libcoap | c | rfc 7252 | client + server | observe, blockwise transfers | bsd/gpl | libcoap: c-implementation of coap download | sourceforge.net |
microcoap | c | rfc 7252 | client + server | mit | https://github.com/1248/microcoap | |
ncoap | java | rfc 7252 | client + server | observe | bsd | https://github.com/okleine/ncoap |
node-coap | javascript | rfc 7252 | client + server | core, observe, block | mit | https://github.com/mcollina/node-coap |
ruby coap | ruby | rfc 7252 | client + server (david) | core, observe, block, rd | mit, gpl | https://github.com/nning/coap https://github.com/nning/david |
sensinode c device library | c | rfc 7252 | client + server | core, observe, block, rd | commercial | downloads - arm developer |
sensinode java device library | java se | rfc 7252 | client + server | core, observe, block, rd | commercial | downloads - arm developer |
sensinode nanoservice platform | java se | rfc 7252 | cloud server | core, observe, block, rd | commercial | downloads - arm developer |
smcp | c | rfc 7252 | client + server | core, observe, block | mit | https://github.com/darconeous/smcp |
swiftcoap | swift | rfc 7252 | client + server | core, observe, blockwise transfers | mit | https://github.com/stuffrabbit/swiftcoap |
tinyos coapblip | nesc/c | coap-13 | client + server | observe, blockwise transfers | bsd | http://docs.tinyos.net/tinywiki/index.php/coap |
txthings | python (twisted) | rfc 7252 | client + server | blockwise transfers, observe (partial) | mit | https://github.com/siskin/txthings/ |
由于对asp.net core并不是很熟悉,在进行基础入门编程学习之后后,我们基于mozi开源框架进行扩展扩展实现,这是gitee上mozi项目地址。原本的项目包含的内容比较多,我们可以在它的基础之上进行简化,改造成符合自己需求的项目。本文以coapserver为主线,介绍coapserver使用c#语言的定义以及后台资源管理类的定义和实现。
一、c#的coap server实现
本节将重点介绍coapserver在c#中的设计与实现,由于coap协议在java的篇章中有所涉及,相信大家对coap已经不再陌生,因此这里不再对coap进行赘述。下面依然采用熟悉的ooa即面向对象分析,采用面向对象的方式进行源代码分析。
1、coapserver相关类
在coapserver中,在这个工程中,主要涉及的类如下:
序号 | 类名 | 说明 |
1 | coapserver | coap的服务端 |
2 | coapresource | 类似于java的中controller |
3 | resourcemanager | 资源管理器,可以理解成ioc容器 |
2、主要类解析
coapserver是服务端程序中最重要的类,其中主要定义了后端的服务,以及绑定了coap协议,用于接收前端来自client的请求。
从类的继承体系来说,coapserver是coappeer的子类,有必要对coappeer进行一个全面的说明。
using system;
using system.collections.generic;
namespace mozi.iot
{
/// <summary>
/// coap对等端
/// </summary>
public class coappeer
{
/// <summary>
/// 最大数据包尺寸 包含所有头信息和有效荷载 byte
/// </summary>
private int _maxtransferpacksize = 512;
private int _blocksize = 128;
private ulong _packetsendcount, _totalsendbytes, _packetreceived = 0, _totalreceivedbytes;
protected udpsocketiocp _socket;
protected int bindport = coapprotocol.port;
/// <summary>
/// 最小分块大小,单位byte
/// </summary>
public const int minblocksize = 16;
/// <summary>
/// 最大分块大小,单位byte
/// </summary>
public const int maxblocksize = 2048;
/// <summary>
/// 当前端默认采用块大小,默认值为128bytes,单位byte
/// </summary>
/// <remarks>在通讯两方没有进行协商的情况下,默认采用此值作为分块大小。取值区间为{<see cref="minblocksize"/>~<see cref="maxblocksize"/>}</remarks>
public int blocksize { get { return _blocksize; } set { _blocksize = value; } }
/// <summary>s
/// 受支持的请求方法
/// </summary>
protected list<coapcode> supportedrequest = new list<coapcode> { coaprequestmethod.get, coaprequestmethod.post, coaprequestmethod.put, coaprequestmethod.delete };
/// <summary>
/// 数据包接收事件,字节流数据包
/// </summary>
public packagereceive datagramreceived;
/// <summary>
/// 服务端口
/// </summary>
public int port { get { return bindport; } protected set { bindport = value; } }
/// <summary>
/// 启动时间
/// </summary>
public datetime starttime { get; private set; }
/// <summary>
/// 服务器运行状态
/// </summary>
public bool running
{
get; set;
}
/// <summary>
/// 最大数据包尺寸 包含所有头信息和有效荷载
/// </summary>
internal int maxtransferpacksize { get => _maxtransferpacksize; set => _maxtransferpacksize = value; }
/// <summary>
/// 累计接收到的包的数量
/// </summary>
public ulong packetreceivedcount { get => _packetreceived; }
/// <summary>
/// 累计接收的字节数
/// </summary>
public ulong totalreceivedbytes { get => _totalreceivedbytes; }
/// <summary>
/// 累计发出的包的数量
/// </summary>
public ulong packetsendcount => _packetsendcount;
/// <summary>
/// 累计发出的字节数
/// </summary>
public ulong totalsendbytes => _totalsendbytes;
public coappeer()
{
_socket = new udpsocketiocp();
_socket.afterreceiveend += socket_afterreceiveend;
}
/// <summary>
/// 以指定端口启动<see cref="f:port"/>,如果不配置端口则使用默认端口
/// </summary>
public void start()
{
start(bindport);
}
/// <summary>
/// 启动本端服务 默认5683端口
/// </summary>
/// <param name="port"></param>
public void start(int port)
{
bindport = port;
_socket.start(bindport);
starttime = datetime.now;
running = true;
}
/// <summary>
/// 端口下线
/// </summary>
public void shutdown()
{
_socket.shutdown();
starttime = datetime.minvalue;
running = false;
}
/// <summary>
/// 数据接收完成回调
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
/// <remarks>继承类如果覆盖该事件,则可以接管数据处理</remarks>
protected virtual void socket_afterreceiveend(object sender, datatransferargs args)
{
_packetreceived++;
_totalreceivedbytes += args.data != null ? (uint)args.data.length : 0;
if (datagramreceived != null)
{
datagramreceived(args.ip, args.port, args.data);
}
}
}
}
从以上的代码中可以发现,上述类也是定义了coap协议工作所必须要的一些属性,比如端口、协议、数据包内容等等。coapserver的属性代码如下:
private cache.messagecachemanager _cm;
/// <summary>
/// 接收到请求
/// </summary>
public messagetransmit requestreceived;
/// <summary>
/// 发起响应请求
/// </summary>
public messagetransmit responsed;
private bool _proxypassed = false;
private uint maxbodysize=20*1024*1024;
/// <summary>
/// 服务端能处理的最大post资源大小 单位byte
/// </summary>
public uint maxbodysize { get => maxbodysize; set => maxbodysize = value; }
/// <summary>
/// 服务器根目录
/// </summary>
public string root = appdomain.currentdomain.basedirectory;
3、资源控制器定义
每一个后台都会对应一个资源控制器,这里也不例外,我们来看下c#的实现。在coapresource中同样的定义了get、post、put、delete四种请求方法。如下图所示:
/// <summary>
/// coap资源
/// </summary>
public abstract class coapresource
{
/// <summary>
/// 资源总大小
/// </summary>
public abstract uint resourcesize { get; }
/// <summary>
/// 默认分块大小128,单位bytes
/// </summary>
/// <remarks>
/// 如果资源尺寸过大,则必须合理配置此大小。
/// 取值范围为16-2048bytes blockoptionvalue中size的数据容量。参考<see cref="blockoptionvalue"/>
/// </remarks>
public virtual uint blocksize { get { return 128; } }
/// <summary>
/// get方法
/// </summary>
/// <param name="ctx"></param>
/// <returns></returns>
public virtual coappackage onget(coapcontext ctx)
{
ctx.response = new coappackage { messagetype = coapmessagetype.acknowledgement, messsageid = ctx.request.messsageid, token = ctx.request.token, code = coapresponsecode.forbidden };
return ctx.response;
}
/// <summary>
/// post方法
/// </summary>
/// <param name="ctx"></param>
/// <returns></returns>
public virtual coappackage onpost(coapcontext ctx)
{
ctx.response = new coappackage { messagetype = coapmessagetype.acknowledgement, messsageid = ctx.request.messsageid, token = ctx.request.token, code = coapresponsecode.forbidden };
return ctx.response;
}
/// <summary>
/// put方法
/// </summary>
/// <param name="ctx"></param>
/// <returns></returns>
public virtual coappackage onput(coapcontext ctx)
{
ctx.response = new coappackage { messagetype = coapmessagetype.acknowledgement, messsageid = ctx.request.messsageid, token = ctx.request.token, code = coapresponsecode.forbidden };
return ctx.response;
}
/// <summary>
/// delete方法
/// </summary>
/// <param name="ctx"></param>
/// <returns></returns>
public virtual coappackage ondelete(coapcontext ctx)
{
ctx.response = new coappackage { messagetype = coapmessagetype.acknowledgement, messsageid = ctx.request.messsageid, token = ctx.request.token, code = coapresponsecode.forbidden };
return ctx.response;
}
/// <summary>
/// 分块查找
/// </summary>
/// <param name="indblock"></param>
/// <param name="blocksize"></param>
/// <returns></returns>
protected virtual byte[] seek(int indblock, int blocksize)
{
return new byte[] { };
}
/// <summary>
/// block2分块协商
/// </summary>
/// <param name="ctx"></param>
/// <returns></returns>
internal virtual void handleblock2query(coapcontext ctx)
{
coapoption opt = ctx.request.options.find(x => x.option == coapoptiondefine.block2);
if (opt != null)
{
optionvalue opt2 = new blockoptionvalue() { pack = opt.value.pack };
//if(opt2)
}
}
/// <summary>
/// 请求服务端资源大小,响应条件为 get size2=0
/// </summary>
/// <param name="ctx">响应上下文对象</param>
/// <returns></returns>
internal virtual bool handlesize2query(coapcontext ctx)
{
coapoption opt = ctx.request.options.find(x => x.option == coapoptiondefine.size2);
if (opt != null && int.parse(opt.value.tostring()) ==0 && ctx.request.code == coaprequestmethod.get)
{
ctx.response = new coappackage { messagetype = coapmessagetype.acknowledgement, messsageid = ctx.request.messsageid, token = ctx.request.token, code = coapresponsecode.content };
coapoption optresp = new coapoption() { option = coapoptiondefine.size2, value = new unsignedintegeroptionvalue() { value = resourcesize } };
ctx.response.setoption(optresp);
return true;
}
else
{
return false;
}
}
}
4、resourcemanager管理器
每个业务接收类都对应一个resource,而这些resource必须要使用一个统一的容器管理起来,可以把它理解成java对应的ioc容器,程序运行时会自动把相关资源管理起来。资源描述如下
public class resourcedescriptionattribute : attribute
{
/// <summary>
/// 命名空间
/// </summary>
public string namespace { get; set; }
/// <summary>
/// 资源名称
/// </summary>
public string name { get; set; }
/// <summary>
/// 文字描述
/// </summary>
public string description { get; set; }
/// <summary>
/// 资源类型
/// </summary>
public string resourcetype { get; set; }
}
资源管理器的核心管理方式也是采用反射的机制,如下:
二、coapserver生命周期
在上面的代码中,对coapserver的编码实现进行了介绍,下面将采用熟悉的调试方法来进行调用跟踪,在关键代码中进行深度讲解。
1、server创建代码
创建server的代码如下:
static void main(string[] args)
{
console.writeline("你好!coap服务端已开启,等待客户端连接......");
//服务端
coapserver cs = new coapserver();
cs.requestreceived += new messagetransmit((host, port, pack) =>
{
console.writeline($"from:[{host}:{port}]");
console.writeline(pack.tostring());
console.title = string.format("elapsed:{2},count:{0},pps:{3},bytes:{1}", cs.packetreceivedcount, cs.totalreceivedbytes,formatseconds(sp.elapsedmilliseconds),pps);
});
cs.start();
console.readline();
}
2、服务端创建
第一步、调用构造方法
第二步、指定端口启动
第三步、设置socket,绑定协议
3、绑定endpoint
4、准备接收请求
总结
以上就是本文的主要内容,本文以coapserver为主线,介绍coapserver使用c#语言的定义以及后台资源管理类的定义和实现。行文仓促,定有不当之处,欢迎各位朋友专家批评指正。
发表评论