CoAP(Constrained Application Protocol)协议是一种物联网协议。与传统的PC、智能手机相比,物联网设备大多是资源限制型的,有限的CPU、RAM、Flash、网络带宽等。对于这些设备来说,直接使用TCP和HTTP协议是不太现实的。
CoAP协议主要有以下特征
从抽象协议层,CoAP可以表示为
如上所示,CoAP协议分为Request/Response和Messages层,其中,Messages层处理UDP和异步消息。Request/Response基于请求/响应消息来管理请求/响应交互
CoAP支持始终不同的消息类型
这里我们重点介绍下可确认消息与不可确认消息。
可确认消息是可靠消息,在两个端点之间交换信息时,这些消息可能是可靠的。在CoAP中,使用确认消息(CON)获得可靠的消息。使用这些消息,客户端可以确保消息将达到服务器。反复发送确认消息,知道另一方发送确认回复消息(ACK)。ACK消息包含与确认消息(CON)相同的ID。
整个过程如下图所示
不可确认消息(NON)是指不需要服务端来确认的消息。它们是不可靠消息,或者换句话说,这些消息不包含必须传递给服务器的关键信息。包含从传感器读取的值的消息属于此类别。
即使这些消息不可靠,它们也具有唯一的ID。
值的注意的是,与HTTP协议类似,CoAP协议的Request方法包含以下四种。
当然,以上的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请求了!