简介这是一套基于Java实现的CoAP协议源码工程包含Android服务器端与Java服务器端并附有Java客户端测试代码。CoAP作为专为受限设备设计的轻量级物联网传输协议底层基于UDP具备资源发现如/.well-known/core、资源描述与GET/POST/PUT/DELETE操作等特性这套资源恰好覆盖了这些核心机制适合物联网开发者、协议学习者快速上手服务端搭建与请求调试。压缩包共297个文件以262个Java源码文件为主体辅以XML配置、PNG示意图、properties及Gradle构建脚本等整体仅582KB体积精巧、结构清晰便于直接导入工程研读。目前已有503人学习下载。读者可从中获取Android端CoAP服务器的完整目录结构、Java服务器与客户端测试代码并结合资源发现、增删改查操作示例理解UDP不可靠性下的重传事务处理机制为后续移植到嵌入式设备或与Web映射集成提供可直接参考的工程蓝本。1. 一份 CoAP 的 Java 服务端源码到底能帮你省掉什么做物联网对接的时候我经常被问一个问题设备端资源这么紧张为什么还要硬上 HTTPCoAP 这个协议就是为了解决这个问题存在的——它走 UDP报文头只有 4 个字节却提供一套类似 REST 的 GET/PUT/POST/DELETE 语义还支持设备发现、资源订阅、主动推送从 HTTP 转过来的开发者基本零门槛就能上手。这份「实现 CoAP 的 Java 源码」给的是服务器端那半边一套纯 Java 服务器端一套可以跑在 Android 上的服务器端把 CoAP 的请求处理、资源注册、观察推送都封装好了。适合正在做智能家居、传感器网关、边缘网关的开发者想自己收设备上报的数据或者想把 CoAP 服务端跑在手机/板子上做临时网关的人往下看。2. 先摸清 CoAP 的底为什么服务器端不自己硬啃 Socket很多人第一次接触 CoAP 时的第一反应是不就是 UDP 收包吗我直接开个 DatagramSocket 不就行了。这话对了一半——收包确实简单但 CoAP 不是裸 UDP它有自己的报文格式、可靠传输机制、观察者模型和资源发现流程这些只有全部实现到位才能跟市面上主流 CoAP 客户端互通。自己从零实现一遍少说要两三个星期直接站在成熟库的肩膀上一个下午就能把服务端跑起来。2.1 CoAP 和 HTTP 的本质差异四个方法、两类报文、一种订阅CoAP 设计上刻意模仿 HTTP但又针对物联网场景做了大量减法。方法只有四个GET、POST、PUT、DELETE语义和 HTTP 基本一致。报文分两类CONConfirmable需要确认和 NONNon-confirmable不需要确认。CON 报文发出去之后接收方必须回 ACK否则发送方会按指数退避重传这就是 CoAP 在 UDP 上实现可靠传输的核心手段。Observe 是 CoAP 最有价值的一个特性。HTTP 是典型的请求-响应模型客户端不主动拉就永远拿不到新数据设备端只能不停地轮询。CoAP 的 Observe 允许客户端在 GET 请求里带一个 Observe 选项注册观察之后服务器的资源一变化就主动把新数据推给所有观察者。这份源码在 Java 端和 Android 端都实现了这个机制做传感器数据上报的时候能省掉大量无意义的轮询流量。2.2 Java 生态选型Californium 和自研 Socket 的取舍我拆这份源码时第一件事就是看它底层用了什么库。果不其然服务端的核心是基于 Eclipse Californium 封装的。Californium 是 Java 生态里最主流的 CoAP 实现Eclipse 社区维护完整兼容 RFC 7252同时支持 Java SE 和 AndroidObserve、Blockwise 传输、CoAP over DTLS 这些高级特性都有现成接口。对比维度Californium自写 UDP SocketnCoAP 等轻量库RFC 7252 兼容完整部分实现基本兼容Observe 推送内置支持自己写观察者注册表需手动补充Blockwise 分块内置自己处理 0x1C/0x1D 选项较少支持Android 适配官方支持自己处理线程与生命周期适配一般维护活跃度Eclipse 社区持续更新无个人维护为主拿 CoAP 报文里的 Token 匹配来说自己写 Socket 时收到一个响应后要自己维护 Token 到请求的映射关系还要处理超时、重传、重复报文去重。Californium 把这些全部封装在 CoapServer 和 CoapClient 两个门面类里服务端只要关心资源逻辑。顺带提醒一句如果未来设备端和服务端之间要跑 DTLS 加密CoAPsCalifornium 配套有 Scandium 子项目加依赖就行。这套源码虽然有基础安全能力但生产环境建议把 DTLS 纳入评估。2.3 服务端要处理的核心状态从 CON 报文到资源回调CoAP 服务端的请求处理链路是这样的UDP 报文到达 5683 端口Californium 的协议栈先解析 CoAP 头判断消息类型CON 还是 NON如果是 CON 就自动回 ACK然后根据 URI 路径把请求路由到对应的资源处理器。资源处理器执行完业务逻辑后把响应交给协议栈协议栈再决定用 CON 还是 NON 发回给客户端。这一步的关键在于ACK 和业务响应是分离的。收到 CON 请求后协议栈会立刻回一个空 ACK 表示「收到了」业务代码在 handleGET 等回调里慢慢算算完后再单独发一个 CON 响应给客户端。如果业务计算超过 2 秒客户端可能已经按重传策略把同一个请求再发了一遍服务端要靠 Token 去重保证同一请求只处理一次。3. CoAPService 源码拆解服务端从启动到响应一个请求的完整路径这一章落到代码上。拿到源码包之后不要急着跑先把目录结构和启动流程过一遍后面排查问题会省很多事。3.1 源码包的工程结构先认清楚三个模块这份源码拆开之后核心是三个部分Java SE 服务器端模块、Android 服务模块、资源示例模块。Java SE 模块是独立的 main 方法可以打包成 jar 直接丢在 Linux 服务器上跑Android 模块是一个 Service 组件集成到任意 App 里就能把手机变成 CoAP 服务器。运行 Java 服务器端之前先把 JDK 环境确认好。命令行直接输java -version如果是 JDK 8 以上就满足要求如果提示找不到 java说明环境变量没配好去系统设置里把JAVA_HOME指到 JDK 安装目录再把%JAVA_HOME%\bin加进 PATH。Android 端则需要用 Android Studio 打开工程SDK 版本建议在 API 21 以上太老的版本对 UDP 多线程的支持不太友好。3.2 Java 服务器端三行代码启动 CoAP Server这份源码里最核心的启动代码就是用 Californium 的 CoapServer 创建实例并注册资源。常见的做法是下面这样// 创建 CoAP 服务器监听默认端口 5683 CoapServer server new CoapServer(5683); // 注册一个资源路径为 /sensors/temperature server.add(new TemperatureResource(temperature)); // 启动服务器开始接收 CoAP 请求 server.start();这里CoapServer(5683)指定了服务监听端口CoAP 的 IANA 默认端口就是 5683如果设备端固件里写死了端口服务端不要随便改。new TemperatureResource(temperature)注册了一个名为 temperature 的资源客户端访问路径就是coap://服务器IP:5683/sensors/temperature。server.start()内部会创建 UDP Socket 并启动接收线程之后的请求分发全部由框架处理。这段代码背后还有一层容易被忽略的细节start()方法是异步的它启动接收线程后立即返回。如果希望确认服务器真的起来了可以手动调用一次客户端连接测试或者看日志里 Californium 打印的启动信息。3.3 自定义一个 CoAP 资源handleGET 方法的完整逻辑资源类是服务端的业务核心。下面这个示例实现了读取温度并返回 JSON 的完整逻辑import org.eclipse.californium.core.CoapResource; import org.eclipse.californium.core.coap.CoAP.ResponseCode; import org.eclipse.californium.core.server.resources.CoapExchange; public class TemperatureResource extends CoapResource { public TemperatureResource(String name) { super(name); // 设置为可观察资源允许客户端订阅推送 setObservable(true); } Override public void handleGET(CoapExchange exchange) { // 从传感器读取温度的实际逻辑这里用固定值代替 double temperature 26.5; // 构造 JSON 响应 String payload {\sensor\:\temperature\,\value\: temperature }; // 响应客户端Content 表示 2.05 成功 exchange.respond(ResponseCode.CONTENT, payload); } }setObservable(true)这行很关键没有它客户端的 Observe 订阅请求会被直接忽略。handleGET是 GET 请求的入口CoapExchange对象里封装了请求的所有信息包括查询参数、Token、消息类型。exchange.respond(ResponseCode.CONTENT, payload)会发送响应并自动处理 ACK、重传等底层逻辑。参数说明ResponseCode.CONTENT对应 CoAP 的 2.05 响应码和 HTTP 的 200 语义一致。payload是字符串类型Californium 会自动编码为 UTF-8 字节流。如果要做 POST/PUT 处理重写handlePOST和handlePUT就行签名和handleGET一样。3.4 启动类的日志确认与异常排查源码包里的主类一般长这样直接运行就能看到启动日志public class ServerMain { public static void main(String[] args) { CoapServer server new CoapServer(5683); server.add(new TemperatureResource(temperature)); server.add(new SensorHubResource(sensorhub)); server.start(); System.out.println(CoAP server started on port 5683); } }启动之后如果控制台没有任何输出先检查端口是否被占用。Linux 上用netstat -anp | grep 5683Windows 上用netstat -ano | findstr 5683有进程占着就换个端口。日志里出现Exception in thread main java.net.BindException: Address already in use基本可以断定是前一个服务没退干净或者另一个程序先一步占用了端口。我一般会在启动类里强制加一条打印语句把注册的所有资源路径列出来确认每个资源都注册成功了避免客户端请求返回 4.04 Not Found 之后再回去翻代码。4. 把服务端塞进 Android进程、生命周期与权限的三重适配Android 端跑 CoAP 服务器和纯 Java 端完全是两个难度。Java 端只要 JDK 环境对一个 main 方法就能跑Android 端要处理权限声明、线程模型、Service 生命周期和后台回收任何一个地方没注意服务就悄无声息地死了。4.1 Android 权限与主线程限制先过两道关把 CoAP 服务端集成进 Android App第一步是权限声明。在AndroidManifest.xml里必须加上网络权限manifest xmlns:androidhttp://schemas.android.com/apk/res/android uses-permission android:nameandroid.permission.INTERNET / uses-permission android:nameandroid.permission.WAKE_LOCK / application service android:name.CoapServerService android:exportedfalse / /application /manifestINTERNET权限负责 UDP Socket 的创建和收发不加这个权限CoapServer 启动时会在 bind 阶段抛SecurityException。WAKE_LOCK权限用来在 CPU 休眠时保持运行做嵌入式网关场景时很有用。第二关是线程模型。Android 从 4.0 开始强制主线程UI 线程禁止做网络操作。虽然 CoapServer 的start()在网络线程池里跑接收逻辑理论上不撞 StrictMode但很多人在测试阶段会顺手写一个 CoapClient 在 Activity 里同步发 GET 请求这一下就会崩。解决方式很简单——所有和 CoAP 的交互都丢到子线程或者用 Handler 做线程切换。4.2 用 Service 承载 CoAP 服务前台服务与 START_STICKY 的选择CoAP 服务端如果只是一个普通的后台 ServiceAndroid 系统会在内存不足时把它回收掉。回收之后客户端再请求就超时而且不会自动重启。源码里比较规范的处理方式是用前台服务来解决。public class CoapServerService extends Service { private CoapServer coapServer null; Override public void onCreate() { super.onCreate(); startAsForeground(); startCoapServer(); } Override public int onStartCommand(Intent intent, int flags, int startId) { // START_STICKY 让服务被杀后系统尝试重建 return START_STICKY; } private void startAsForeground() { Notification notification new Notification.Builder(this, coap_channel) .setContentTitle(CoAP 服务运行中) .setContentText(正在监听 5683 端口) .build(); startForeground(1, notification); } private void startCoapServer() { new Thread(new Runnable() { Override public void run() { coapServer new CoapServer(5683); coapServer.add(new TemperatureResource(temperature)); coapServer.start(); } }).start(); } Override public void onDestroy() { if (coapServer ! null) { coapServer.stop(); coapServer.destroy(); } super.onDestroy(); } }代码里的notification是前台服务的通知栏常驻提示这是 Android 8.0 之后强制要求的用户能直观看到服务还在跑。START_STICKY表示系统杀掉服务后在有内存的情况下会尝试重建但注意它不会自动重启内部的 CoapServer所以startCoapServer()放在onStartCommand里比放在onCreate里更稳妥。coapServer.stop()和coapServer.destroy()这两个方法用途不同stop()停掉接收线程但保留配置destroy()释放全部资源。Android Service 销毁阶段最好两个都调避免内存泄漏。另外一个细节我在startCoapServer()里强制包了一层new Thread(...)因为有些旧版本的 Californium 在start()时会在当前线程做 Socket 初始化如果在onCreate里直接调等于在主线程做了网络操作严格模式会弹异常。4.3 Android 端调试ADB 看端口、看日志两不误服务跑起来之后先别急着写客户端用 ADB 确认服务端真的在监听。# 确认 5683 端口处于 LISTEN 状态 adb shell netstat -an | grep 5683 # 查看 CoAP 相关日志 adb logcat -s Californium # 如果 CPU 占用异常检查是不是收到大量无效 UDP 包 adb shell top -n 1 | grep javanetstat能看到0.0.0.0:5683的 LISTEN 记录说明服务端已经正常绑定端口。logcat里如果出现Californium标签的日志说明框架内部的协议栈在正常工作。这一步相当于给服务端做了个健康检查比直接上客户端测试更早发现问题。5. 避坑与排查CoAP 服务端跑不起来的五个常见原因在拆这份源码并实际部署的过程中我踩过不少坑。有些坑属于粗心大意有些是 Android 系统机制导致的还有几个纯粹是 CoAP 协议本身的特性。每一条都是实际的失败记录按「现象 → 原因 → 解决」列出来。5.1 现象CoapServer 一启动就抛 SecurityException第一次在 Android 工程里跑服务端时启动瞬间崩溃日志指向Socket.bind。原因很直接AndroidManifest.xml里漏掉了INTERNET权限。我们做 Java 服务器端时习惯性地以为本地 Socket 不需要权限Android 在这里跟桌面 Java 完全不同。解决方式是在 manifest 里补上uses-permission android:nameandroid.permission.INTERNET /保存后重新编译安装。另一种可能被忽视的原因是CoAP 端口如果绑定到 1024 以下端口普通 App 的 Android 进程没有 root 权限会直接失败。如果你图省事把端口改成了 80 或 443先改回 5683 或者换成 1024 以上的端口。5.2 现象服务启动后运行几分钟客户端就连接超时这个现象在 Android 上最容易出现。App 退到后台一段时间后再从前台切回来客户端就请求不到数据了。原因是系统内存压力下把后台 Service 回收了或者 CPU 休眠导致接收线程被挂起。解决的做法有两个层面第一把 Service 提升为前台服务保活能力有质的提升第二在onStartCommand里处理START_STICKY重启逻辑服务被杀后系统自动重建在onStartCommand里再次调startCoapServer()。如果服务端是跑在 Linux 服务器上的 Java 进程那基本可以排除系统回收问题。这时候去看是不是防火墙拦了 UDP 5683。CentOS/RHEL 用firewall-cmd --add-port5683/udp --permanent firewall-cmd --reloadUbuntu 检查ufw status很多云服务器的安全组默认不放行 UDP 端口。5.3 现象控制台报 Address already in use端口被占用是启动阶段的经典问题。有两个进程同时启动 CoAP 服务端或者上一个服务端进程没被完全终止都会触发这个错误。解决方式分三步先查占用进程Linux 用lsof -i :5683或netstat -anp | grep 5683Android 上用adb shell netstat -an | grep 5683然后杀掉旧进程最后等 30 秒确认端口完全释放再重启。如果确认没有其他进程占用还有一种隐藏情况Californium 的 CoapServer 在 Java 进程里被创建了两次两个实例都调用了start()。这种情况通常是因为onCreate和onStartCommand里各初始化了一次。解决方式是给服务加一个初始化标志位或者在onStartCommand里先判断coapServer ! null就直接返回。5.4 现象客户端能 get但 observe 订阅一直接不到推送这是最让人头大的一个问题。资源实现了handleGET能正常返回数据但客户端带上 Observe 选项订阅后服务器数据更新了客户端却收不到任何推送。我排查了很久才发现原因资源类里调用了changed()方法但没有调setObservable(true)。Californium 的设计里setObservable(true)是资源进入观察者模式的开关没有这个开关changed()调用会被直接忽略。另一个原因是客户端根本没有成功注册观察者。检查客户端是否真的发送了带 Observe 选项的 GET 请求——用 Wireshark 抓包看请求的 CoAP 报头里有没有 Observe 选项类型是 6。如果客户端用的是自己写的 CoAP 库这个选项很容易被漏掉换成 Californium 的 CoapClient 再测试一遍就清楚了。5.5 现象大数据包传输时客户端收到的数据和发送的不一致CoAP 的报文长度受 UDP 数据报大小限制。IPv4 下 UDP 承载的数据部分理论可以到 65507 字节但实际网络链路 MTU 通常在 1500 字节以内超过这个值报文会被 IP 层分片在网络环境差的场景下分片丢失率会明显上升。Californium 默认的maxMessageSize是 1024 字节用户数据部分超过这个值的响应会被自动按 Blockwise 分块传输但前提是资源返回的数据被正确交给了 Californium 的分块逻辑。如果手写 Socket 实现 CoAP 服务端Blockwise 分块需要自己实现客户端请求Block1和服务器响应Block2选项都要处理。这也是我建议尽量用 Californium 这类库的原因——分块逻辑、超时重传、Token 管理全部是经过大规模验证的。遇到大载荷场景直接把生成 JSON 的数据量控制在 800 字节以内最省心。6. 验证与进阶coap-client 三个命令确认服务端真的能用再考虑 Observe 推送服务端部署完之后我习惯性的第一个动作是用命令行客户端做冒烟测试而不是直接写集成代码。这样能在 30 秒内判断问题出在服务端还是客户端。先装一个 libcoap 命令行工具Ubuntu/Debian 上是apt-get install libcoap3-bin或者直接编译源码。然后执行三条命令# 1. GET 请求验证基本资源访问 coap-client -m get coap://127.0.0.1:5683/temperature # 2. POST 请求验证写入类接口 coap-client -m post -p {cmd:reboot} coap://127.0.0.1:5683/device # 3. 带 Observe 订阅看服务器能否主动推送 coap-client -m get -s 10 coap://127.0.0.1:5683/temperature-m指定方法-p指定请求体-s 10表示保持观察 10 秒。第三条命令能收到多条响应说明 Observe 推送正常只收到一条就是资源没调changed()或者没有开启setObservable(true)。进阶用法上我会把changed()的调用时机和数据更新逻辑绑定在一起。比如温度资源每 5 秒读一次传感器读数变化超过 0.5 度才触发推送而不是每次都广播这样能显著降低无效 UDP 报文和客户端功耗。对于观察者数量多的场景Californium 默认的资源管理会自动处理观察者过期清理但深度休眠的客户端还是建议定期确认或者实现客户端重订阅机制。另外可以考虑 Wireshark 抓包验证在服务端所在机器上抓 UDP 5683 端口看 CON/ACK 的交互序列是否符合预期。CoAP 的调试就是这样——协议栈帮你隐藏了细节但也让你更难确认问题在哪层。从那以后我每次部署完服务端不管用没用到命令行工具都会强制走一遍 coap-client Wireshark 的验证流程先确认链路真的通再往上叠业务逻辑。希望帮到你。本文还有配套的精品资源点击获取
企业数字化 ERP 产品动态
相关推荐
模拟IC设计实战:从LDO内部原理到版图匹配与工艺角仿真 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:02:54
Connected Papers:用引文网络图谱高效搞定文献综述 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:02:42
Windows下Neo4j 5.26.0安装配置、知识图谱与避坑指南 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:02:36
从 CHANGES.md 看 Tekton Pipeline 仓库中 go-restful v3 的十年演进史 云原生CI/CDDevOps后端 【免费下载链接】pipeline A cloud-native Pipeline resource. 项目地址: https://gitcode.com/gh_mirrors/pipelin/pipeline 点击查看 免费下载 Tekton Pipeline 仓库通过 vendor 机制内置了 Go REST 框架 go-restful 的完整实现࿰… · 2026/9/25 1:37:09
芯片设计方法演化史:从手工版图到RTL与Chiplet /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:37:03
MXSPyCOM源代码拆解:Python调3ds Max的COM桥实战笔记 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:37:03
oclif readme 命令完全指南:自动生成与维护 CLI 项目的 README 文档 开发工具 【免费下载链接】oclif CLI for generating, building, and releasing oclif CLIs. Built by Salesforce. 项目地址: https://gitcode.com/gh_mirrors/oc/oclif 点击查看 免费下载 oclif readme 是 oclif(Salesforce 开源的 Open CLI Framewor… · 2026/9/25 1:37:02
Codex Router完全指南:如何在Codex中用上Kimi、DeepSeek、Claude等30+外部AI模型 Codex Router完全指南:如何在Codex中用上Kimi、DeepSeek、Claude等30外部AI模型 【免费下载链接】codex-router External-model router for Codex with guided Kimi OAuth/API, DeepSeek, safe migration, and rollback. 项目地址: https://gitcode.com/gh_mirror… · 2026/9/25 1:37:02
创维E900V22D刷机全攻略:S905L3SB芯片兼容性解析与救砖实战 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:31
MQTT协议原理与Broker服务器搭建实战:从Mosquitto到EMQX /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:37