什么是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: CoapServerprivate lateinit var mBinder: Binderoverride 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) returnmyLogger.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) returnmyLogger.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.localAddressrequest.setLocalAddress(address, true)}return super.send(request)}
}
如此,我们就可以实现局域网内广播CoAP请求了!