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协议要点:

  1. 轻量级:专为受限设备设计,头部仅4字节
  2. RESTful风格:支持GET/POST/PUT/DELETE方法
  3. 观察者模式:支持资源变化通知
  4. 块传输:支持大数据分块传输
  5. 资源发现:通过.well-known/core发现资源

下一章我们将学习HTTP RESTful服务,实现Web API开发。

最后更新:2026-03-27