Andlink设备使用了Coap来进行配网和一定的数据交互,因此记录一下。
Coap协议
Coap可以简单理解为是为了在物联网场景下实现web功能而产生的一种轻量级网络协议;
主要特点有使用UDP方式传输以及基于REST等。
coap协议的服务地址和http的url类似:
coap://192.168.52.101:5683/qlink/request
也同样接收POST/GET/PUT/DELETE等请求;
区别在于,coap还将消息类型分为4类:
消息类型 | 含义 |
---|---|
CON | 需要回复 |
NON | 无须回复 |
ACK | 回应,对应CON的回复 |
RST | 复位,对应CON错误内容情况下的回复 |
一般情况下只需要关注前两种类型即可;
比如“POST-CON”和“POST-NON”,前者代表“需要回复的POST请求”,后者代表“不需要回复的POST请求”。
californium
californium是Eclipse旗下的一个成熟的coap库,可在californium找到demo-apps分支并学习。
在gradle中导入:
implementation 'org.eclipse.californium:californium-core:3.5.0'
implementation 'org.eclipse.californium:element-connector:3.5.0'
implementation 'org.eclipse.californium:scandium:3.5.0'
implementation 'org.eclipse.californium:demo-certs:3.5.0'
implementation 'org.slf4j:slf4j-simple:1.7.25'
如果有报META-INF相关的错误,则需要进行pick处理:
packagingOptions {
pickFirst 'META-INF/legal/**'
}
客户端
客户端关注CoapClient类即可,如常用的post请求方法:
public void post(CoapHandler handler, String payload, int format)
三个参数分别为回调、正文、正文格式;
示例如下:
CoapClient coapClient = new CoapClient("coap://192.168.21.104:5683/qlink/success");
coapClient.post(new CoapHandler() {
@Override
public void onLoad(CoapResponse coapResponse) {
System.out.println(coapResponse.getResponseText());
}
@Override
public void onError() {
}
}, "{\"deviceMac\":\"1\",\"deviceType\":\"2\"}", MediaTypeRegistry.APPLICATION_JSON);
MediaTypeRegistry.APPLICATION_JSON即JSON格式,类似于content-type,有很多种选择;
getResponseText方法可从response中拿到回复的正文内容;
CoapClient默认的消息类型为CON;
/** The type used for requests (CON is default) */
private Type type = Type.CON;
其他请求方法类似;
服务端
服务端关注CoapServer类,以及注意添加负责处理逻辑的Resource,Resource类似于Handler,但兼任了路径解析功能;
CoapServer coapServer = new CoapServer(5683);
Resource root = new CoapResource("qlink");
root.add(new CoapResource("success"){
@Override
public void handlePOST(CoapExchange exchange) {
System.out.println("--------"+getURI());
System.out.println(exchange.getRequestText());
Response response = new Response(CoAP.ResponseCode.CONTENT);
response.setPayload("{\"result\":1}");
response.getOptions().setContentFormat(MediaTypeRegistry.APPLICATION_JSON);
exchange.respond(response);
}
});
root.add(new CoapResource("regist"){
@Override
public void handlePOST(CoapExchange exchange) {
System.out.println("--------"+getURI());
System.out.println(exchange.getRequestText());
Response response = new Response(CoAP.ResponseCode.CONTENT);
response.setPayload("{\"result\":1}");
response.getOptions().setContentFormat(MediaTypeRegistry.APPLICATION_JSON);
exchange.respond(response);
}
});
coapServer.add(root);
coapServer.start();
上面的示例中,服务端将监听两个路径即:
coap://host:5683/qlink/success
coap://host:5683/qlink/regist
CoapResource是一个集成的处理类,只需要关注正文内容即可,可自由复写handle**来处理各类请求,在其源码中可见端倪:
//CoapResource.java
public void handleRequest(Exchange exchange) {
Code code = exchange.getRequest().getCode();
switch(code) {
case GET:
this.handleGET(new CoapExchange(exchange, this));
break;
case POST:
this.handlePOST(new CoapExchange(exchange, this));
break;
case PUT:
this.handlePUT(new CoapExchange(exchange, this));
break;
case DELETE:
this.handleDELETE(new CoapExchange(exchange, this));
break;
case FETCH:
this.handleFETCH(new CoapExchange(exchange, this));
break;
case PATCH:
this.handlePATCH(new CoapExchange(exchange, this));
break;
case IPATCH:
this.handleIPATCH(new CoapExchange(exchange, this));
break;
default:
exchange.sendResponse(new Response(ResponseCode.METHOD_NOT_ALLOWED, true));
}
}
回复只需要取得Exchange类,生成Response后使用respond方法即可:
Response response = new Response(CoAP.ResponseCode.CONTENT);
response.setPayload("{\"result\":1}");
response.getOptions().setContentFormat(MediaTypeRegistry.APPLICATION_JSON);
exchange.respond(response);
CoAP.ResponseCode.CONTENT类似于http的状态响应码,同样有很多选择,在这里输出的是2.05;
广播
严格来说是组播。
组播不能简单用CoapClient来完成,因为CoapClient默认新建的Request是CON的,而组播并不允许,并会报错警告只能使用使用NON消息类型:
System.err: [Thread-3] WARN org.eclipse.californium.core.network.CoapEndpoint - coap CON request to multicast destination 192.168.21.255:5683 is not allowed, as per RFC 7252, 8.1, a client MUST use NON message type for multicast requests
因而只能依照源码自行新建Request:
Request request = new Request(CoAP.Code.POST, CoAP.Type.NON);
request.setURI("coap://192.168.21.255/qlink/success");
request.setPayload("{\"deviceMac\":\"11\",\"deviceType\":\"22\"}");
下面两行依次设置请求路径、正文;
当然,需要对回复进行监听,依旧仿照源码设置Observer即可:
request.addMessageObserver(new MessageObserverAdapter() {
@Override
public void onResponse(Response response) {
System.out.println(response.toString());
System.out.println(response.getPayloadString());
}
});
最后稍微封装一下,主要是让其自行获取组播地址:
private static void broadcast(int port, String path, String content,MessageObserverAdapter observerAdapter) {
Inet4Address broadcastAddress = NetworkInterfacesUtil.getBroadcastIpv4();
String url = String.format(Locale.CHINA, "coap://%s:%d%s", broadcastAddress.getHostAddress(), port, path);
Request request = new Request(CoAP.Code.POST, CoAP.Type.NON);
request.setURI(url);
request.setPayload(content);
request.addMessageObserver(observerAdapter);
request.send();
}
组播的接收用上面的CoapServer服务端接收即可,一般情况下是够用的。
收到服务端的回复输出如下:
NON-2.05 MID=62472, Token=2E390D5C9381DFA7, OptionSet={"Content-Format":"application/json"}, "{"result":1}"
{"result":1}
NON-2.05 MID=13847, Token=2E390D5C9381DFA7, OptionSet={"Content-Format":"application/json"}, "{"result":2}"
{"result":2}
注意事项
- 以上所有操作都建议在子线程中进行;
- 记得导入org.slf4j:slf4j-simple:1.7.25
以上就是接触coap的一些记录,另外关于californium中的EndPoint以及线程模型之类的,有时间再看吧。