人工智能AI Agent多模态语音AI 应用【免费下载链接】ten-frameworkOpen-source framework for conversational voice AI agents项目地址https://gitcode.com/TEN-framework/ten-framework点击查看免费下载SMDSystem Message Distribution是 libwebsocketslws提供的一套跨平台、线程安全的系统内消息发布/订阅机制同一进程内任意已注册的参与者可以向其他参与者广播短消息进程之间还可借助 Secure Streams 代理互通。本文以 TEN-framework 仓库内嵌的 libwebsockets 源码SMD 说明文档 与 实现 smd.c为对象梳理消息格式、类过滤、投递保证、跨进程桥接等核心机制并给出可直接参考的 API 用法与三类预定义消息 Schema。SMD 是什么系统内部轻量消息总线在嵌入式与物联网场景中系统内的多个独立模块网络栈、按键处理、状态机、业务应用等经常需要快速感知彼此的事件与状态变化。libwebsockets 的 SMD 为这种系统内部、短消息、低占空比的通信提供了统一的 API跨平台一致各操作系统与框架通常有各自碎片化的消息传递 API而lws_smd的接口在 Windows、RTOS 等任何平台上行为一致跨平台代码只需编写一次。消息短小单条消息负载不超过 384 字节LWS_SMD_MAX_PAYLOAD定义于 lws-smd.h低于管道或 UDS 数据报的原子性系统限制也适合小内存系统的堆使用但同时足以承载有实际意义的 JSON。触发来源内外皆可消息本身只在系统内部传递但其触发事件可能来自外部例如按键keypress或网络状态变化networking state changes。从实现上看SMD 是挂在struct lws_context上的一个子系统ctx-smd参与者peer注册后形成一条双向链表owner_peers待分发消息挂在另一条链表owner_messages上消息在堆上分配由 smd.c 统一调度。三种部署形态单进程广播到跨进程代理SMD 支持从单进程到多进程的渐进式扩展文档中给出了三种形态单进程形态所有参与者注册在同一个lws_context上消息由 lws 服务线程上下文以回调方式投递给参与者。参与者注册与发送消息的 API 都是线程安全的因此非服务线程也可安全发布消息。跨进程形态不同进程、不同lws_context的参与者之间借助现有 Secure Streams 代理SS Proxy互连——用户可复用_lws_smd内建 streamtype 建立 Secure Stream即可接收远端进程的 SMD 消息也可反向把本进程消息发给远端。事件循环集成消息在任何注册参与者发送后最早也要等到事件循环的下一次轮转才会被分发从而在单次事件循环 trip 内批量处理多个排队事件保证消息处理非递归、栈占用温和。消息类Message Class与过滤消息类lws_smd_class_t即uint32_t位图用于标识消息的通用类型例如网络状态或 UI 按键事件。参与者注册时通过_class_filter位掩码声明自己关心哪些类过滤器位为 0 的类永远不会投递给该参与者一条消息通常只标记单个类但也允许同时置多个类位、按任一匹配投递——此时必须保证负载能被所有可能类的读者解析例如统一使用 JSON。仓库预定义了几个 well-known 高层类见 lws-smd.h常量位值含义LWSSMDCL_INTERACTION1 0用户与设备交互按键、触摸屏、拿起设备等LWSSMDCL_SYSTEM_STATE1 1lws_system 状态机变化如进入 OPERATIONALLWSSMDCL_NETWORK1 2网络事件link-up、DHCP、captive portal 状态更新LWSSMDCL_METRICS1 3SS 客户端进程向代理上报指标特殊代理不转播此类LWSSMDCL_USER_BASE_BITNUM24用户自定义类起始位用户可用LWSSMDCL_USER_BASE_BITNUM定义最多 8 个私有类类位取(1 LWSSMDCL_USER_BASE_BITNUM)至(1 (LWSSMDCL_USER_BASE_BITNUM 7))。分配期拒绝机制lws_smd维护所有参与者类掩码的全局并集smd-_class_filter见_lws_smd_class_mask_union()。若某个类当前没有任何参与者监听则分配该类的消息会被在分配期而非分发期直接拒绝lws_smd_msg_alloc()返回 NULL从而不浪费堆与 CPU一旦出现感兴趣的参与者该类消息随即恢复产生。因此调用方在lws_smd_msg_alloc()返回 NULL 时应无错误地跳过本次事件生成动作对应源码 smd.c#L38-L49 的rejecting class ... no participant wants分支。消息投递保证与线程语义文档与实现共同明确了以下投递语义smd.c投递范围已发送消息会投递给所有类掩码匹配的已注册参与者包括发送者自己发送 API 线程安全。回调上下文本地投递的回调发生在 lws 事件循环线程 0默认LWS_MAX_SMP 1时的唯一线程跨进程参与者则在各自 UDS 网络线程的上下文收到回调。负载生命周期从回调返回那一刻起消息负载就可能被立即销毁不可保存引用或期待之后仍可用。时间戳与顺序消息携带全系统单调时钟monotonic时间戳同事件循环上的参与者按序收到跨线程时顺序取决于平台锁竞争经 UDS 连接的跨进程参与者可能乱序——在意顺序的接收方必须依据消息创建时间戳自行排序。队列深度与 TTL从 context.c#L669-L683 可见SMD 的队列行为可通过lws_context_creation_info配置smd_ttl_us排队消息存活时间默认非 FreeRTOS 平台 2,000,000 µs2sFreeRTOS 平台 5,000,000 µs5s超时消息会被强制移除并推进相关 peer 的 taillws_smd_message_pending()中的超时清理逻辑。smd_queue_depth最大排队消息数默认非 FreeRTOS 平台 40、FreeRTOS 平台 20超过深度的新消息在_lws_smd_msg_send()中被拒绝。消息引用计数Refcounting为免为每条消息维护一份参与者数量长度的列表SMD 在消息到达时按当时表示愿意接收该类的活跃参与者数计算一个 refcount_lws_smd_msg_assess_peers_interested()。每个参与者把消息作为自己的tail逐条推进每成功投递一次就递减 refcount归零即销毁_lws_smd_msg_destroy()。由于对端可能异步 detach / 关闭链路分发器distributor侧的逻辑 peer 对象会推迟自我销毁直到不可能再收到其活跃期内时间戳的消息为止默认 2 秒宽限期grace period用于保证离开的 peer 在销毁前正确结算消息引用计数。消息创建alloc/send 与 printf 一站式根据负载类型lws_smd提供两套消息创建路径已知长度lws_smd_msg_alloc(ctx, _class, len)返回不透明消息对象关联的负载缓冲调用方直接就地准备内容免拷贝随后用lws_smd_msg_send(ctx, payload)入队若生成内容过程中出错可用lws_smd_msg_free(payload)放弃否则 lws 会自行接管销毁。格式化文本推荐 JSONlws_smd_msg_printf(ctx, _class, format, ...)一步完成尺寸探测 → 分配 → 格式化 → 入队内部先vsnprintf(NULL, 0, ...)计算长度再调用lws_smd_msg_alloc与lws_smd_msg_send同样线程安全若当前无任何参与者关注该类直接返回 0、不做任何分配。int lws_smd_msg_printf(struct lws_context *ctx, lws_smd_class_t _class, const char *format, ...);由于 SMD 消息短小、低占空比而内容开放官方明确推荐使用 JSON可维护、可扩展、可调试、自描述且避免跨团队共享头文件版本这类脆弱依赖。Secure Streams_lws_smdstreamtype 跨进程互通当以LWS_WITH_SECURE_STREAMS构建时lws_smd暴露内建 streamtype_lws_smdLWS_SMD_STREAMTYPENAME用户 Secure Stream 可借助 SS payload 语义与 SMD 互通创建 Secure Stream 时用户提供的 SS info 结构成员manual_initial_tx_credit被复用为该 SMD 连接的RX 类掩码。RX 与 TX 负载前均有16 字节二进制头LWS_SMD_SS_RX_HEADER_LENMSB-first 的 64 位类位域当前仅用低 32 位 MSB-first 的 64 位微秒级时间戳。TX 时只需设置前 64 位类位域时间戳由 lws 自动补齐对应 smd.c#L331-L359 中lws_ser_wu64be写类、时间戳位留 0 的实现。在 SS 的tx()回调中可用助手lws_smd_ss_msg_printf(tag, buf, len, _class, format, ...)一步完成格式化与写头int lws_smd_ss_msg_printf(const char *tag, uint8_t *buf, size_t *len, lws_smd_class_t _class, const char *format, ...);对应地在_lws_smdstreamtype 的rx()回调中把收到的载荷直接透传给lws_smd_ss_rx_forward(ss_user, buf, len)代理侧变体lws_smd_sspc_rx_forward()即可把序列化的 SMD 消息反序列化并转发给本 context 内所有关注该类的本地参与者转发时保留原始源时间戳而非转发时刻见 smd.c#L404-L409。Well-known 消息 Schema三类典型业务事件SMD 针对常见嵌入式业务预定义了三种高层类与对应 JSON Schema参与者可按类订阅并解析。用户交互按键事件LWSSMDCL_INTERACTION由lws_button在用户与已定义按键交互时产生点击类事件与按下/抬起类事件会同时发布参与者按交互语义选择关注哪一类。两类事件发布前都经过精细过滤细节见 按键驱动说明。Schema{ type: button, src: controller-name/button-name, event: event-name }示例{type:button,src:bc/user,event:doubleclick}事件名含义down按键通过按下过滤适合基于时长的响应up按键已抬起适合基于时长的响应click按键活动被归类为单击longclick按键活动被归类为长按doubleclick按键活动被归类为双击路由表变化LWSSMDCL_NETWORK若能订阅 OS 路由表变化例如 Linux 上通过 rtnetlink 支持lws 会用 SMD 通告变化若启用了 Captive Portal DetectCPD且能看到路由表变化则自动触发新一轮 CPD结果同样经 SMD 广播。Schema{ rt: add|del, add if being added }实现细节context/pt 创建时在 Linux 下 lws 会尝试获取路由表需要 root 权限该动作发生在协议初始化后、权限被 drop 之前lws 在每个 pt 维护路由表缓存变化时重估既有连接的对端是否仍可达不可达则关闭连接若网关路由变化还会额外发布{trigger:cpdcheck,src:gw-change}。Captive Portal 检测LWSSMDCL_NETWORK主动探测网络能否直连互联网或是否被强制门户拦截。检测步骤通过 Secure Streams Policy 中 streamtypecaptive_portal_detect编程示例配置captive_portal_detect: { endpoint: connectivitycheck.android.com, http_url: generate_204, port: 80, protocol: h1, http_method: GET, opportunistic: true, http_expect: 204, http_fail_redirect: true }检测结果Schema 为{type: cpd, result:result}结果取值OK可直达互联网、Captive处于强制门户后、No internet无连接。请求重测Schema 为{trigger: cpdcheck}任何参与者可借此主动请求重新检测。lws_system 状态推进LWSSMDCL_SYSTEM_STATElws_system 状态变化被转成 SMD 消息让不在事件循环上的参与者也能感知系统进度。注意区分两套机制在主事件循环上注册 lws_system notifier 回调可以同步否决状态变更并 hook 拟议状态而 SMD 事件是异步的、只在状态变更被决定之后通知但可覆盖整个系统。在 lws 负责引导启动的前提下系统必须获得日期date并取得非强制门户连接的 IP 之后才能建立经过验证的 TLS 连接因此用户代码通常依赖系统到达OPERATIONAL状态再行动。Schema{state:state}状态枚举及含义状态含义CONTEXT_CREATED正在创建 lws_contextINITIALIZED初始 vhost 与协议已初始化IFACE_COLDPLUG已发现网络接口DHCP已获得 DHCPCPD_PRE_TIME尚未拥有系统时间时的 captive portal 检测钩子TIME_VALIDNtpclient 已运行CPD_POST_TIME拥有系统时间后基于 TLS的 captive portal 检测钩子POLICY_VALID系统策略已获取并解析REGISTERED设备已向权威机构注册AUTH1已凭注册信息从权威机构取得 auth1AUTH2已凭注册信息从权威机构取得 auth2OPERATIONAL系统已激活可建立经过认证的 TLS 连接POLICY_INVALID策略正在变更参与者注册与回调签名要成为 SMD 参与者调用lws_smd_register()并给出类过滤掩码与回调注册同样线程安全struct lws_smd_peer * lws_smd_register(struct lws_context *ctx, void *opaque, int flags, lws_smd_class_t _class_filter, lws_smd_notification_cb_t cb); typedef int (*lws_smd_notification_cb_t)(void *opaque, lws_smd_class_t _class, lws_usec_t timestamp, void *buf, size_t len);回调收到注册时传入的opaque指针、消息类、单调时间戳、负载缓冲与长度。注册时可用LWSSMDREG_FLAG_PROXIED_SS标记实际是代理 SS 连接在注册此时 opaque 为 ss handle。注销使用lws_smd_unregister(pr)若注册打算与lws_context同生命周期则无需显式注销lws_context_destroy会统一清理对应 smd.c#L767-L804 的_lws_smd_destroy()。注册新参与者时实现会同步更新全局类掩码并为队列中所有该参与者感兴趣的既有消息递增 refcount保证新订阅者不会错过已在途的消息见 smd.c#L634-L653反之注销时会为其本应收到的所有排队消息递减 refcount归零即销毁。小结与源码阅读建议SMD 为 TEN-framework 内嵌 libwebsockets 的嵌入式系统提供了单进程广播 跨进程代理桥接 事件循环集成的统一消息总线其设计要点可归纳为384 字节短负载、类位图过滤、分配期拒绝无听众的类、引用计数管理生命周期、单调时间戳保证顺序可判、以及JSON 负载的自描述 Schema。建议按以下路径深入源码接口定义与常量include/libwebsockets/lws-smd.h核心实现分配/入队/分发/注册/超时/销毁lib/system/smd/smd.c队列深度与 TTL 的默认值及配置smd_queue_depth/smd_ttl_us见 lib/core/context.c#L669-L683按键事件过滤的细节说明lib/drivers/button/README.md架构图消息格式、单进程分发、SS 代理桥接doc-assets/smd-message.png、doc-assets/smd-single-process.png、doc-assets/smd-proxy.png赞分享人工智能AI Agent多模态语音AI 应用【免费下载链接】ten-frameworkOpen-source framework for conversational voice AI agents项目地址https://gitcode.com/TEN-framework/ten-framework点击查看免费下载相关推荐TEN-framework 集成下的 libwebsockets 发布策略stable 分支、backport 与 soname 机制详解TEN framework 集成下的 libwebsockets 发布策略stable 分支、backport 与 soname 机制详解 libwebsoc人工智能AI Agent多模态语音AI 应用TEN-framework 内置 libwebsockets meta-drivers用统一驱动抽象解锁跨平台嵌入式开发TEN framework 内置 libwebsockets meta drivers用统一驱动抽象解锁跨平台嵌入式开发 导读 本文围绕 TEN framew人工智能AI Agent多模态语音AI 应用libwebsockets 客户端 HTTP Cookie 存储、缓存与自动应用机制详解TEN-framework 内置组件libwebsockets 客户端 HTTP Cookie 存储、缓存与自动应用机制详解TEN framework 内置组件 本指南围绕 TEN frame人工智能AI Agent多模态语音AI 应用创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考