CoAP协议 #
一、CoAP协议概述 #
1.1 CoAP简介 #
CoAP(Constrained Application Protocol)是专为物联网受限设备设计的Web传输协议。
text
┌─────────────────────────────────────────────────────────┐
│ CoAP协议特点 │
├─────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────┐ │
│ │ CoAP over UDP │ │
│ │ ├── 头部仅4字节 │ │
│ │ ├── 支持GET/POST/PUT/DELETE │ │
│ │ ├── 支持观察者模式 │ │
│ │ ├── 支持块传输 │ │
│ │ └── 支持资源发现 │ │
│ └─────────────────────────────────────────────────┘ │
│ │
│ 与HTTP对比: │
│ ┌───────────────┬───────────────┬───────────────┐ │
│ │ 特性 │ HTTP │ CoAP │ │
│ ├───────────────┼───────────────┼───────────────┤ │
│ │ 传输层 │ TCP │ UDP │ │
│ │ 头部大小 │ 数百字节 │ 4字节 │ │
│ │ 方法 │ GET/POST/... │ GET/POST/... │ │
│ │ 观察者 │ 不支持 │ 支持 │ │
│ └───────────────┴───────────────┴───────────────┘ │
│ │
└─────────────────────────────────────────────────────────┘
1.2 CoAP方法 #
| 方法 | 说明 | 对应HTTP |
|---|---|---|
| GET | 获取资源 | HTTP GET |
| POST | 创建资源 | HTTP POST |
| PUT | 更新资源 | HTTP PUT |
| DELETE | 删除资源 | HTTP DELETE |
1.3 CoAP响应码 #
| 响应码 | 类别 | 说明 |
|---|---|---|
| 2.xx | 成功 | 请求成功处理 |
| 4.xx | 客户端错误 | 请求格式错误 |
| 5.xx | 服务器错误 | 服务器处理失败 |
二、Californium框架 #
2.1 添加依赖 #
xml
<dependency>
<groupId>org.eclipse.californium</groupId>
<artifactId>californium-core</artifactId>
<version>3.8.0</version>
</dependency>
<dependency>
<groupId>org.eclipse.californium</groupId>
<artifactId>californium-elements</artifactId>
<version>3.8.0</version>
</dependency>
2.2 CoAP服务器 #
java
package com.example.network.coap;
import org.eclipse.californium.core.CoapResource;
import org.eclipse.californium.core.CoapServer;
import org.eclipse.californium.core.coap.CoAP;
import org.eclipse.californium.core.server.resources.CoapExchange;
public class CoAPServer {
private CoapServer server;
public void start(int port) {
server = new CoapServer(port);
server.add(new SensorResource("temperature"));
server.add(new SensorResource("humidity"));
server.add(new DeviceResource("device"));
server.add(new LEDResource("led"));
server.start();
System.out.println("CoAP服务器启动,端口: " + port);
}
public void stop() {
if (server != null) {
server.stop();
}
}
static class SensorResource extends CoapResource {
private double value;
public SensorResource(String name) {
super(name);
setObservable(true);
setObserveType(CoAP.Type.CON);
getAttributes().setObservable();
}
@Override
public void handleGET(CoapExchange exchange) {
value = Math.random() * 100;
exchange.respond(String.format("%.2f", value));
}
public void updateValue(double newValue) {
this.value = newValue;
changed();
}
}
static class DeviceResource extends CoapResource {
private String status = "online";
public DeviceResource(String name) {
super(name);
}
@Override
public void handleGET(CoapExchange exchange) {
String response = String.format(
"{\"status\":\"%s\",\"uptime\":%d}",
status, System.currentTimeMillis() / 1000);
exchange.respond(response);
}
@Override
public void handlePUT(CoapExchange exchange) {
String payload = exchange.getRequestText();
if (payload.contains("status")) {
status = payload.split("\"")[3];
exchange.respond(CoAP.ResponseCode.CHANGED);
} else {
exchange.respond(CoAP.ResponseCode.BAD_REQUEST);
}
}
}
static class LEDResource extends CoapResource {
private boolean state = false;
public LEDResource(String name) {
super(name);
setObservable(true);
getAttributes().setObservable();
}
@Override
public void handleGET(CoapExchange exchange) {
exchange.respond(state ? "ON" : "OFF");
}
@Override
public void handlePOST(CoapExchange exchange) {
String command = exchange.getRequestText().toUpperCase();
if ("ON".equals(command)) {
state = true;
changed();
exchange.respond(CoAP.ResponseCode.CHANGED, "LED turned ON");
} else if ("OFF".equals(command)) {
state = false;
changed();
exchange.respond(CoAP.ResponseCode.CHANGED, "LED turned OFF");
} else if ("TOGGLE".equals(command)) {
state = !state;
changed();
exchange.respond(CoAP.ResponseCode.CHANGED,
"LED toggled to " + (state ? "ON" : "OFF"));
} else {
exchange.respond(CoAP.ResponseCode.BAD_REQUEST,
"Unknown command. Use ON, OFF, or TOGGLE");
}
}
}
public static void main(String[] args) {
CoAPServer server = new CoAPServer();
server.start(5683);
Runtime.getRuntime().addShutdownHook(new Thread(server::stop));
}
}
2.3 CoAP客户端 #
java
package com.example.network.coap;
import org.eclipse.californium.core.CoapClient;
import org.eclipse.californium.core.CoapHandler;
import org.eclipse.californium.core.CoapObserveRelation;
import org.eclipse.californium.core.CoapResponse;
import org.eclipse.californium.core.coap.CoAP;
import org.eclipse.californium.core.coap.MediaTypeRegistry;
public class CoAPClient {
private final String serverUri;
public CoAPClient(String host, int port) {
this.serverUri = "coap://" + host + ":" + port;
}
public String get(String path) {
CoapClient client = new CoapClient(serverUri + path);
CoapResponse response = client.get();
if (response != null) {
return response.getResponseText();
}
return null;
}
public String post(String path, String payload) {
CoapClient client = new CoapClient(serverUri + path);
CoapResponse response = client.post(payload,
MediaTypeRegistry.TEXT_PLAIN);
if (response != null) {
return response.getResponseText();
}
return null;
}
public String put(String path, String payload) {
CoapClient client = new CoapClient(serverUri + path);
CoapResponse response = client.put(payload,
MediaTypeRegistry.TEXT_PLAIN);
if (response != null) {
return response.getResponseText();
}
return null;
}
public boolean delete(String path) {
CoapClient client = new CoapClient(serverUri + path);
CoapResponse response = client.delete();
return response != null &&
response.getCode() == CoAP.ResponseCode.DELETED;
}
public CoapObserveRelation observe(String path, CoapHandler handler) {
CoapClient client = new CoapClient(serverUri + path);
return client.observe(handler);
}
public void observeAsync(String path, java.util.function.Consumer<String> callback) {
CoapClient client = new CoapClient(serverUri + path);
client.observe(new CoapHandler() {
@Override
public void onLoad(CoapResponse response) {
callback.accept(response.getResponseText());
}
@Override
public void onError() {
System.err.println("观察者错误");
}
});
}
public void discover() {
CoapClient client = new CoapClient(serverUri + "/.well-known/core");
CoapResponse response = client.get();
if (response != null) {
System.out.println("资源发现:");
System.out.println(response.getResponseText());
}
}
public static void main(String[] args) throws InterruptedException {
CoAPClient client = new CoAPClient("localhost", 5683);
System.out.println("=== GET请求 ===");
String temp = client.get("/temperature");
System.out.println("温度: " + temp);
String humidity = client.get("/humidity");
System.out.println("湿度: " + humidity);
System.out.println("\n=== POST请求 ===");
String result = client.post("/led", "ON");
System.out.println("LED控制结果: " + result);
System.out.println("\n=== 观察者模式 ===");
client.observeAsync("/temperature", value ->
System.out.println("温度更新: " + value));
client.observeAsync("/led", state ->
System.out.println("LED状态更新: " + state));
Thread.sleep(30000);
}
}
三、资源发现 #
3.1 资源发现实现 #
java
package com.example.network.coap;
import org.eclipse.californium.core.CoapResource;
import org.eclipse.californium.core.CoapServer;
import org.eclipse.californium.core.coap.CoAP;
import org.eclipse.californium.core.server.resources.CoapExchange;
import org.eclipse.californium.core.server.resources.Resource;
public class ResourceDiscovery {
public static void main(String[] args) {
CoapServer server = new CoapServer(5683);
CoapResource sensors = new CoapResource("sensors");
sensors.add(new TemperatureResource());
sensors.add(new HumidityResource());
sensors.add(new PressureResource());
server.add(sensors);
server.add(new CoapResource(".well-known") {
{
add(new CoapResource("core") {
@Override
public void handleGET(CoapExchange exchange) {
StringBuilder sb = new StringBuilder();
buildCoreLink(server.getRoot(), "", sb);
exchange.respond(sb.toString());
}
});
}
});
server.start();
System.out.println("CoAP服务器启动,支持资源发现");
}
private static void buildCoreLink(Resource resource, String path, StringBuilder sb) {
String resourcePath = path + "/" + resource.getName();
if (!".well-known".equals(resource.getName())) {
if (sb.length() > 0) {
sb.append(",");
}
sb.append("<").append(resourcePath).append(">");
String rt = resource.getAttributes().getResourceTypes();
if (rt != null && !rt.isEmpty()) {
sb.append(";rt=\"").append(rt).append("\"");
}
String ct = resource.getAttributes().getContentTypes();
if (ct != null && !ct.isEmpty()) {
sb.append(";ct=").append(ct);
}
if (resource.isObservable()) {
sb.append(";obs");
}
}
for (Resource child : resource.getChildren()) {
buildCoreLink(child, resourcePath, sb);
}
}
static class TemperatureResource extends CoapResource {
public TemperatureResource() {
super("temperature");
getAttributes().setResourceType("sensor");
getAttributes().addContentType("application/json");
setObservable(true);
getAttributes().setObservable();
}
@Override
public void handleGET(CoapExchange exchange) {
double temp = 20 + Math.random() * 10;
exchange.respond("{\"value\":" + temp + ",\"unit\":\"C\"}");
}
}
static class HumidityResource extends CoapResource {
public HumidityResource() {
super("humidity");
getAttributes().setResourceType("sensor");
setObservable(true);
getAttributes().setObservable();
}
@Override
public void handleGET(CoapExchange exchange) {
double humidity = 40 + Math.random() * 30;
exchange.respond("{\"value\":" + humidity + ",\"unit\":\"%\"}");
}
}
static class PressureResource extends CoapResource {
public PressureResource() {
super("pressure");
getAttributes().setResourceType("sensor");
setObservable(true);
getAttributes().setObservable();
}
@Override
public void handleGET(CoapExchange exchange) {
double pressure = 1000 + Math.random() * 30;
exchange.respond("{\"value\":" + pressure + ",\"unit\":\"hPa\"}");
}
}
}
四、块传输 #
4.1 大数据传输 #
java
package com.example.network.coap;
import org.eclipse.californium.core.CoapClient;
import org.eclipse.californium.core.CoapResource;
import org.eclipse.californium.core.CoapServer;
import org.eclipse.californium.core.coap.CoAP;
import org.eclipse.californium.core.coap.MediaTypeRegistry;
import org.eclipse.californium.core.server.resources.CoapExchange;
public class BlockTransfer {
public static void main(String[] args) {
CoapServer server = new CoapServer(5683);
server.add(new CoapResource("data") {
@Override
public void handleGET(CoapExchange exchange) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
sb.append("Data line ").append(i).append("\n");
}
exchange.respond(sb.toString());
}
@Override
public void handlePOST(CoapExchange exchange) {
byte[] payload = exchange.getRequestPayload();
System.out.println("收到数据: " + payload.length + " 字节");
exchange.respond(CoAP.ResponseCode.CREATED,
"Received " + payload.length + " bytes");
}
});
server.start();
System.out.println("CoAP服务器启动,支持块传输");
CoapClient client = new CoapClient("coap://localhost:5683/data");
System.out.println("请求大数据...");
org.eclipse.californium.core.CoapResponse response =
client.get(MediaTypeRegistry.TEXT_PLAIN);
if (response != null) {
System.out.println("收到数据: " +
response.getResponsePayload().length + " 字节");
}
byte[] largeData = new byte[5000];
for (int i = 0; i < largeData.length; i++) {
largeData[i] = (byte) (i % 256);
}
System.out.println("发送大数据...");
response = client.post(largeData, MediaTypeRegistry.APPLICATION_OCTET_STREAM);
if (response != null) {
System.out.println("响应: " + response.getResponseText());
}
}
}
五、总结 #
CoAP协议要点:
- 轻量级:专为受限设备设计,头部仅4字节
- RESTful风格:支持GET/POST/PUT/DELETE方法
- 观察者模式:支持资源变化通知
- 块传输:支持大数据分块传输
- 资源发现:通过.well-known/core发现资源
下一章我们将学习HTTP RESTful服务,实现Web API开发。
最后更新:2026-03-27