Android中使用CoAP协议
什么是CoAP协议
CoAP(Constrained Application Protocol)协议是一种物联网协议。与传统的PC、智能手机相比,物联网设备大多是资源限制型的,有限的CPU、RAM、Flash、网络带宽等。对于这些设备来说,直接使用TCP和HTTP协议是不太现实的。
CoAP协议主要有以下特征
- 受限制的小型设备的Web传输协议(类似于HTTP)
- 异步信息交换
- 低开销,非常易于解析
- 支持URI和内容类型
- 代理和缓存功能
从抽象协议层,CoAP可以表示为
如上所示,CoAP协议分为Request/Response和Messages层,其中,Messages层处理UDP和异步消息。Request/Response基于请求/响应消息来管理请求/响应交互
CoAP支持始终不同的消息类型
- CON Confirmable 可确认的
- NON Non-Confirmable 无法确认
- Acknowledge 确认
- Reset 重置
这里我们重点介绍下可确认消息与不可确认消息。
可确认消息是可靠消息,在两个端点之间交换信息时,这些消息可能是可靠的。在CoAP中,使用确认消息(CON)获得可靠的消息。使用这些消息,客户端可以确保消息将达到服务器。反复发送确认消息,知道另一方发送确认回复消息(ACK)。ACK消息包含与确认消息(CON)相同的ID。
整个过程如下图所示
不可确认消息(NON)是指不需要服务端来确认的消息。它们是不可靠消息,或者换句话说,这些消息不包含必须传递给服务器的关键信息。包含从传感器读取的值的消息属于此类别。
即使这些消息不可靠,它们也具有唯一的ID。
值的注意的是,与HTTP协议类似,CoAP协议的Request方法包含以下四种。
- GET方法 用于获取某资源
- POST方法 用于创建某资源
- PUT方法 用于更新某资源
- DELETE方法 用于删除某资源
当然,以上的Request方法都是官方的定义,具体的功能由代码来决定。POST也可以用来获取信息
下面来重点介绍下Android端如何集成使用CoAP服务。
集成
这里我们使用org.eclipse.californium:californium-core开源框架来实现CoAP功能
GitHub地址
https://github.com/eclipse/californium
项目的build.gradle文件集成如下代码
dependencies { def californiumVersion = '3.2.0' implementation 'org.eclipse.californium:californium-core:'+californiumVersion}
服务端实现
服务端开启CoAP服务时,需要开启一个Service,如下
class DemoServerService : Service() { private lateinit var mCoapService: CoapServer private lateinit var mBinder: Binder override fun onCreate() { this.mCoapService = CoapServer(6723) //这里需要定义端口号 val coapResource = CoapResource("base") coapResource.add(AResource()) this.mCoapService.add(coapResource) } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { kotlin.runCatching { this.mCoapService.start() myLogger.d("onStartCommand") } return super.onStartCommand(intent, flags, startId) }}
这里AResource的定义如下
class SearchResource : BaseCoapResource("a") { private val myLogger: MyLogger by lazy { MyLogger.get(SearchResource::class.java.simpleName) } override fun handleGET(exchange: CoapExchange?) { super.handleGET(exchange) exchange?.respond("this is get") } override fun handlePOST(exchange: CoapExchange?) { if (exchange == null) return myLogger.d("requestString:${exchange.requestText}") }}
这样,当我们启动DemoServerService服务时,服务端就开启了一个CoAP服务,如下
val intent = Intent(context, DemoServerService::class.java)context.startService(intent)
客户端实现
客户端如何发起一个CoAP请求呢?示例代码如下,我们对服务端发起一个POST请求
internal fun request() { val url = "coap://192.168.3.12:6723)}/base/a" val map = ArrayMap<String, Any>() map["deviceId"] = "1234" val handler = object : CoapHandler { override fun onLoad(response: CoapResponse?) { if (response == null) return myLogger.d(Utils.prettyPrint(response)) } override fun onError() { myLogger.d("connect error") callback?.invoke(false) } } val coapClient = CoapClient(url).useNONs().setTimeout(UAMClientManager.DEFAULT_TIMEOUT) coapClient.post(handler, Gson().toJson(map), MediaTypeRegistry.APPLICATION_JSON) }
实现广播
如何向局域网内发起一个广播呢?我们都知道只需要向网关发送CoAP请求即可。但是实践中,我们发现一个CoAP request只能收到一个response,那怎么能保证一个Request收到多个Response呢?
如下实现,我们将发起网络请求的时候,不使用默认的CoapClient,而是使用我们定义的MulticastCoapClient
class MulticastCoapClient(url: String) : CoapClient(url) { companion object { private val TAG = MulticastCoapClient::class.java.simpleName } private val logger by lazy { MyLogger.get(TAG) } override fun send(request: Request?): Request { if (request != null) { val address = request.localAddress request.setLocalAddress(address, true) } return super.send(request) }}
如此,我们就可以实现局域网内广播CoAP请求了!