libwebsockets Secure Streams + Sequencer 实战:用 `lws-minimal-secure-streams-seq` 构建可编排的 HTTP/HTTPS 客户端

发布时间:2026/10/7 2:10:08

libwebsockets Secure Streams + Sequencer 实战:用 `lws-minimal-secure-streams-seq` 构建可编排的 HTTP/HTTPS 客户端 人工智能AI Agent多模态语音AI 应用【免费下载链接】ten-frameworkOpen-source framework for conversational voice AI agents项目地址https://gitcode.com/TEN-framework/ten-framework点击查看免费下载libwebsocketslws的 Secure Streams安全流是一套把“连接策略”与“业务负载”彻底解耦的客户端通信 API用户代码只认streamtype流类型名和负载至于连哪个端点、走什么协议、用什么 TLS 证书全部由创建lws_context时注入的策略数据库决定。本文以当前仓库中 minimal-secure-streams-seq 这个最小示例为骨架从源码到策略 JSON 再到构建与运行完整讲解“Secure Streams lws_sequencer”的组合用法如何用约 400 行 C 代码实现一个访问https://warmcat.com/index.html的 HTTPS GET 客户端并利用 Sequencer 把“创建流 → 连接 → 重试 → 收包完成”编排成一个可以继续叠加复杂业务的状态机。示例在说什么一次带 Sequencer 编排的 HTTPS GET该示例的程序行为非常直观应用访问https://warmcat.com/并读取那里的index.html。它基于 Secure Streams API主文件 minimal-secure-streams.c 只负责两件事创建 lws context并把整份策略 JSON 注入其中创建一个类型为mintest的 Secure Stream。而流的状态迁移回调与负载收发回调myss_state/myss_rx/myss_tx放在同一个文件中mintest这个流“该如何连接、用什么协议”的全部信息则被独立保存在内嵌的策略数据库中本例直接以 C 字符串常量default_ss_policy形式内联在源码里正式工程中通常单独成文件。与不带 sequencer 的 minimal-secure-streams 相比本示例展示了一个关键差异它用lws_sequencer把整个流程串成了一个可组合的事件序列。源码头部注释给出了四层生命周期模型------ app ------ process 应用代码进程级 ---- sequencer ---- process 序列器进程级跨连接存活 --- secure stream --- process 安全流进程级 ------- wsi ------- connection 底层连接连接级理解这四层是读懂本示例的钥匙wsi是最底层的一次网络连接连接随时会断Secure Stream 是对“同一业务流”的抽象它的状态变化由策略和重试逻辑驱动lws_sequencer则独立于任何连接和协议存活负责编排“一步完成后再走下一步”因此天然适合在它之上继续叠加更复杂的业务流程。构建cmake . make与 CMake 功能开关示例 README 给出的构建方式就是标准的两步$ cmake . make对应的 CMakeLists.txt 值得细看因为它定义了本示例的编译期依赖开关project(lws-minimal-secure-streams-seq C) cmake_minimum_required(VERSION 2.8.12) find_package(libwebsockets CONFIG REQUIRED) ... set(SAMP lws-minimal-secure-streams-seq) set(SRCS minimal-secure-streams.c) set(requirements 1) require_lws_config(LWS_ROLE_H1 1 requirements) # 需要 H1HTTP/1.x角色 require_lws_config(LWS_WITHOUT_CLIENT 0 requirements) # 需要客户端能力 require_lws_config(LWS_WITH_SEQUENCER 1 requirements) # 需要 Sequencer 支持 require_lws_config(LWS_WITH_SECURE_STREAMS 1 requirements) # 需要 Secure Streams require_lws_config(LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY 0 requirements) # 需要运行期动态策略注入 if (requirements) add_executable(${SAMP} ${SRCS}) ... endif()LWS_ROLE_H1本示例策略中protocol: h1即底层走 HTTP/1.xLWS_WITH_SEQUENCERlws_seq_create()等 API 依赖此开关LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY必须为 0因为示例要在运行期把策略 JSON 通过info.pss_policies_json传给 context——若编译成“仅静态策略”就无法这样注入。在 TEN-framework 仓库里libwebsockets 以第三方库形式被构建系统使用third_party/BUILD.gn 中通过ten_enable_libwebsockets特性开关把libwebsockets加入public_depsthird_party/libwebsockets/BUILD.gn 则用cmake_project(websockets)驱动其 CMake 构建并且使用 mbedtls 作为 TLS 后端websockets_copy_mbedtls会把 mbedtls 的安装产物复制给 libwebsockets 使用头文件搜索路径同时包含两者的 install 目录。这解释了为何示例能零配置地完成https://访问——TLS 能力来自同仓库的 mbedtls 依赖。运行方式与命令行参数构建产物名为lws-minimal-secure-streams-seq直接运行$ ./lws-minimal-secure-streams-seq该示例支持两个命令行选项命令行选项含义-d loglevel调试详细度十进制例如-d15-f强制连接错误端点用于验证 backoff 重试流程参数解析逻辑在 minimal-secure-streams.c 中-d通过lws_cmdline_option读取并atoi后交给lws_set_log_level-f和--h1post源码中还支持 POST 测试选项通过lws_cmdline_option的存在性判断设置全局标志。-f的妙处在于它把 streamtype 从mintest换成策略里专门预埋的mintest-fail——该流类型同样指向warmcat.com但端口被写成了22一个不可能建立 HTTPS 的端口。这样每次连接必然失败你就能在日志里直接观察到 Secure Streams 的 backoff 重试行为。源码默认日志级别为LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE见 main() 首行因此正常情况下你会看到USER:、NOTICE:等前缀的日志。一次成功运行的典型输出源自示例 README节选关键行[2018/03/04 14:43:20:8562] USER: LWS minimal http client [2018/03/04 14:43:20:8571] NOTICE: Creating Vhost default port -1, 1 protocols, IPv6 on [2018/03/04 14:43:20:8616] NOTICE: created client ssl context for default [2018/03/04 14:43:20:8617] NOTICE: lws_client_connect_2: 0x1814dc0: address warmcat.com [2018/03/04 14:43:22:0154] NOTICE: lws_client_interpret_server_handshake: client connection up [2018/03/04 14:43:22:0169] USER: RECEIVE_CLIENT_HTTP_READ: read 1024 ...约 27 次分片读取每次 1024/1015/974 字节不等合计约 26.5KB... [2018/03/04 14:43:22:3022] NOTICE: lws_http_client_read: transaction completed says -1 [2018/03/04 14:43:23:3042] USER: Completed日志清晰地展示了 HTTPS 连接建立created client ssl context→client connection up→ 分片接收页面每次read1024 字节左右直至服务端incoming content length 26520全部读完→ 事务完成Completed的完整链路。-d15可看到更细的调试输出便于跟踪内部状态。策略数据库把“怎么连”从代码里剥离Secure Streams 的核心思想见 lws-secure-streams.h 的头部注释Secure Streams 是一个只关心负载payload-only的客户端通信通道连接的所有细节都存放在系统级策略数据库中并按 streamtype 索引。用户代码不知道、也不管理端点选择、TLS CA 乃至线缆协议——好处是代码对这些零依赖日后只要改策略数据库就能整体换连接方式而不用动业务代码。本示例的策略 JSONdefault_ss_policy完整定义见 minimal-secure-streams.c分四大部分值得逐段拆解。1. 元信息与retry命名的 backoff 重试策略release: 01234567, product: myproduct, schema-version: 1, retry: [{ default: { backoff: [1000, 2000, 3000, 5000, 10000], conceal: 5, jitterpc: 20, svalidping: 300, svalidhup: 310 } }]字段含义本示例取值backoff重试间隔毫秒数组第 n 次重试等待backoff[n]毫秒超出数组长度后沿用最后一项1s → 2s → 3s → 5s → 10s之后一直 10sconceal前 N 次重试对外“隐藏”不向上层报错5 次jitterpc在基础间隔上叠加的随机抖动百分比避免“惊群”式同步重试20%svalidping连接空闲多少秒后发出 PING 保活300ssvalidhup连接空闲多少秒后判定无效并挂断310s这些字段与底层 lws_retry_bo_t 一一对应retry_ms_table/retry_ms_table_count对应backoff数组conceal_count对应concealjitter_percent对应jitterpcsecs_since_valid_ping/secs_since_valid_hangup对应svalidping/svalidhup。底层lws_retry_get_delay_ms()负责按表推进重试每次调用递增 try 计数并返回下次重试应等待的毫秒数同时通过conceal标志告诉调用方这次失败是否应该被掩盖见 lws-retry.h。这也正是-f触发失败时能看到“静默重试 5 次后才报 ALL_RETRIES_FAILED”的原因。2.certs命名的 BASE64 DER 证书与trust_stores命名的信任链certs: [{ isrg_root_x1: BASE64 编码的 ISRG Root X1 DER 证书 }], trust_stores: [{ name: le_via_isrg, stack: [isrg_root_x1] }]certs段定义命名单个证书BASE64 编码的 DER 格式且必须按“从根证书开始”的顺序给出注释特别说明像 Lets Encrypt 这种存在多条校验路径的 CA只需给出其中一条完整路径所需的全部证书不含服务端证书本身。本示例选择了 ISRG 路径ISRG Root X1从而可以跳过额外部署 IdenTrust 交叉签名的根证书。trust_stores段把证书组装成命名的信任链stack里的元素引用certs中定义的证书名。策略数据结构在头文件中的对应物是 lws_ss_x509_tca_der/ca_der_len持有 DER 证书与 lws_ss_trust_store_tssx509数组 count表示链上证书数。3.s支持的流类型集合mintest正常 GET、mintest-fail故意连错误端口供-f测试与minpostPOST 测试三种流类型共享同一套结构s: [{ mintest: { endpoint: warmcat.com, port: 443, protocol: h1, http_method: GET, http_url: index.html, plugins: [], tls: true, opportunistic: true, retry: default, tls_trust_store: le_via_isrg } }, { mintest-fail: { endpoint: warmcat.com, port: 22, /* 故意写错端口连接必然失败 */ ...同上... } }, { minpost: { endpoint: warmcat.com, port: 443, protocol: h1, http_method: POST, http_url: testserver/formtest, ...同上... } }]各字段含义字段含义本示例取值endpoint要连接的 DNS 地址warmcat.comport目标端口443mintest-fail故意用 22protocol线缆协议h1HTTP/1.xlws 还支持h2、ws、mqtt、raw等见 lws-secure-streams-policy.hhttp_methodHTTP 方法GET/POSThttp_url请求的 URL 路径index.html/testserver/formtestplugins认证插件列表空数组不启用认证插件tls是否必须经 TLS 隧道连接对应策略标志 LWSSSPOLF_TLStrueopportunistic机会型连接只有在客户端发起写请求时才建立连接对应 LWSSSPOLF_OPPORTUNISTIC与之相对的是nailed_up常驻连接trueretry引用上面retry段里命名的重试策略defaulttls_trust_store引用上面trust_stores段里命名的信任链le_via_isrg策略中http_method/http_url等字段在头文件里对应 lws_ss_policy_t 的method/url成员用户代码只凭streamtype名称如mintest创建流lws 在 context 内部完成“名称 → 策略”的查表匹配。应用层三件套rx / tx / state 回调示例在myss_t应用自定义流对象内含struct lws_ss_handle *ss和void *opaque_data上注册了三个回调它们在 minimal-secure-streams.c 中myss_rx收到负载时被调用。每次打印len与flags并用lwsl_hexdump_info做十六进制转储当flags LWSSS_FLAG_EOM消息结束标志时说明整条消息收完于是置bad 0; interrupted 1通知事件循环结束、程序判定成功退出。LWSSS_FLAG_SOM/LWSSS_FLAG_EOM等标志定义于 lws-secure-streams.h。myss_tx发送负载时被调用。本例是纯 GET 客户端、不发送任何正文所以函数体仅注释说明“本示例不发送任何负载”并直接返回 0LWSSSSRET_OK。tx回调的返回约定见 lws_ss_info_t0 表示按len/flags发送1LWSSSSRET_TX_DONT_SEND表示什么都不发。myss_state连接状态变化时被调用通过lws_ss_state_name(state)打印可读状态名。关键分支LWSSSCS_CREATING流刚创建调用lws_ss_request_tx(ss)触发一次 tx 流程GET 请求因此发出LWSSSCS_ALL_RETRIES_FAILED策略内所有重试均已耗尽置interrupted 1结束程序并让最终结果标记为失败其余状态DISCONNECTED、UNREACHABLE、AUTH_FAILED、CONNECTED、CONNECTING、DESTROYING等默认忽略。lws_ss_constate_t的完整状态集合包括QOS_ACK_REMOTE/QOS_NACK_REMOTE/QOS_ACK_LOCAL/QOS_NACK_LOCAL这类 QoS 确认、SINK_JOIN/SINK_PART这类 sink 事件定义于 lws-secure-streams.h。Sequencer把流程编排成可扩展的状态机这是本示例区别于基础版 minimal-secure-streams 的核心增量。lws_sequencer的设计目标见 lws-sequencer.h帮助实现超出一个连接生命周期、不绑定特定协议、不绑定特定 vhost、且必须在事件循环内收发事件的序列。创建与事件模型main() 通过lws_seq_create()创建序列器memset(i, 0, sizeof(i)); i.context context; i.user_size sizeof(myseq_t); /* 为序列器用户态分配内存 */ i.puser (void **)ms; i.cb min_sec_str_sequencer_cb; /* 序列回调 */ i.name min-sec-stream-seq; if (!lws_seq_create(i)) { ... }事件通过lws_seq_event_cb回调分发签名见 lws-sequencer.h事件枚举见 lws_seq_events_t事件含义LWSSEQ_CREATED/LWSSEQ_DESTROYED序列器被创建 / 销毁LWSSEQ_TIMED_OUT/LWSSEQ_HEARTBEAT当前步骤超时 / 1Hz 心跳LWSSEQ_WSI_CONNECTED/CONN_FAIL/CONN_CLOSE绑定的 wsi 连接结果LWSSEQ_SS_STATE_BASE 状态值归属该序列器的 Secure Stream 状态变化自动转发到序列器事件号为LWSSEQ_SS_STATE_BASE加lws_ss_constate_t枚举值LWSSEQ_USER_BASE 100起用户自定义事件回调返回值只有两种LWSSEQ_RET_CONTINUE继续与 LWSSEQ_RET_DESTROY销毁序列。事件入队用lws_seq_queue_event()超时控制用lws_seq_timeout_us()触发时仅入队LWSSEQ_TIMED_OUT消息、不强制关连接见 lws-sequencer.h。本示例的序列逻辑序列器状态机myseq_state_t定义了四个步骤SEQ_IDLE→SEQ_TRY_CONNECT→SEQ_RECONNECT_WAIT→SEQ_CONNECTEDminimal-secure-streams.c。回调min_sec_str_sequencer_cb的核心流程LWSSEQ_CREATED序列器刚启动进入SEQ_IDLE填充lws_ss_info_t ssihandle_offset用offsetof(myss_t, ss)定位流句柄槽位、opaque_user_data_offset定位不透明数据指针、注册三个回调、user_alloc sizeof(myss_t)然后按命令行标志挑选 streamtype——-f选mintest-fail、--h1post选minpost、默认选mintest——最后调用lws_ss_create(lws_seq_get_context(seq), 0, ssi, NULL, s-ss, seq, NULL)创建流。注意最后一个非 NULL 参数seq把流绑定到序列器上之后流的所有状态变化都会自动以LWSSEQ_SS_STATE_BASE 状态的形式投递到该序列器。流状态转发事件LWSSEQ_SS_STATE_BASE LWSSSCS_CREATING时同样调用lws_ss_request_tx(s-ss)触发 GET LWSSSCS_CONNECTED/CONNECTING/DISCONNECTED/UNREACHABLE/AUTH_FAILED/DESTROYING/POLL/QOS_ACK_*等逐一打印 LWSSSCS_ALL_RETRIES_FAILED时置interrupted 1。LWSSEQ_TIMED_OUT若当前处于SEQ_RECONNECT_WAIT则调用lws_ss_request_tx触发重试发送。LWSSEQ_USER_BASE这是示例预留的自定义事件入口——由流回调“通知序列器负载已正确收到”时使用源码注释说明了这一用途收到后同样置成功标志结束程序。lws_ss_create()的完整语义见 lws-secure-streams.h成功时流创建、state回调收到LWSSSCS_CREATING、*ppss指向句柄用户数据结构由 lws 整体分配user_alloc指定大小并根据handle_offset/opaque_user_data_offset自动预置句柄与不透明指针。这解释了为何myss_state里可以直接用m-ss。事件循环与收尾while (n 0 !interrupted) n lws_service(context, 0); ... lws_context_destroy(context); lwsl_user(Completed: %s\n, bad ? failed : OK); return bad;lws_service(context, 0)是 lws 经典的事件循环SIGINT通过 sigint_handler 置interrupted实现优雅退出。bad是程序退出码成功收到完整消息rx 中出现LWSSS_FLAG_EOM或序列器收到LWSSEQ_USER_BASE时置 0否则保持 1最终以Completed: OK/Completed: failed呈现对应日志末尾的Completed。从示例到生产关键经验策略与代码分离是核心收益改端点、换 CA、把 h1 升级成 h2 或 mqtt都只需要改策略数据库业务回调一行不动——这正是 Secure Streams 相对传统lws_client_connect手写连接代码的最大价值可对照 lws-secure-streams.h 的论述。用 Sequencer 承载“跨连接”的业务步骤连接可能随时断开、重试由策略自动推进但“整个任务做到哪一步了”这种状态属于业务层。把状态机放进lws_sequencer流的状态变化自动投递进来再结合LWSSEQ_USER_BASE自定义事件就能自然地扩展出“先鉴权再取数据”“多流串联”“失败后降级”等复杂流程。-f是免费的重试验证工具生产前用mintest-fail这类错误策略组合改端口即可构造跑一遍-f即可在日志中确认conceal掩盖期、backoff 间隔与ALL_RETRIES_FAILED终态是否符合预期。留意编译开关矩阵LWS_WITH_SECURE_STREAMS、LWS_WITH_SEQUENCER、LWS_ROLE_H1缺一不可且动态策略注入要求关闭LWS_WITH_SECURE_STREAMS_STATIC_POLICY_ONLY在 TEN-framework 这类用 GN 驱动 CMake 的集成环境里这些开关由 third_party/libwebsockets/BUILD.gn 统一管理TLS 由同仓库的 mbedtls 提供。延伸阅读同目录下的其它 Secure Streams 示例secure-streams 汇总了 client-tx、post、proxy、staticpolicy、sigv4、metadata、blob 等二十余个进阶示例可作为继续学习的索引无 Sequencer 的对照版minimal-secure-streams/README.mdAPI 参考lws-secure-streams.h、lws-sequencer.h、lws-secure-streams-policy.h、lws-retry.h。赞分享人工智能AI Agent多模态语音AI 应用【免费下载链接】ten-frameworkOpen-source framework for conversational voice AI agents项目地址https://gitcode.com/TEN-framework/ten-framework点击查看免费下载相关推荐libwebsockets Secure Streams 代理客户端批量发送实战minimal-secure-streams-client-tx 逐行解析libwebsockets Secure Streams 代理客户端批量发送实战minimal secure streams client tx 逐行解析 本人工智能AI Agent多模态语音AI 应用libwebsockets Secure Streams 裸 TCP 服务器实战minimal-secure-streams-server-raw 完全解析libwebsockets Secure Streams 裸 TCP 服务器实战minimal secure streams server raw 完全解析人工智能AI Agent多模态语音AI 应用libwebsockets Secure Streams 客户端实战api-test-secure-streams 测试程序原理与源码解析libwebsockets Secure Streams 客户端实战api test secure streams 测试程序原理与源码解析 本文以 TEN f人工智能AI Agent多模态语音AI 应用上一篇FGO-py终极指南告别肝痛让《命运/冠位指定》全自动刷本的智能助手下一篇React DnD拖拽层终极指南如何创建自定义拖拽预览组件创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
延伸阅读

更多相关文章

2026/10/7 2:10:08

终端编码代理pi:自主执行代码任务的AI Agent实战解析

pi这个词,最近在开发者圈子里有点热。无论是GitHub Trending还是技术流时间线,都能看到有人聊pi、pi agent、pi coding agent这类话题。简单说,pi就是一个跑在终端里的AI编码代理,你给它一句话或一个任务,它就自己完成…

2026/10/7 3:05:10

TRex服务能力解析:从单机流量工具到可集成的测试服务

你在做自动化测试平台或者大规模网络验收的时候,很快就会意识到一个问题:再好的流量发生器,如果只能坐在机房里敲命令行,那它就是一个高级玩具。TRex之所以能在高性能流量工具里站稳脚跟,不只是因为它基于DPDK能打出线…

2026/10/7 3:05:10

vi与Docker实战指南:从容器基础命令到高效运维

做 Linux 运维和开发这些年,我见过太多人被两个东西劝退:一个是 vi,进去之后不知道怎么退出;另一个是 Docker,装完不知道容器和镜像到底啥关系。vi 是 Linux 环境里最底层的编辑工具,Docker 是现在部署应用…

2026/10/7 3:05:10

福建DEM原始高程TIF数据:从选型、拼接到三维建模全攻略

简介:福建省数字高程模型(DEM)原始高程数据以TIFF格式存储,面向ArcGIS等地理信息软件的使用者,适合开展地形分析、水文模拟、灾害评估与城乡空间规划。压缩包整体约三百六十二兆字节,共三十个文件&#xff…

2026/10/7 3:05:10

C语言递归实战:从栈帧原理到高频题型拆解

带过C语言的人都体验过那种状态:盯着屏幕上十几行递归代码,明明每一行都认识,可函数一调用自己,脑内就立刻乱成一锅粥。在很多技术社群里,有个高频提问来回出现——“递归到底怎么想到这么写的?”我当年也在…

2026/10/7 3:05:10

Unity新输入系统实战指南:Action抽象与跨平台配置

说实话,Unity 从 2019 年开始把新输入系统(Input System)包塞进 Package Manager 的时候,我是持观望态度的。那会儿项目组里人多嘴杂,老的Input.GetAxis用得顺手,改接口、改配置、改设备兼容逻辑&#xff0…

2026/10/7 3:00:10

2026降重会不会影响质量?7款工具六维度实测打分

论文降重到底会不会把内容改坏?这个焦虑几乎每个毕业生都经历过。重复率是降下来了,可导师一句"这段读着不像人写的"又让人心里打鼓。为回答这个问题,笔者耗时三周,围绕生成质量、语义保留、上下文连贯、降重效率、功能…

2026/10/5 6:32:56

Jev+Agent接管浏览器:browser-use实战与jev-ultrafast性能优化

1. 从“Jev”说起:为什么我要把Agent接进浏览器“Jev”这个词最近在圈子里出现的频率越来越高,很多人第一次听到会以为是某个新模型的名字,其实它更像是一种思路——把Jev模型的能力当作底座,通过Agent的方式去接管浏览器&#xf…

2026/10/6 4:01:51

多智能体集群实战:DeepAgents编排、MCP与A2A协议及Skills体系

1. 从"单兵作战"到"集群协同":多智能体编排到底在解决什么问题如果你最近在折腾 Agent 相关的东西,大概率会有一种感觉:单个 Agent 能做的事情,其实很快就摸到天花板了。你给它一个提示词,挂几个工…

2026/10/6 17:46:51

无源低通滤波器设计实战:从RC到LC,手把手教你避开那些坑

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

2026/10/7 1:05:03

ESP32免重刷固件:浏览器直接修改NVS键值实现WiFi配置更新

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

2026/10/7 1:05:03

SAP HANA查询结果导出CSV:避开乱码、性能与权限的实用指南

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

2026/10/7 1:05:03

数字后端Placement阶段Density与Congestion控制实战

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

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑