Suricata 应用层解析器(App Layer Parser)开发指南:回调协议与 AppLayerResult 返回值语义
网络安全【免费下载链接】suricataSuricata is a network Intrusion Detection System, Intrusion Prevention System and Network Security Monitoring engine developed by the OISF and the Suricata community.项目地址https://gitcode.com/gh_mirrors/su/suricata点击查看免费下载导读本文是 Suricata 开发者指南doc/userguide/devguide/extending/app-layer/中“Parsers”一章的完整展开面向希望为 Suricata 扩展自定义应用层协议解析器的开发者。文章以解析器回调函数签名为骨架系统讲解AppLayerResult三种返回值APP_LAYER_OK/APP_LAYER_ERROR/APP_LAYER_INCOMPLETE的精确语义、consumed与needed的字节级计算规则并结合仓库源码C 与 Rust 两套实现给出可复制、可运行的示例。读完本文你将能正确实现一个遵守 Suricata 应用层 API 约束的协议解析回调并理解不完整数据INCOMPLETE机制在 TCP 流重组中的底层原理。1. 解析器回调API 在程序启动时注册的调用入口Suricata 的应用层App Layer框架通过回调机制驱动协议解析。这些回调在程序启动阶段setup 阶段完成注册运行时由流引擎与数据包处理路径统一调度解析器自身并不关心数据来自 TCP 重装流还是 UDP 数据报。1.1 回调函数原型解析回调的标准函数原型定义如下typedef AppLayerResult (*AppLayerParserFPtr)(Flow *f, void *protocol_state, AppLayerParserState *pstate, const uint8_t *buf, uint32_t buf_len, void *local_storage, const uint8_t flags);各参数含义Flow *f当前流对象携带流级状态方向、协议映射、文件标志等void *protocol_state协议私有状态如 HTTP 的htp_state由协议的StateAlloc回调创建并随流生命周期存在AppLayerParserState *pstate由应用层框架维护的解析器状态包含解码事件、事务进度等信息buf/buf_len本次交付给解析器的数据及其长度重组后的 TCP 流数据或 UDP 载荷local_storage解析器注册的本地存储flags数据方向等标志如STREAM_TOSERVER/STREAM_TOCLIENT。当前仓库的签名演进在本仓库的 src/app-layer-parser.h 中实际签名已把buf/buf_len/flags封装为StreamSlice结构体typedef struct StreamSlice { const uint8_t *input; uint32_t input_len; /// STREAM_* flags uint8_t flags; uint64_t offset; } StreamSlice; typedef AppLayerResult (*AppLayerParserFPtr)(Flow *f, void *protocol_state, AppLayerParserState *pstate, StreamSlice stream_slice, void *local_storage);StreamSlice在 src/app-layer-parser.h 中定义并通过StreamSliceGetData()/StreamSliceGetDataLen()等内联函数见 src/app-layer-parser.h访问数据。它额外携带offset流内偏移供解析器在跨分片处理时定位。编写新解析器时应以本仓库当前签名为准。1.2 C 语言示例HTTP 解析器是 C 侧最典型的实现。它的请求数据处理函数签名如下见 src/app-layer-htp.cstatic AppLayerResult HTPHandleRequestData(Flow *f, void *htp_state, AppLayerParserState *pstate, const uint8_t *input, uint32_t input_len, void *local_data, const uint8_t flags);该回调通过AppLayerParserRegisterParser()在注册阶段绑定到IPPROTO_TCP, ALPROTO_HTTP1, STREAM_TOSERVER方向见 src/app-layer-htp.c。1.3 Rust 语言示例Rust 侧解析器以#[no_mangle]导出 C ABI 函数与 C 原型一一对应。文档给出的 DNS TCP 响应解析示例#[no_mangle] pub extern C fn rs_dns_parse_response_tcp(_flow: *const core::Flow, state: *mut std::os::raw::c_void, _pstate: *mut AppLayerParserState, input: *const u8, input_len: u32, _data: *mut std::os::raw::c_void, _flags: u8) - AppLayerResult在本仓库的 rust/src/applayer.rs 中Rust 侧统一封装为ParseFn类型同样采用StreamSlice传递数据pub type ParseFn unsafe extern C fn( flow: *mut Flow, state: *mut c_void, pstate: *mut AppLayerParserState, stream_slice: StreamSlice, data: *mut c_void, ) - AppLayerResult;更贴近日常开发的是 rust/src/applayertemplate/template.rs 中parse_request的写法——解析器内部直接操作[u8]切片并在需要更多数据时返回AppLayerResult::incomplete(consumed, needed)fn parse_request(mut self, input: [u8]) - AppLayerResult { // Were not interested in empty requests. if input.is_empty() { return AppLayerResult::ok(); } // ... let mut start input; while !start.is_empty() { match parser::parse_message(start) { Ok((rem, request)) { start rem; let mut tx self.new_tx(); tx.request Some(request); // ... self.transactions.push_back(tx); } Err(nom::Err::Incomplete(_)) { // 数据不足先消费已解析的部分再要求更多字节 let consumed input.len() - start.len(); let needed start.len() 1; return AppLayerResult::incomplete(consumed as u32, needed as u32); } Err(_) { return AppLayerResult::err(); } } } // Input was fully consumed. return AppLayerResult::ok(); }该模板还展示了事务transaction的创建、事件上报set_event与TEMPLATE_MAX_TX上限约束是完整的 Rust 解析器骨架可直接作为新协议开发的起点。2. AppLayerResult解析器的唯一返回值所有解析回调统一返回AppLayerResult类型。在 C 侧其结构体定义位于 src/app-layer-parser.htypedef struct AppLayerResult { int32_t status; // 状态码0 OK-1 ERROR1 INCOMPLETE uint32_t consumed; // INCOMPLETE 时已消费的字节数 uint32_t needed; // INCOMPLETE 时还需要多少字节 } AppLayerResult;三种结果的可能取值与对应宏定义见 src/app-layer-parser.hC 返回值Rust 返回值语义APP_LAYER_OKAppLayerResult::ok()解析器成功消费了本次数据APP_LAYER_ERRORAppLayerResult::err()解析器遇到不可恢复错误本流停止处理APP_LAYER_INCOMPLETE(c,n)AppLayerResult::incomplete(c,n)解析器消费了c字节还需要n字节才会再次被调用Rust 侧这三种构造方法由AppLayerResultRusttrait 提供见 rust/ffi/src/applayer.rs其底层实现直接映射 C 结构体impl AppLayerResultRust for AppLayerResult { /// parser has successfully processed in the input, and has consumed all of it fn ok() - Self { Default::default() // status 0 } /// parser has hit an unrecoverable error... fn err() - Self { AppLayerResult { status: -1, ..Default::default() } } /// parser needs more data... fn incomplete(consumed: u32, needed: u32) - Self { Self { status: 1, consumed, needed } } }C 侧宏定义与之等价见 src/app-layer-parser.h#define APP_LAYER_OK (AppLayerResult) { 0, 0, 0 } #define APP_LAYER_ERROR (AppLayerResult) { -1, 0, 0 } #define APP_LAYER_INCOMPLETE(c,n) (AppLayerResult) { 1, (c), (n) }对i32和bool返回值Rust 解析器还可以直接使用.into()转换为AppLayerResult简化简单分支的代码。3. 三种返回值的精确定义与调用约定3.1 APP_LAYER_OK / AppLayerResult::ok()返回 “OK” 表示解析器已成功消费本次交付的全部数据。API 将在有更多数据可用时再次调用解析器。这是最常见的返回值大部分协议解析循环在成功处理完一批数据后返回它。例如 rust/src/bittorrent_dht/bittorrent_dht.rs 中解析器按方向循环解析消息任何一条消息解析失败即转为AppLayerResult::err()否则保持ok()。3.2 APP_LAYER_ERROR / AppLayerResult::err()返回 “ERROR” 表示解析器遇到了不可恢复的错误API 将停止处理该协议且整个流的后续数据都不会再交给该解析器对应当前流应用层跟踪的终止。关键约束文档明确强调不要用它来表示可恢复的错误。可恢复错误应通过设置解码事件events来处理而不是返回 ERROR。在 src/app-layer-parser.c 中AppLayerParserParse()对返回值进行分发res.status 0时累加解析器错误计数器并跳转到错误处理路径该流即被标记为不再进行应用层解析。C 侧的真实使用可以参考 FTP 解析器src/app-layer-ftp.c其函数注释明确写着* \retval APP_LAYER_OK when input was process successfully * \retval APP_LAYER_ERROR when a unrecoverable error was encountered例如命令解析彻底失败时直接SCReturnStruct(APP_LAYER_ERROR)见 src/app-layer-ftp.c。3.3 APP_LAYER_INCOMPLETE / AppLayerResult::incomplete()这是最有价值也最容易被误用的返回值。许多协议以记录record为单位传输记录长度常常写在记录头部的最前几个字节。当解析器只拿到一条记录的部分数据时它可以读出记录头中的长度字段返回APP_LAYER_INCOMPLETE(consumed, needed)告诉 API “本次已消费consumed字节还需要needed字节才足够处理下一条记录”API 会推迟下一次调用直到凑够所需数据或流结束。consumed 与 needed 的精确语义consumed本次交付数据中已经被处理掉的字节数从本次输入起点开始计算needed在已消费字节之上还需要多少字节才能继续解析。文档给出的示例图解[ 32 record 1 ][ 32 record 2 ][ 32 r.. ] 0 31 32 63 64 72 ^ ^ consumed: 64 ---------------/ | needed: 32 -------------------/即前两条各 32 字节的记录已完整消费consumed 64第三条记录只有 8 字节64..72还差 24 字节才能凑满 32 字节的记录因此needed 32注意这里的needed指“在已消费 64 字节之上再需要的字节数”。引擎侧的校验与处理在 src/app-layer-parser.c 中AppLayerParserParse()对 INCOMPLETE 返回值做了严格的合法性校验DEBUG_VALIDATE_BUG_ON(res.consumed input_len); DEBUG_VALIDATE_BUG_ON(res.needed input_len - res.consumed); DEBUG_VALIDATE_BUG_ON(res.needed 0); /* incomplete is only supported for TCP */ DEBUG_VALIDATE_BUG_ON(f-proto ! IPPROTO_TCP); /* 对不合法使用返回码的情况将协议置于错误状态 */ if (res.consumed input_len || res.needed res.consumed input_len) { AppLayerIncInternalErrorCounter(tv, f); goto error; }校验规则可归纳为consumed不得超过本次输入长度consumed needed必须不小于本次输入长度即需要的字节数必须覆盖剩余未处理部分needed不能为 0为 0 意味着没有等待的必要应直接返回 OKINCOMPLETE 仅对 TCP 有效UDP 是数据报语义无流式重组直接返回即可。校验通过后引擎会把needed写入 TCP 会话对应方向的data_required字段见 src/app-layer-parser.cif (direction 0) { /* 告诉流引擎还需要多少数据才再次调用解析器 */ ssn-client.data_required res.needed; } else { ssn-server.data_required res.needed; }这样流引擎stream engine就知道在收到足够多的重组数据之前不必再调用解析器——这正是 INCOMPLETE 机制减少无效调用、降低 CPU 开销的原理。再次被调用的两个时机文档明确解析器会在以下两种情况下被再次调用needed指定的数据已凑齐——正常情况解析器可继续解析完整记录流结束EOF时——此时交付的数据必然是不完整的解析器必须自行决定如何处理尾部残片例如丢弃、上报事件或按不完整记录处理。引擎侧AppLayerParserParse()的调用条件也印证了这一点if (input_len 0 || (flags STREAM_EOF))见 src/app-layer-parser.c即 EOF 时即使无数据也会调用一次解析器让其处理流尾状态。4. 支持不完整数据记录头 流式数据体的实战模式4.1 为什么需要流式处理对某些协议完全缓冲queue整条记录再处理可能是巨大的资源浪费。文档指出SMB 和 NFS 在文件传输中可能使用非常大的记录——如果把整个文件体都等齐再交给解析器内存开销不可接受。此时应采用“记录头用 INCOMPLETE 逻辑等待记录体数据流式进入解析器”的模式仅当记录头含长度字段不完整时返回APP_LAYER_INCOMPLETE等待头部凑齐记录头一旦完整立即返回APP_LAYER_OK消费头数据并让后续记录体数据随流持续流入解析器边收边处理。4.2 源码中的真实案例DCERPCRust 实现是这种模式最典型的体现。在 rust/src/dcerpc/dcerpc.rs 中当流中存在不完整数据时解析器先消费能安全消费的部分再要求至少 2 字节以判断是否是新记录的开始_ { consumed cur_i.len() as u32; // 至少需要 2 字节才能判断是否是新记录的开始 if consumed 2 { consumed 0; } else { consumed - 1; } SCLogDebug!(DCERPC record NOT found); return AppLayerResult::incomplete(consumed, 2); }而在 rust/src/dcerpc/dcerpc.rs 中解析器先解析 DCERPC 固定头部再根据头部中的fraglen分片长度字段动态计算需要的数据量return AppLayerResult::incomplete(consumed, DCERPC_HDR_LEN as u32); // ... return AppLayerResult::incomplete(consumed, fraglen.into());这正是“长度字段在头部、按需等待”的标准实现头部长度不足时等头部DCERPC_HDR_LEN头部齐全后按记录实际长度fraglen继续等数据。HTTPC 实现中的协议升级upgrade场景也用到了 INCOMPLETE。在 src/app-layer-htp.c 中当 HTTP/1.1 响应头出现h2cHTTP/2 cleartext upgrade时解析器消费掉 HTTP/1 部分后把剩余字节数作为needed返回让引擎在下一轮把余下数据交给 HTTP/2 解析器// During HTTP2 upgrade, we may consume the HTTP1 part of the data // and we need to parser the remaining part with HTTP2 if (consumed 0 consumed input_len) { SCReturnStruct(APP_LAYER_INCOMPLETE(consumed, input_len - consumed)); } SCReturnStruct(APP_LAYER_OK);WebSocket 升级路径的逻辑完全相同见 src/app-layer-htp.c。这展示了 INCOMPLETE 除了“等待更多数据”之外的另一用途精确控制本次消费边界实现协议间的数据交接。4.3 编写建议综合文档与源码实现 INCOMPLETE 逻辑时应遵循只在 TCP 协议上使用UDP 解析器直接返回 OK 或 ERRORconsumed只统计确实已处理的字节宁可保守如 DCERPC 保留 1 字节用于判断边界needed的取值要使consumed needed覆盖全部剩余输入否则触发引擎的内部错误路径流结束时EOF 调用不要依赖 INCOMPLETE 语义自行处理残片对可恢复的格式异常用AppLayerParserSetDecoderEvent之类的事件机制上报而不是返回 ERROR——ERROR 会永久终止该流后续所有解析。5. 深入回调在引擎中的调用链了解返回值之后掌握回调被调用的上下文有助于写出正确的解析器。AppLayerParserParse()见 src/app-layer-parser.c是引擎侧统一入口其调用链为TCP 流重组完成后由应用层分发逻辑调用src/app-layer.c 等处调用前框架会确保AppLayerParserState与协议状态alstate已分配src/app-layer-parser.c流存在 GAP丢包空洞时若解析器未注册APP_LAYER_PARSER_OPT_ACCEPT_GAPS选项直接进入错误路径并触发原始流重检src/app-layer-parser.c解析结果按status分发 0进入错误状态、 0按 INCOMPLETE 处理、 0视为正常消费src/app-layer-parser.c。C 侧对应选项常量定义在 src/app-layer-parser.hRust 侧在 rust/ffi/src/applayer.rs。这套框架对 C 与 Rust 解析器完全一致Rust 通过 rust/src/applayer.rs 的AppLayerRegisterParser将RustParser结构映射为 C 侧的AppLayerParser注册项。6. 相关文档与进一步阅读应用层整体架构与协议注册流程overview.rst应用层事务Transaction模型transactions.rst应用层帧Frames机制app-layer-frames.rst完整的 Rust 解析器模板rust/src/applayertemplate/template.rs解析器状态与状态机定义src/app-layer-parser.h、src/app-layer-parser.c本文涉及的三个关键源码锚点可对照阅读AppLayerResult 结构体与宏、引擎侧返回值分发与 data_required 写入、Rust 侧 AppLayerResultRust trait。赞分享网络安全【免费下载链接】suricataSuricata is a network Intrusion Detection System, Intrusion Prevention System and Network Security Monitoring engine developed by the OISF and the Suricata community.项目地址https://gitcode.com/gh_mirrors/su/suricata点击查看免费下载相关推荐ArkAnalyzer返回语句函数返回值分析ArkAnalyzer返回语句函数返回值分析 引言 在ArkTS语言开发中函数返回值分析是静态程序分析的关键环节。ArkAnalyzer作为面向ArkTS语静态分析开发工具OpenHarmonySalt 内置 local 返回器Returner完全指南用法、原理与自定义返回器开发Salt 内置 local 返回器Returner完全指南用法、原理与自定义返回器开发 Salt 的返回器Returner机制允许把 minion 执运维配置管理后端Arduino LoRa与LoRaWAN的区别你应该选择哪个Arduino LoRa与LoRaWAN的区别你应该选择哪个 Arduino LoRa是一个用于通过LoRa无线电发送和接收数据的Arduino库它直接暴物联网通信嵌入式上一篇Mi-Create免费开源的小米手表表盘制作工具零基础也能做出自己的表盘下一篇把NCM拖成MP3ncmdump本地免费转换网易云音乐一篇讲透创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

Kats 多变量时序预测:VAR 向量自回归模型(VARModel / VARParams)完整实战指南

Kats 多变量时序预测:VAR 向量自回归模型(VARModel / VARParams)完整实战指南

数据分析机器学习数据科学 【免费下载链接】Kats Kats, a kit to analyze time series data, a lightweight, easy-to-use, generalizable, and extendable framework to perform time series analysis, from understanding the key statistics and characteristics, detecting…

2026/10/11 18:54:30 阅读更多 →
Skills 实战指南:从 SKILL.md 到 Agent 技能包的全链路解析

Skills 实战指南:从 SKILL.md 到 Agent 技能包的全链路解析

最近技术社区里“skills”这个词几乎刷屏了:前端开发 skills、codex skills、claude agent skills、superpowers……聊天时互相问“你装了哪些 skills”,听起来像游戏里在攒技能点,实际上这是大模型 Agent 生态里正在快速标准化的一种新玩法。…

2026/10/12 9:06:22 阅读更多 →
AI编程助手Skills实战:从设计到团队协作的完整指南

AI编程助手Skills实战:从设计到团队协作的完整指南

1. 从“skills”这个热词说起:它到底在解决什么问题最近半年,不管是在技术社区还是开发者群聊里,“skills”这个词出现的频率高得离谱。你如果只看字面意思,可能会以为它说的是“技能”这个泛泛的概念,但放到当下的开发…

2026/10/11 18:54:30 阅读更多 →

最新新闻

构建数学解题Agent:从LLM局限到工具调用与验证优先架构

构建数学解题Agent:从LLM局限到工具调用与验证优先架构

1. 从“让模型直接做数学”到“让模型组织数学”写这篇的时候,我翻了下前面三篇的留言,发现问得最多的不是某一个具体提示词怎么写,而是一类带着困惑的问题:为什么我让大模型算个积分、解个方程,它经常一本正经地给我一…

2026/10/14 4:47:57 阅读更多 →
C++类型转换详解:原理、实践与陷阱

C++类型转换详解:原理、实践与陷阱

1. 类型转换的本质与分类在C编程中,类型转换是每个开发者每天都会遇到的常规操作。但看似简单的类型转换背后,却隐藏着许多值得深入探讨的技术细节。类型转换本质上是在不同类型之间建立桥梁的过程,它决定了数据如何在不同类型间流动和解释。…

2026/10/14 4:47:57 阅读更多 →
B2B电商平台银联电子支付实战接入指南

B2B电商平台银联电子支付实战接入指南

1. 项目概述:为什么一个B2B电商平台必须认真对待银联电子支付接入“B2B电商平台——ChinaPay银联电子支付功能(实战)”,这个标题看起来平实,甚至有点老派,但背后藏着的是中国产业数字化进程中一个极其关键却…

2026/10/14 4:47:57 阅读更多 →
Rabbit health check failed排查:从健康检查原理到连接配置修复

Rabbit health check failed排查:从健康检查原理到连接配置修复

排查过一阵“Rabbit health check failed”报错之后,我发现不少开发者在Spring Cloud项目里见到这个日志的第一反应都是先Check一下RabbitMQ的端口通不通、账号密码对不对,结果发现Rabbit本身好好的,消息也能发能收,但服务注册到注…

2026/10/14 4:47:57 阅读更多 →
亲测2026最新10款免费降ai率工具(附工具优缺点对比图)

亲测2026最新10款免费降ai率工具(附工具优缺点对比图)

看到满页飘红的AIGC检测结果,真的挺让人头疼的。我之前为了降低ai率,各种偏方都试过,现在网上的降ai率工具五花八门,挑得人眼晕。 为了搞清楚到底哪个靠谱,我干脆把市面上热度最高的10款工具全测了一遍。今天把这些免…

2026/10/14 4:47:57 阅读更多 →
Qt Linux打包失败原因与linuxdeployqt避坑指南

Qt Linux打包失败原因与linuxdeployqt避坑指南

简介:本资源是一份面向Linux Qt开发者的技术实践指南,聚焦Ubuntu环境下使用linuxdeployqt工具完成Qt程序跨机部署的核心痛点。针对无Qt运行环境的目标机器,系统性梳理了从Qt开发环境配置、linuxdeployqt源码编译(含Ubuntu 18.04兼…

2026/10/14 4:46:57 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 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/13 22:01:27 阅读更多 →
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/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练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/12 18:11:17 阅读更多 →