Constrained Application Protocol (CoAP) 是一种专为网络受限设备设计的互联网应用协议。CoAP 常用于低功耗、计算能力有限的物联网设备,能够以较小的开销实现设备和服务器之间的通信。Californium 是一个用于 CoAP 的 Java 库,用于实现 CoAP 服务器和客户端。
使用了 RabbitMQ 进行消息的异步处理,这是一种非常有效的方法,可以将接收消息和处理消息分开,提高系统的响应能力。同时,通过 RabbitMQ,服务器和客户端可以解耦,降低系统的复杂性。
Californium (Cf) 是一个用于构建物联网应用的强大工具,它是一个基于Java的开源框架,专门为实现Constrained Application Protocol (CoAP)设计。CoAP是一种专为小型、低功率和有限存储设备设计的网络协议,经常被用于物联网环境。
以下是一些Californium的关键特性:
总的来说,Californium是一个强大的CoAP实现,可以为物联网应用提供稳定、高效和安全的网络通信。
以下是项目中构建的CoAP资源,本项目构建了多层级的资源,现只展示底层资源
package com.bytecub.coap.service.resource;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.bytecub.coap.domain.PropertySetRequest;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.californium.core.coap.BlockOption;
import org.eclipse.californium.core.coap.CoAP;
import org.eclipse.californium.core.coap.OptionSet;
import org.eclipse.californium.core.observe.ObserveRelation;
import org.eclipse.californium.core.observe.ObserveRelationFilter;
import org.eclipse.californium.core.observe.ObservingEndpoint;
import org.eclipse.californium.core.server.resources.CoapExchange;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
/**
* CoAP 设备资源
* @author gcu_gz
* @version Id: DeviceResource.java, v 0.1 2022/8/29 Exp $$
*/
@Slf4j
public class DeviceResource extends AbstractResource {
PropertySetRequest resource;
public DeviceResource(String name) {
super(name);
}
public DeviceResource(String name,PropertySetRequest request) {
super(name);
this.resource = request;
/**
* 允许被观察(底层采用观察者模式,当前类为一个subject)
*/
//添加标记,使core在get时可以看到obs:true
this.getAttributes().setObservable();
this.setObservable(true);
this.setObserveType(CoAP.Type.CON);
}
/**
* 属性由产品管理
* 每个属性下有具体设备,由设备返回当前属性的具体值
* @param exchange
*/
@Override
public void handleGET(CoapExchange exchange) {
OptionSet options = exchange.getRequestOptions();
// 判断是否发出观察请求
if (options.hasObserve()) {
this.handleObserve(exchange,options);
} else {
String dataJson = JSON.toJSONString(this.resource);
exchange.respond(CoAP.ResponseCode.CONTENT,dataJson);
}
}
private void handleObserve(CoapExchange exchange, OptionSet options) {
Integer observe = options.getObserve();
if (observe > 1) {
// 其他情况返回数据
String dataJson = JSON.toJSONString(this.resource);
exchange.respond(CoAP.ResponseCode.CONTENT,dataJson);
return;
}
ObservingEndpoint endpoint = new ObservingEndpoint(exchange.getSourceSocketAddress());
ObserveRelation relation = new ObserveRelation(endpoint,this, exchange.advanced());
/**
* 当options含有observe参数:
* observe = 0,表示注册操作
* observe = 1,表示注销操作
*/
if (observe == 0) {
// 开启订阅,注册观察
relation.setEstablished(); // 允许连接
this.addObserveRelation(relation);
log.info("客户端 {} 订阅资源 {} 成功",exchange.getSourceSocketAddress().toString(),this.getURI());
exchange.respond(exchange.getSourceSocketAddress().toString()+"subscription successful,resource:"+this.getURI());
} else if (observe == 1) {
this.removeObserveRelation(relation);
byte[] block2 = new byte[3];
block2[2] = 2;
options.setBlock2(new BlockOption(block2));
exchange.respond(exchange.getSourceSocketAddress().toString()+"unsubscribe successful,resource:"+this.getURI());
}
}
@Override
public void handlePUT(CoapExchange exchange) {
String payload = null;
try {
payload = new String(exchange.getRequestPayload(),"UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
if (payload == null || payload.equals("")) {
exchange.respond(CoAP.ResponseCode.REQUEST_ENTITY_INCOMPLETE,"request payload must not be empty !");
return;
}
log.info("payload: " + payload);
JSONObject payloadObj = (JSONObject) JSON.parse(payload);
Class<? extends PropertySetRequest> clazz = this.resource.getClass();
try {
Field valueField = clazz.getDeclaredField("value");
valueField.setAccessible(true);
Object value = payloadObj.get("value");
valueField.set(this.resource,value);
Field saveTimeField = clazz.getDeclaredField("saveTimestamp");
saveTimeField.setAccessible(true);
Long saveTimestamp = System.currentTimeMillis(); // 获取当前系统时间戳
saveTimeField.set(this.resource,saveTimestamp);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
// changed()方法可以通知所有观察者,也可自己通过notifyObserverRelations实现通知
// this.changed();
// 更新后通知所有观察对象
this.notifyObserverRelations(new ObserveRelationFilter() {
@Override
public boolean accept(ObserveRelation observeRelation) {
if (observeRelation.isCanceled()) {
return false;
}
OptionSet options = observeRelation.getExchange().getRequest().getOptions();
Integer observe = options.getObserve();
if (observe == 0) {
observe = 1;
}
options.setObserve(++observe);
return true;
}
});
exchange.respond(CoAP.ResponseCode.CHANGED,payloadObj.getString("identifier")+" updated...");
}
@Override
public void handleDELETE(CoapExchange exchange) {
this.delete();
exchange.respond(CoAP.ResponseCode.DELETED,"deleted...");
}
}
RabbitMQ是一个开源的消息代理和队列服务器,用来通过轻量级的消息在分布式系统或者在微服务架构中的服务之间进行通信。RabbitMQ是使用Erlang语言来编写的,并且通过AMQP协议来进行通信。
在RabbitMQ中,生产者(Producer)创建消息并发送到交换器(Exchange),交换器根据指定的路由规则,将消息路由到一个或多个队列(Queue),消费者(Consumer)则监听队列,接收队列中的消息进行处理。
RabbitMQ 是一种消息中间件,主要用于应用程序之间的异步通信和解耦。它实现了高级消息队列协议(AMQP),并提供了丰富的特性和插件。
服务解耦是指将系统中的各个部分独立出来,使之能够独立变化和发展,而不会对整个系统产生副作用。在微服务架构中,服务解耦是非常重要的设计原则,可以提高系统的可维护性、扩展性和稳定性。
RabbitMQ 在服务间解耦中的作用主要表现在以下几点:
总的来说,RabbitMQ 可以有效地解耦服务,使系统更加灵活、可靠和易于扩展。
RabbitMQ 是一种开源消息队列(MQ)系统,用于通过消息的形式在处理和响应系统之间进行异步通信。在异步消息传递中,RabbitMQ 扮演了非常关键的角色,下面详细描述了一些主要作用:
总的来说,RabbitMQ 在异步消息传递中扮演了重要的角色,通过解耦生产者和消费者,缓冲消息,路由和分发消息,支持消息持久化和消息确认,以及设置消息的优先级,使得系统能够异步、灵活、可靠地进行消息传递。
本项目中,在CoAP服务器和CoAP客户端之间加了一层消息中间件RabbitMQ,服务器与客户端两者并不之间连接,而是通过RabbitMQ进行消息的传递。
在本项目中,RabbitMQ 有可能被用在以下场景中:
通过这些方式,RabbitMQ 在你的项目中实现了消息的异步处理,服务间的解耦,任务的调度和执行,以及提高系统的可靠性和容错性。
以下是RabbitMQ在项目中的使用:
1、更新属性,更新的数据先加入RabbitMQ
package com.bytecub.coap.service.protocol;
import com.bytecub.plugin.rabbitmq.utils.RabbitMQSender;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.californium.core.CoapClient;
import org.eclipse.californium.core.CoapResponse;
import org.eclipse.californium.elements.exception.ConnectorException;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
/**
* SetResource.java 2022/8/30
*/
@Slf4j
@Component
public class SetResource {
@Autowired
private RabbitMQSender rabbitMQSender ;
/**
* 更新属性资源
* @param uriString
* @param payload
*/
public void setProperty(String uriString,String payload) {
MessageProperties properties = new MessageProperties();
properties.setHeader("uri", uriString); // 设置URI
properties.setHeader("method", "put"); // 设置请求方式
Message amqpMessage = new Message(payload.getBytes(), properties);
// 此处消息异步发送
rabbitMQSender.send("coap.exchange","coap.key",amqpMessage);
}
}
2、CoAPSendClient类为CoAP客户端类,用于对RabbitMQ消费,并将数据根据CoAP具体请求类型交给不同类处理处理
package com.bytecub.coap.service.client;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.californium.core.CoapClient;
import org.eclipse.californium.elements.exception.ConnectorException;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
@Component
@Slf4j
public class CoAPSendClient {
public static void send(String uriString, String payload, String method) {
URI uri = null;
CoapClient coapClient = null;
try {
uri = new URI(uriString);
} catch (URISyntaxException e) {
log.warn("coap url 错误...");
} catch (Exception e) {
log.warn("{}连接失败...",uri);
}
coapClient = new CoapClient(uri);
try {
switch (method) {
case "post":
// 发送post请求
PostClient.send(coapClient, payload);
break;
case "put":
// 发送put请求
PutClient.send(coapClient, payload);
break;
}
} catch (ConnectorException e) {
log.warn("连接失败...,原因:{}", e.getMessage());
} catch (IOException e) {
log.warn("IO异常...");
} finally {
if (coapClient != null) {
coapClient.shutdown();
}
}
}
}
package com.bytecub.coap.service.client;
import org.eclipse.californium.core.CoapClient;
import org.eclipse.californium.elements.exception.ConnectorException;
import java.io.IOException;
public class PutClient {
public static void send(CoapClient coapClient, String payload) throws ConnectorException, IOException {
// the first arg is the request body
// the second arg is the format id of the body, 0-text|50-json
// coapClient.post(payload.getBytes(), 50);
coapClient.put(payload.getBytes(),50);
}
}
以下只展示部分代码,实现了observer的观察
#include
#include
#include
#include
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_log.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "nvs_flash.h"
#include "protocol_examples_common.h"
#if 1
#include "libcoap.h"
#include "coap_dtls.h"
#endif
#include "coap.h"
#include "driver/uart.h"
#include "driver/gpio.h"
#include "string.h"
#include
#include
#include
#include "cJSON.h"
#define COAP_DEFAULT_TIME_SEC 5
#define EXAMPLE_COAP_PSK_KEY CONFIG_EXAMPLE_COAP_PSK_KEY
#define EXAMPLE_COAP_PSK_IDENTITY CONFIG_EXAMPLE_COAP_PSK_IDENTITY
#define EXAMPLE_COAP_LOG_DEFAULT_LEVEL CONFIG_COAP_LOG_DEFAULT_LEVEL
#define COAP_DEFAULT_DEMO_URI CONFIG_EXAMPLE_TARGET_DOMAIN_URI
#define DHT11_PIN 15//定义DHT11的引脚
#define LED_PIN 2 //定义Led灯的引脚
#define uchar unsigned char
#define uint8 unsigned char
#define uint16 unsigned short
const static char *TAG = "CoAP_client";
void LED_Control(int i){
switch(i){
case 1 :
gpio_pad_select_gpio(LED_PIN);
gpio_set_direction(LED_PIN, GPIO_MODE_OUTPUT);
gpio_set_level(LED_PIN, 1);
break;
case 2 :
gpio_pad_select_gpio(LED_PIN);
gpio_set_direction(LED_PIN, GPIO_MODE_OUTPUT);
gpio_set_level(LED_PIN, 0);
break;
default:
ESP_LOGI(TAG, "错误:LED灯状态标识符识别失败!");
}
}
#ifdef CONFIG_COAP_MBEDTLS_PKI
extern uint8_t ca_pem_start[] asm("_binary_coap_ca_pem_start");
extern uint8_t ca_pem_end[] asm("_binary_coap_ca_pem_end");
extern uint8_t client_crt_start[] asm("_binary_coap_client_crt_start");
extern uint8_t client_crt_end[] asm("_binary_coap_client_crt_end");
extern uint8_t client_key_start[] asm("_binary_coap_client_key_start");
extern uint8_t client_key_end[] asm("_binary_coap_client_key_end");
#endif /* CONFIG_COAP_MBEDTLS_PKI */
// 监控响应处理
static void monitor_message_handler(coap_context_t *ctx, coap_session_t *session,
coap_pdu_t *sent, coap_pdu_t *received,
const coap_tid_t id)
{
unsigned char *data = NULL;
size_t data_len;
coap_opt_iterator_t opt_iter;
if (coap_check_option(received, COAP_OPTION_OBSERVE, &opt_iter)) {
coap_log(LOG_DEBUG,
"observation relationship established\n",
obs_seconds);
}
if (COAP_RESPONSE_CLASS(received->code) == 2) {
if (coap_get_data(received, &data_len, &data)) {
if ((int)data_len > 100) {
cJSON* root=cJSON_Parse((char *)data); //解析JSON字符串
cJSON* item=cJSON_GetObjectItem(root,"identifier"); //读取 identifier 字段
cJSON* deviceCode=cJSON_GetObjectItem(root,"deviceCode"); //读取 deviceCode 字段
//开启事件
if(strcmp(item->valuestring,"open") == 0){
ESP_LOGI(TAG, "开启设备");
LED_Control(1);
} else if(strcmp(item->valuestring,"open") == 1){
ESP_LOGI(TAG, "关闭设备");
LED_Control(2);
} else{
ESP_LOGI(TAG, "错误:调用设备服务,未知服务,该报文不做处理");
}
} else {
// printf("Received: %.*s\n", (int)data_len, data);
}
}
}
}
#ifdef CONFIG_COAP_MBEDTLS_PKI
static int
verify_cn_callback(const char *cn,
const uint8_t *asn1_public_cert,
size_t asn1_length,
coap_session_t *session,
unsigned depth,
int validated,
void *arg
)
{
coap_log(LOG_INFO, "CN '%s' presented by server (%s)\n",
cn, depth ? "CA" : "Certificate");
return 1;
}
#endif /* CONFIG_COAP_MBEDTLS_PKI */
// 监控open
static void monitor_open(void *p)
{
struct hostent *hp;
coap_address_t dst_addr;
static coap_uri_t uri;
const char *server_uri = &"coap://coap_server:5683/lqa5bihfks4fzkbn/service/open/z96g32s2i27hswxj";
char *phostname = NULL;
coap_optlist_t *optlist = NULL;
coap_set_log_level(EXAMPLE_COAP_LOG_DEFAULT_LEVEL);
while (1) {
#define BUFSIZE 100
unsigned char _buf[BUFSIZE];
unsigned char *buf;
size_t buflen;
int res;
coap_context_t *ctx = NULL;
coap_session_t *session = NULL;
coap_pdu_t *request = NULL;
optlist = NULL;
ESP_LOGI(TAG, "server_uri: %s", server_uri);
// 解析请求URI
if (coap_split_uri((const uint8_t *)server_uri, strlen(server_uri), &uri) == -1) {
ESP_LOGE(TAG, "CoAP server uri error");
break;
}
if (uri.scheme == COAP_URI_SCHEME_COAPS && !coap_dtls_is_supported()) {
ESP_LOGE(TAG, "MbedTLS (D)TLS Client Mode not configured");
break;
}
if (uri.scheme == COAP_URI_SCHEME_COAPS_TCP && !coap_tls_is_supported()) {
ESP_LOGE(TAG, "CoAP server uri coaps+tcp:// scheme is not supported");
break;
}
// 查询URI中远程主机地址
phostname = (char *)calloc(1, uri.host.length + 1);
if (phostname == NULL) {
ESP_LOGE(TAG, "calloc failed");
break;
}
// 分配内存
memcpy(phostname, uri.host.s, uri.host.length);
hp = gethostbyname(phostname);
free(phostname);
if (hp == NULL) {
ESP_LOGE(TAG, "DNS lookup failed");
vTaskDelay(1000 / portTICK_PERIOD_MS);
free(phostname);
continue;
}
char tmpbuf[INET6_ADDRSTRLEN];
// 初始化并配置地址
coap_address_init(&dst_addr);
switch (hp->h_addrtype) {
case AF_INET:
dst_addr.addr.sin.sin_family = AF_INET;
dst_addr.addr.sin.sin_port = htons(uri.port);
memcpy(&dst_addr.addr.sin.sin_addr, hp->h_addr, sizeof(dst_addr.addr.sin.sin_addr));
inet_ntop(AF_INET, &dst_addr.addr.sin.sin_addr, tmpbuf, sizeof(tmpbuf));
ESP_LOGI(TAG, "DNS lookup succeeded. IP=%s", tmpbuf);
break;
case AF_INET6:
dst_addr.addr.sin6.sin6_family = AF_INET6;
dst_addr.addr.sin6.sin6_port = htons(uri.port);
memcpy(&dst_addr.addr.sin6.sin6_addr, hp->h_addr, sizeof(dst_addr.addr.sin6.sin6_addr));
inet_ntop(AF_INET6, &dst_addr.addr.sin6.sin6_addr, tmpbuf, sizeof(tmpbuf));
ESP_LOGI(TAG, "DNS lookup succeeded. IP=%s", tmpbuf);
break;
default:
ESP_LOGE(TAG, "DNS lookup response failed");
goto clean_up;
}
if (uri.path.length) {
buflen = BUFSIZE;
buf = _buf;
// 解析请求路径
res = coap_split_path(uri.path.s, uri.path.length, buf, &buflen);
while (res--) {
coap_insert_optlist(&optlist,
coap_new_optlist(COAP_OPTION_URI_PATH,
coap_opt_length(buf),
coap_opt_value(buf)));
buf += coap_opt_size(buf);
}
}
if (uri.query.length) {
buflen = BUFSIZE;
buf = _buf;
// 解析查询
res = coap_split_query(uri.query.s, uri.query.length, buf, &buflen);
while (res--) {
// 安装查询
coap_insert_optlist(&optlist,
coap_new_optlist(COAP_OPTION_URI_QUERY,
coap_opt_length(buf),
coap_opt_value(buf)));
buf += coap_opt_size(buf);
}
}
// 创建context
ctx = coap_new_context(NULL);
if (!ctx) {
ESP_LOGE(TAG, "coap_new_context() failed");
goto clean_up;
}
if (uri.scheme == COAP_URI_SCHEME_COAPS || uri.scheme == COAP_URI_SCHEME_COAPS_TCP) {
#ifndef CONFIG_MBEDTLS_TLS_CLIENT
ESP_LOGE(TAG, "MbedTLS (D)TLS Client Mode not configured");
goto clean_up;
#endif /* CONFIG_MBEDTLS_TLS_CLIENT */
#ifdef CONFIG_COAP_MBEDTLS_PSK
session = coap_new_client_session_psk(ctx, NULL, &dst_addr,
uri.scheme == COAP_URI_SCHEME_COAPS ? COAP_PROTO_DTLS : COAP_PROTO_TLS,
EXAMPLE_COAP_PSK_IDENTITY,
(const uint8_t *)EXAMPLE_COAP_PSK_KEY,
sizeof(EXAMPLE_COAP_PSK_KEY) - 1);
#endif /* CONFIG_COAP_MBEDTLS_PSK */
#ifdef CONFIG_COAP_MBEDTLS_PKI
unsigned int ca_pem_bytes = ca_pem_end - ca_pem_start;
unsigned int client_crt_bytes = client_crt_end - client_crt_start;
unsigned int client_key_bytes = client_key_end - client_key_start;
coap_dtls_pki_t dtls_pki;
static char client_sni[256];
memset (&dtls_pki, 0, sizeof(dtls_pki));
dtls_pki.version = COAP_DTLS_PKI_SETUP_VERSION;
if (ca_pem_bytes) {
dtls_pki.verify_peer_cert = 1;
dtls_pki.require_peer_cert = 1;
dtls_pki.allow_self_signed = 1;
dtls_pki.allow_expired_certs = 1;
dtls_pki.cert_chain_validation = 1;
dtls_pki.cert_chain_verify_depth = 2;
dtls_pki.check_cert_revocation = 1;
dtls_pki.allow_no_crl = 1;
dtls_pki.allow_expired_crl = 1;
dtls_pki.allow_bad_md_hash = 1;
dtls_pki.allow_short_rsa_length = 1;
dtls_pki.validate_cn_call_back = verify_cn_callback;
dtls_pki.cn_call_back_arg = NULL;
dtls_pki.validate_sni_call_back = NULL;
dtls_pki.sni_call_back_arg = NULL;
memset(client_sni, 0, sizeof(client_sni));
if (uri.host.length) {
memcpy(client_sni, uri.host.s, MIN(uri.host.length, sizeof(client_sni)));
} else {
memcpy(client_sni, "localhost", 9);
}
dtls_pki.client_sni = client_sni;
}
dtls_pki.pki_key.key_type = COAP_PKI_KEY_PEM_BUF;
dtls_pki.pki_key.key.pem_buf.public_cert = client_crt_start;
dtls_pki.pki_key.key.pem_buf.public_cert_len = client_crt_bytes;
dtls_pki.pki_key.key.pem_buf.private_key = client_key_start;
dtls_pki.pki_key.key.pem_buf.private_key_len = client_key_bytes;
dtls_pki.pki_key.key.pem_buf.ca_cert = ca_pem_start;
dtls_pki.pki_key.key.pem_buf.ca_cert_len = ca_pem_bytes;
session = coap_new_client_session_pki(ctx, NULL, &dst_addr,
uri.scheme == COAP_URI_SCHEME_COAPS ? COAP_PROTO_DTLS : COAP_PROTO_TLS,
&dtls_pki);
#endif /* CONFIG_COAP_MBEDTLS_PKI */
} else {
// 创建session
session = coap_new_client_session(ctx, NULL, &dst_addr,
uri.scheme == COAP_URI_SCHEME_COAP_TCP ? COAP_PROTO_TCP :
COAP_PROTO_UDP);
}
if (!session) {
ESP_LOGE(TAG, "coap_new_client_session() failed");
goto clean_up;
}
// 绑定消息响应处理函数
coap_register_response_handler(ctx, monitor_message_handler);
// 根据session内容创建请求
request = coap_new_pdu(session);
if (!request) {
ESP_LOGE(TAG, "coap_new_pdu() failed");
goto clean_up;
}
request->type = COAP_MESSAGE_CON;
request->tid = coap_new_message_id(session);
request->code = COAP_REQUEST_GET; // 订阅服务器资源,发送GET请求
// 添加observe选项,开启观察者模式
uint8_t obs_buf[4];
coap_insert_optlist(&optlist,
coap_new_optlist(COAP_OPTION_OBSERVE,
coap_encode_var_safe(obs_buf, sizeof(obs_buf),
COAP_OBSERVE_ESTABLISH), obs_buf)
);
coap_add_optlist_pdu(request, &optlist);
// 发送请求
coap_send(session, request);
// 死循环检测当前context,服务器资源更新后立刻响应
while (1) {
coap_run_once(ctx, 100);
// sleep(2);
vTaskDelay(2000 / portTICK_PERIOD_MS);
}
clean_up:
if (optlist) {
coap_delete_optlist(optlist);
optlist = NULL;
}
if (session) {
coap_session_release(session);
}
if (ctx) {
coap_free_context(ctx);
}
coap_cleanup();
break;
}
vTaskDelete(NULL);
}
void app_main(void)
{
ESP_ERROR_CHECK(nvs_flash_init() );
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
ESP_ERROR_CHECK(example_connect());
// 启动监控open任务
xTaskCreate(monitor_open, "monitor_open", 8 * 1024, NULL, 5, NULL);
}
经过测试,本方案可行,通过引入RabbitMQ,提高了系统的可用性。
在本文中,RabbitMQ 扮演了非常重要的角色,它被用于实现服务间的异步通信和解耦,处理设备发送的数据,调度和执行定时任务,提高系统的可靠性和容错性。而 Californium 作为 CoAP 的 Java 实现,也提供了丰富的功能,为物联网设备提供了 REST 操作和消息观察。
libcoap客户端
copper客户端