Java实现UDP可靠传输:解决丢包乱序重复的生产级方案
简介本资源是一套基于Java实现的UDP可靠通信系统完整源码面向网络编程初学者与中级开发者解决UDP协议天然不可靠性带来的丢包、乱序、无确认等核心问题。项目通过序列号机制、超时重传、CRC校验及简易流量控制在UDP基础上构建类TCP的可靠性保障适用于即时通讯、轻量级物联网传输等对实时性与可控性均有要求的场景。压缩包含132个文件主体为43个Java源文件与63个编译后class文件辅以9个XML配置、6个GIF界面资源及3个JAR依赖库整体1.13MB结构清晰分为Client与Server两大模块涵盖DatagramSocket通信、数据包序列化、连接线程管理、好友列表与消息收发等关键逻辑。内容预览显示存在DataPacket、ClientConnectionThread、ServerMessageThread等核心类体现分层设计与状态管理思想。目前已有187人学习下载可直接导入IDE运行调试是理解UDP增强型通信原理与Java网络编程实践的优质教学范例。1. 为什么用 Java 做 UDP 可靠通讯不是“造轮子”而是解决真实丢包、乱序、重复的硬需求你手头有个嵌入式设备上报心跳每秒发一个 UDP 包或者在局域网内做实时音视频前传要求端到端延迟 50ms又或者在工业 PLC 通信中TCP 握手开销太大、重传机制太慢但裸 UDP 又总丢包——这时候“Java 基于 UDP 协议的可靠通讯系统”就不是学术玩具而是能立刻上线的生产级补丁。它不替换 TCP而是在 UDP 之上叠加 ACK、滑动窗口、超时重传、序列号校验、去重缓存等机制把不可靠的 UDP 拉到“类 TCP 级别”的交付质量同时保留 UDP 的零握手、低延迟、无连接优势。本方案面向 Java 后端/边缘网关开发者不依赖 Netty 或 Spring Integration 这类重型框架核心逻辑可独立打包为 JAR嵌入 IoT 平台、监控 Agent 或自研中间件。源码结构清晰UdpReliableChannel封装收发通道ReliablePacket定义带 SEQ/ACK/FLAG 的协议帧RetransmitManager控制重传策略InOrderBuffer解决乱序交付——所有代码纯 Java 实现JDK 8 可直接编译运行无需 JNI 或 native 库。2. 从零构建可靠 UDP 通道协议设计、状态机与核心类骨架2.1 协议帧格式为什么必须自定义头部而不是套用 TCP 字段UDP 原生只有 8 字节头部源端口、目的端口、长度、校验和无法承载序列号、确认号、窗口大小、重传标志等可靠传输必需信息。因此我们定义ReliablePacket类其二进制布局严格对齐网络字节序Big-Endian共 24 字节固定头 可变负载偏移长度字段名含义示例值04magic协议魔数用于快速过滤非法包0x4A525550JRUP ASCII44seqNum发送方序列号从 0 开始递增1234584ackNum最新收到的对方 seqNum仅 ACK 包有效12344122flags位掩码SYN(0x01)、ACK(0x02)、FIN(0x04)、RETRANS(0x08)0x03SYNACK142window接收窗口大小单位字节用于流控65535164crc32负载 头部的 CRC32 校验值0x8A3F2B1C204payloadLen实际负载长度≤6553532提示magic字段是防错第一道防线——接收端收到非 JRUP 开头的 UDP 包直接丢弃避免误解析普通 DNS 或 NTP 包导致状态机崩溃。CRC32 必须覆盖整个ReliablePacket含头部否则重传时因网络扰动导致头部字段翻转却未被检测会引发 ACK 错位、窗口错乱等连锁故障。2.2 连接建立与状态机三次握手机制如何适配无连接 UDPTCP 的三次握手依赖内核维护连接状态而 UDP 无状态我们必须在应用层模拟。UdpReliableChannel内部维护ConnectionState枚举public enum ConnectionState { CLOSED, // 初始态未发起连接 SYN_SENT, // 已发 SYN等待 SYN-ACK ESTABLISHED, // 收到 SYN-ACK发送 ACK进入数据传输态 FIN_WAIT_1, // 主动关闭已发 FIN等待对方 ACK CLOSE_WAIT, // 被动关闭收到 FIN需发 ACK 自己 FIN TIME_WAIT // 主动关闭方最后状态等待 2MSL 防止旧包干扰 }连接建立流程主动方视角调用connect(InetSocketAddress remote)→ 状态切SYN_SENT构造ReliablePacketflagsSYNseqNum0ackNum0发送至远端启动SYN_TIMEOUT 3000ms定时器若超时未收SYN-ACK重发 SYN最多 3 次收到flags (SYN|ACK)且ackNum 1即确认了我们的 SYN→ 状态切ESTABLISHED立即回ACKflagsACKackNum当前 seqNum1若收到flags ACK但ackNum ! 1视为非法包丢弃。注意seqNum和ackNum在握手阶段强制为 0/1避免初始序列号协商复杂化真正数据传输从seqNum1开始编号ackNum始终表示“期望收到的下一个包的 seqNum”。2.3 核心通道类UdpReliableChannel 的生命周期与线程模型UdpReliableChannel是可靠 UDP 的门面类封装DatagramSocket、重传调度器、接收缓冲区。关键设计点单 socket 复用一个UdpReliableChannel实例绑定唯一DatagramSocket通过InetSocketAddress区分不同对端避免频繁创建 socket 的开销双线程驱动receiverThread专职阻塞读取DatagramPacket并解析为ReliablePacketsenderThread从sendQueue取包发送并管理重传队列retransmitQueue资源安全释放close()方法需同步关闭 socket、中断 receiverThread、清空所有队列、取消所有 ScheduledFuture重传定时器否则残留线程会持续占用 CPU 和内存。public class UdpReliableChannel { private final DatagramSocket socket; private final ScheduledExecutorService scheduler; // 用于重传定时任务 private final BlockingQueueReliablePacket sendQueue; // 待发送队列 private final MapInteger, RetransmitEntry retransmitQueue; // seqNum - 待重传项 private volatile ConnectionState state ConnectionState.CLOSED; public UdpReliableChannel(int localPort) throws SocketException { this.socket new DatagramSocket(localPort); this.scheduler Executors.newScheduledThreadPool(1, r - new Thread(r, udp-retransmit-scheduler)); this.sendQueue new LinkedBlockingQueue(); this.retransmitQueue new ConcurrentHashMap(); } // ... connect(), send(), receive(), close() 方法实现 }scheduler线程池大小设为 1 是关键——重传任务必须串行执行否则并发修改retransmitQueue中的nextRetryTime可能导致漏重传或重复重传。3. 关键机制落地滑动窗口、超时重传与乱序重组3.1 滑动窗口实现用 CircularBuffer 管理接收与发送窗口TCP 窗口是动态调整的但本方案采用静态接收窗口 动态发送窗口简化实现接收窗口receiveWindow固定大小 64KB即window 65535由InOrderBuffer管理。它本质是一个CircularBufferReliablePacket索引按seqNum % windowSize映射支持 O(1) 插入与按序提取发送窗口sendWindow初始为min(64KB, MSS)MSSMaximum Segment Size取65507 - 24UDP 最大负载 65507 字节减去协议头 24 字节≈65483。发送方根据ackNum和window字段动态计算可发送上限canSendBytes Math.min(sendWindow, remoteWindow - (nextSeqNum - lastAckNum))。InOrderBuffer核心逻辑public class InOrderBuffer { private final ReliablePacket[] buffer; // size 65536 private final AtomicInteger baseSeq; // 当前期望的最小 seqNum private final AtomicInteger nextSeq; // 下一个待插入位置 public void put(ReliablePacket packet) { int offset (packet.getSeqNum() - baseSeq.get()) 0xFFFF; if (offset buffer.length offset 0) { buffer[offset] packet; // 尝试向前推进 baseSeq交付连续包 while (buffer[baseSeq.get() 0xFFFF] ! null) { deliver(buffer[baseSeq.get() 0xFFFF]); baseSeq.incrementAndGet(); } } } private void deliver(ReliablePacket packet) { // 交给上层业务处理器如applicationHandler.handle(packet.getPayload()); } }注意baseSeq和nextSeq使用AtomicInteger保证多线程安全 0xFFFF替代% buffer.length提升性能因 buffer.length655362^16deliver()是回调实际业务需实现ApplicationHandler接口。3.2 超时重传策略指数退避 最大重传次数硬限重传不是简单“超时就发”需平衡及时性与网络拥塞基础超时时间RTO初始设为1000ms每次重传后乘以backoffFactor 2.0即 1s → 2s → 4s → 8s最大重传次数maxRetries设为5第 5 次失败后触发ConnectionLostEvent通知上层断连重传触发条件RetransmitEntry中nextRetryTime System.currentTimeMillis()且retryCount maxRetries。RetransmitEntry结构class RetransmitEntry { final ReliablePacket packet; final long firstSentTime; // 首次发送时间戳 long nextRetryTime; // 下次重试时间戳 int retryCount; // 已重试次数 final InetSocketAddress remoteAddress; RetransmitEntry(ReliablePacket p, InetSocketAddress addr) { this.packet p; this.remoteAddress addr; this.firstSentTime System.currentTimeMillis(); this.nextRetryTime this.firstSentTime 1000; // 初始 RTO1s this.retryCount 0; } void scheduleNextRetry() { this.retryCount; this.nextRetryTime System.currentTimeMillis() (long)(1000 * Math.pow(2, this.retryCount)); } }提示nextRetryTime在scheduleNextRetry()中重新计算而非简单因为网络延迟波动大必须基于当前时间重算避免累积误差导致重传过早或过晚。3.3 乱序包处理基于 seqNum 的去重与缓存UDP 天然乱序InOrderBuffer已解决交付顺序但还需防重复包重复判断接收方维护lastReceivedSeq最近成功交付的 seqNum新包若seqNum lastReceivedSeq且已在InOrderBuffer中存在则丢弃缓存清理InOrderBuffer中超过baseSeq windowSize的旧包自动失效避免内存泄漏ACK 生成逻辑收到任意包包括重复包都立即回复 ACKACK 的ackNum始终为baseSeq.get()即期望的下一个 seqNum确保发送方能准确感知接收进度。// 在 receiverThread 的主循环中 if (packet.getSeqNum() inOrderBuffer.getBaseSeq()) { inOrderBuffer.put(packet); } else if (packet.getSeqNum() inOrderBuffer.getBaseSeq()) { // 正好是期望包直接交付 deliver(packet); inOrderBuffer.advanceBaseSeq(); } else { // seqNum baseSeq必为重复包或已交付包 // 但仍需回复 ACK维持窗口滑动 } sendAck(packet.getRemoteAddress(), inOrderBuffer.getBaseSeq());4. 避坑指南5 个血泪经验总结的典型故障与修复4.1 现象连接始终卡在 SYN_SENTWireshark 显示 SYN 包发出但无响应原因防火墙或路由器拦截了 UDP 端口或远端服务未监听该端口更隐蔽的是DatagramSocket绑定时未指定setReuseAddress(true)导致端口被 TIME_WAIT 状态占用重启服务后无法立即复用。解决检查netstat -anu | grep :port确认端口监听在UdpReliableChannel构造函数中添加this.socket.setReuseAddress(true); // 允许 TIME_WAIT 端口复用 this.socket.setSoTimeout(5000); // 设置 recv 超时避免 receiverThread 阻塞4.2 现象高并发下sendQueue持续积压CPU 占用 100%但无数据发出原因senderThread中socket.send()调用未加 try-catch当DatagramSocket被意外关闭如close()被其他线程调用send()抛出SocketException后线程退出无人消费sendQueue。解决senderThread主循环必须包裹try-catch(SocketException e)捕获后记录日志并break退出线程close()方法中需sendQueue.clear()并sendQueue.offer(new PoisonPillPacket())毒丸包通知 senderThread 优雅退出。4.3 现象接收端偶尔交付重复数据业务逻辑处理两次原因InOrderBuffer.put()中offset计算未考虑seqNum回绕UDP 序列号 32 位约 42 亿后归零baseSeq与packet.seqNum相减可能为负 0xFFFF无法正确映射。解决改用IntMath.modulo(packet.getSeqNum() - baseSeq.get(), buffer.length)Guava 的IntMath或手动处理回绕int diff packet.getSeqNum() - baseSeq.get(); int offset (diff 0) ? diff buffer.length : diff; if (offset buffer.length) buffer[offset] packet;4.4 现象大文件传输时retransmitQueue内存暴涨OOM原因重传包未及时从retransmitQueue移除——ACK到达后仅清除了对应seqNum的 entry但未同步清除sendQueue中已确认的原始包引用导致ReliablePacket.payload可能为大 byte[]长期驻留堆内存。解决RetransmitEntry中packet字段改为弱引用WeakReferenceReliablePacket或在ACK处理逻辑中遍历sendQueue移除已确认的包需加锁影响性能慎用更优解ReliablePacket的payload使用ByteBuffer.allocateDirect()减少 GC 压力。4.5 现象跨公网通信时偶发连接闪断日志显示ConnectionLostEvent原因公网 NAT 设备对 UDP 会话有超时通常 30-120 秒长时间无数据交互NAT 表项老化后续 ACK 包被丢弃。解决实现心跳保活UdpReliableChannel启动heartbeatScheduler每HEARTBEAT_INTERVAL 25000ms发送空flagsACK包心跳包不占sendWindow不参与重传仅维持 NAT 映射。5. 性能调优与边界验证吞吐量压测、时延分布与生产部署技巧5.1 压测方法论iperf3 对标 自定义流量生成器不能只跑“Hello World”要验证真实场景对标 TCP用iperf3 -c server -u -b 100M测 UDP 原生丢包率再用本系统UdpReliableChannel发送相同流量对比有效吞吐单位MB/s和端到端 P99 时延自定义压测工具编写StressTestClient启动 100 个UdpReliableChannel并发连接同一服务端每个 channel 每秒发送 100 个 1KB 包持续 5 分钟统计成功交付率deliveredCount / sentCount平均重传次数/包retransmitQueue.size()峰值GC 暂停时间jstat -gc pid。关键参数调优表参数默认值生产建议值调整依据SYN_TIMEOUT3000ms5000ms公网 RTT 波动大避免误判连接失败RTO_INITIAL1000ms200ms局域网环境可激进提升响应速度MAX_RETRIES53公网丢包率高时设 5局域网设 3 减少无效重传RECEIVE_WINDOW6553532768内存受限设备可减半牺牲吞吐保稳定性HEARTBEAT_INTERVAL25000ms15000ms阿里云 SLB UDP 会话超时为 60s需留余量5.2 时延分析用System.nanoTime()打点定位瓶颈在UdpReliableChannel.send()入口、senderThread发送前、receiverThread收到后、InOrderBuffer.deliver()入口各打一次nanoTime()计算差值发送路径耗时send() → socket.send()应 1ms本地 loopback或 5ms千兆局域网接收路径耗时socket.receive() → deliver()应 2ms若 10ms检查InOrderBuffer.put()是否因锁竞争或 GC 导致延迟重传引入额外时延对比firstSentTime与deliverTime若 P95 2*RTO说明网络抖动严重需调大RTO_INITIAL。5.3 生产部署 checklist从开发到上线的 7 个硬性动作JVM 参数固化-Xms2g -Xmx2g -XX:UseG1GC -XX:MaxGCPauseMillis50避免 GC 导致senderThread卡顿Socket 选项优化socket.setReceiveBufferSize(2*1024*1024)2MB 接收缓冲区socket.setSendBufferSize(1*1024*1024)1MB 发送缓冲区线程优先级隔离receiverThread.setPriority(Thread.MAX_PRIORITY)确保及时响应网络事件日志分级DEBUG 级打印seqNum/ackNum/window变化ERROR 级只记录ConnectionLostEvent和OOMEMetrics 上报集成 Micrometer暴露udp_reliable_send_queue_size、udp_reliable_retransmit_count_total、udp_reliable_rtt_ms等指标配置外置化将RTO_INITIAL、MAX_RETRIES等参数放入application.yml支持运行时动态刷新通过RefreshScope灰度发布新版本先切 5% 流量监控deliveredRate和retransmitRate达标后再全量。我在线上跑过三年最深的教训是永远相信网络会丢包但不要迷信重传能解决一切——真正的可靠性来自“快速失败 业务兜底”比如心跳超时后自动切换备用通道比死磕重传更有效。这套 UDP 可靠通讯系统我把它当作 TCP 的轻量替代品用在设备直连、边缘计算节点间通信这些对延迟敏感、但又不能容忍丢包的场景。它不完美但足够健壮它不炫技但能扛住真实流量。希望帮到你。本文还有配套的精品资源点击获取

相关新闻

手写论文三星期,AI辅助两小时?效率差距背后的真实逻辑

手写论文三星期,AI辅助两小时?效率差距背后的真实逻辑

我们来算一笔账。写一篇八千字的课程论文,如果全程手写,大概需要这样分配时间:找资料和读文献两到三天,列大纲半天,写正文四到五天,修改润色一天,调整格式半天,前后加起来至少一周。…

2026/10/11 4:05:36 阅读更多 →
校园代理招募启动:学生群体里的靠谱副业,从一款刚需工具开始

校园代理招募启动:学生群体里的靠谱副业,从一款刚需工具开始

大学生副业这个话题,每年都能在校园论坛上掀起一轮讨论。家教、发传单、做微商、开网店,各种模式层出不穷,但真正适合学生、时间灵活、又能长期积累的其实不多。家教受限于学科能力和地域,发传单纯粹是体力消耗,微商容…

2026/10/11 2:34:46 阅读更多 →
影刀RPA新手教程:数据脱敏与隐私保护实战——合规处理敏感数据

影刀RPA新手教程:数据脱敏与隐私保护实战——合规处理敏感数据

影刀RPA新手教程:数据脱敏与隐私保护实战——合规处理敏感数据 0. 为什么要做脱敏 采集回来的数据里可能全是敏感信息:客户手机号、身份证号、邮箱、家庭地址、银行卡号。直接存是违规的(个人信息保护法第17条),但要分…

2026/10/12 3:32:03 阅读更多 →

最新新闻

Linux命令查阅与文件操作实战:man、--help、apropos及ls、cp、mv、rm避坑指南

Linux命令查阅与文件操作实战:man、--help、apropos及ls、cp、mv、rm避坑指南

1. 为什么命令查阅能力比死记硬背更重要刚接触Linux那会儿,我跟很多人一样,抱着厚厚一本命令手册从第一页开始啃。结果呢?背了忘,忘了背,真正上机操作时脑子里一片空白。后来带我的前辈说了一句话点醒了我:…

2026/10/12 15:01:42 阅读更多 →
Oracle数据模板卸载Shell脚本实战:可重跑、可回滚的自动化方案

Oracle数据模板卸载Shell脚本实战:可重跑、可回滚的自动化方案

简介:这份资源是一套面向数据仓库与ETL开发人员的Oracle数据卸载Shell脚本模板,适合需要定期将库内数据导出为文本并做后续处理的工程师使用。包内共4个文件,以sh脚本、txt模板与config配置文件为主,压缩包约4KB,体积轻…

2026/10/12 15:01:42 阅读更多 →
ARouter服务发现机制:组件化架构下获取Fragment的最佳实践

ARouter服务发现机制:组件化架构下获取Fragment的最佳实践

1. 为什么要通过路由拿Fragment而不是直接new1.1 组件化架构下的依赖困境先说一个很典型的场景。如果你所在的App是组件化架构,主工程需要加载一个来自“用户模块”的Fragment,而主工程在编译期不应该依赖用户模块的代码,因为那样一来所谓的组…

2026/10/12 15:01:41 阅读更多 →
LangChain社区实用组件盘点:文档问答系统落地实战

LangChain社区实用组件盘点:文档问答系统落地实战

LangChain社区这块地方,越逛越觉得藏了不少好东西。大多数人刚开始接触LangChain时,基本只碰官方文档里出现频率最高的那几样:链、提示模板、输出解析器,再套一个模型就算完事。但如果真在社区里多泡上一段时间,你会发…

2026/10/12 15:01:41 阅读更多 →
Windows版OpenClaw最新安装教程:接入本地Ollama模型与飞书机器人配置

Windows版OpenClaw最新安装教程:接入本地Ollama模型与飞书机器人配置

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 15:01:41 阅读更多 →
YOLOv8 RTSP流目标检测实战:从拉流到推理的工程化落地

YOLOv8 RTSP流目标检测实战:从拉流到推理的工程化落地

简介:本资源面向计算机视觉开发者与视频监控、智能交通、工业自动化方向的工程人员,提供YOLOv8结合RTSP实时流做目标检测的完整工程包,解决从拉流、推理到结果展示的落地问题。压缩包共467个文件,约169.25MB,以145个py…

2026/10/12 15:00:39 阅读更多 →

日新闻

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

在数码相机、高清显示屏与现代矢量图形技术高度发达的今天,画面可以做到绝对的锐利、平滑与无瑕。然而,当一张秋日手账插画或拍立得照片过于“平整无瑕”时,往往会散发出一种冰冷生硬的“数码塑料感(Digital Plasticity&#xff0…

2026/10/12 0:00:59 阅读更多 →
活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

在现代网页与移动端设计中,横排(Horizontal Layout)早已经成为了绝对的主流。然而,当我们翻开泛黄的线装古籍、宋版木刻诗集,或是欣赏一张茶道雅集的手写便签时,那种**自上而下纵向书写、自右向左逐列铺展&…

2026/10/12 0:00:59 阅读更多 →
周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

每到周日的晚上八点到十点,很多人心里都会悄悄亮起一盏警示灯。 在心理学上,这种现象有一个专门的称谓——“周日夜晚焦虑症(Sunday Scaries)”。明天又是周一,闹钟又要重新在七点响彻卧房;脑海里仿佛有一个…

2026/10/12 0:00:59 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/12 0:16:30 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/12 0:16:43 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 10:45:37 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 14:48:49 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 14:36:54 阅读更多 →