价格

架构总览

相关源文件

本页面内容基于以下源文件生成:

SRT(Secure Reliable Transport)是一个开源传输协议,专为超低延迟(亚秒级)音视频流及通用批量数据传输设计。该协议在贡献与分发端点之间提供安全、可靠且自适应的传输能力,能够在抖动和带宽波动的网络环境中维持恒定的端到端延迟。SRT 的三大核心特性——安全加密、丢包恢复和网络自适应——使其成为实时流媒体工作流中的关键基础设施(README.md:24-36)。

SRT 协议实现了 AES 加密以保护媒体流载荷,并提供多种错误恢复机制来最小化典型互联网连接中的丢包。其中,自动重传请求(ARQ)是主要的错误恢复方法:当接收方检测到丢包时,会向发送方发送警报请求重传。此外,协议还支持前向纠错(FEC)和连接绑定等高级特性(README.md:36-38)。

系统架构总览

SRT 的整体架构采用分层设计,从上到下依次为:API 控制层、核心协议层、队列与多路复用层、通道抽象层以及加密子系统。每一层都有明确的职责边界,通过定义良好的接口进行交互。

正在加载图表渲染器...

架构图要点说明:

  1. CUDTSocket 与 CUDT 的分层关系CUDTSocket 是位于 CUDT 之上的控制层,负责 Socket 生命周期管理,而 CUDT 是核心协议引擎,处理所有传输逻辑。CUDTSocket 拥有 CUDT 实例(srtcore/api.h:77-81)。

  2. CUDT 的组件依赖CUDT 核心类依赖发送/接收缓冲区(buffer_sndbuffer_rcv)、拥塞窗口(window)、数据包(packet)、队列(queue)、握手模块(handshake)以及拥塞控制(congctl)等关键组件(srtcore/core.h:54-78)。

  3. 多路复用器的资源整合CMultiplexer 将发送队列、接收队列、通道和定时器整合在一起,多个 UDT 实例可以共享同一个多路复用器,通过引用计数管理生命周期(srtcore/queue.h:395-618)。

  4. 通道层的 UDP 封装CChannel 是对底层 UDP socket 的薄封装,提供 openclosesendto 等操作,将 SRT 数据包映射到 UDP 传输(srtcore/channel.h:64-92)。

  5. 加密子系统的独立性:HaiCrypt 作为独立的加密子系统,通过 HaiCrypt_Handle 句柄与核心层交互,支持 AES-128/192/256 以及 GCM 模式(haicrypt/haicrypt.h:30-93)。

核心类层次结构

CUDTSocket 控制层

CUDTSocket 是 SRT API 层的核心类,充当 CUDT 核心功能层的控制包装器。其职责边界明确:负责 Socket 的创建、绑定、连接、监听等生命周期管理操作,但不直接处理数据传输逻辑。

职责边界:

  • 管理 Socket 状态机(打开、绑定、连接、监听、关闭)
  • 维护 Socket 配置参数
  • 协调 CUDT 核心实例与底层多路复用器的关联
  • 不直接处理数据包的发送与接收

入口与关键 API: CUDTSocket 通过 CUDTUnited 全局管理器进行实例化和查找。CUDTUnited 维护所有活跃 Socket 的注册表,负责 ID 分配和并发访问控制(srtcore/api.h:71-242)。

关键数据结构:

  • Socket ID:唯一标识每个 SRT Socket
  • Socket 配置:通过 CUDT 实例间接管理
  • 状态信息:连接状态、绑定地址等

CUDT 核心协议引擎

CUDT 是 SRT 协议的核心实现类,承载了所有传输逻辑,包括可靠传输、拥塞控制、流量控制、数据包调度等。该类的头文件依赖揭示了其复杂的内部结构(srtcore/core.h:54-78)。

职责边界:

  • 实现完整的 SRT/UDT 传输协议
  • 管理发送和接收缓冲区
  • 执行拥塞控制算法
  • 处理握手与连接建立
  • 集成数据包过滤与 FEC
  • 不直接操作网络 I/O(委托给队列层)

关键依赖组件:

组件头文件职责
发送缓冲区buffer_snd.h管理待发送数据的缓冲与重传
接收缓冲区buffer_rcv.h管理接收数据的排序与交付
拥塞窗口window.h流量控制与拥塞窗口管理
数据包packet.hSRT 数据包的封装与解析
队列queue.h发送/接收队列的接口
握手handshake.h连接握手协议实现
拥塞控制congctl.h拥塞控制算法抽象
包过滤器packetfilter.hFEC 与包过滤功能
加密haicrypt.hAES 加密/解密集成

关键调用链: 数据发送时,CUDT 将应用层数据封装为 CPacket,经过拥塞窗口检查后放入发送缓冲区,再由发送队列调度通过 CChannel 发出。数据接收时,CChannel 从 UDP 接收数据包,经接收队列分发到对应的 CUDT 实例,经过排序、解密后交付应用层。

错误处理与边界条件:

  • 拥塞窗口满时,数据发送将被阻塞或缓存
  • 接收缓冲区溢出时,根据配置执行丢弃或反压策略
  • 握手超时后触发连接失败回调
  • 加密失败时丢弃对应数据包并记录错误

传输层与多路复用架构

CChannel UDP 通道封装

CChannel 是对底层 UDP socket 的面向对象封装,为上层提供统一的网络 I/O 抽象。该类隐藏了平台相关的 socket 操作细节(srtcore/channel.h:64-92)。

职责边界:

  • 封装 UDP socket 的创建、绑定与关闭
  • 提供基于地址的数据包发送接口
  • 查询本地与对端 socket 地址
  • 管理 UDP 发送/接收缓冲区大小
  • 不处理协议逻辑(如重传、排序)

入口与关键 API:

方法功能参数
open(addr)绑定到指定本地地址sockaddr_any& 本地地址
open(family)创建指定协议族 socketint AF_INET/AF_INET6
attach(udpsock, addr)附加到已有 UDP socketUDP socket 描述符 + 地址
close()关闭 UDP 实体
sendto(addr, packet, src)发送数据包到指定地址目标地址 + CPacket 引用 + 源地址
getSockAddr(addr)查询本地 socket 地址输出地址引用
getPeerAddr(addr)查询对端地址输出地址引用

关键数据结构:

  • sockaddr_any:兼容 IPv4/IPv6 的通用地址结构
  • CPacket:SRT 数据包实体,包含头部与载荷
  • UDPSOCKET:底层 UDP socket 描述符的抽象类型

错误处理:

  • socket 创建失败时抛出异常
  • sendto 返回实际发送字节数,失败时返回错误码
  • 地址查询失败时输出无效地址

发送/接收队列与多路复用器

发送队列 CSndQueue 和接收队列 CRcvQueue 是连接核心协议层与通道层的关键桥梁。多路复用器 CMultiplexer 将队列与通道绑定,允许多个 SRT 连接共享同一个 UDP 端口(srtcore/queue.h:395-618)。

职责边界:

  • CSndQueue:从 CUDT 实例收集待发送数据包,调度通过 CChannel 发出
  • CRcvQueue:从 CChannel 接收 UDP 数据包,根据 Socket ID 分发到对应 CUDT
  • CMultiplexer:管理发送队列、接收队列、通道和定时器的生命周期,维护引用计数

关键数据结构:

CMultiplexer 的核心成员(srtcore/queue.h:586-618):

成员类型职责
m_pSndQueueCSndQueue*发送队列指针
m_pRcvQueueCRcvQueue*接收队列指针
m_pChannelCChannel*UDP 通道指针
m_pTimerCTimer*定时器指针
m_iPortint绑定的端口号
m_iIPversionint地址族(AF_INET/AF_INET6)
m_iRefCountint关联的 UDT 实例计数
m_mcfgCSrtMuxerConfig多路复用器配置
m_iIDint多路复用器唯一 ID

关键调用链:

  1. 发送路径:CUDTCSndQueueCChannel::sendto → UDP 网络
  2. 接收路径:UDP 网络 → CChannelCRcvQueue → 根据 Socket ID 查找 CUDT → 数据包入队

生命周期管理: CMultiplexer 构造函数将所有指针初始化为 NULL,防止内存分配失败时的悬空指针问题。析构函数按序释放接收队列、发送队列、定时器,最后调用 close() 关闭通道。引用计数 m_iRefCount 初始为 1,每个新关联的 UDT 实例递增,断开时递减,归零时销毁多路复用器。

错误处理与边界条件:

  • 队列为空时的定时轮询策略
  • 多路复用器引用计数归零时的资源清理
  • resetAtFork() 方法处理 fork 后的状态重置

安全加密子系统

HaiCrypt 加密配置与密钥管理

HaiCrypt 是 SRT 的独立加密子系统,提供 AES 加密/解密功能。该模块通过配置结构体和句柄式 API 与核心层交互(haicrypt/haicrypt.h:30-58)。

职责边界:

  • 提供 AES-CTR/AES-GCM 模式的加密与解密
  • 管理密钥派生(PBKDF2)与密钥包装(RFC 3394)
  • 处理密钥材料的周期性刷新与预通告
  • 不处理网络传输(由 SRT 核心层负责传输密钥材料消息)

核心加密常量:

常量含义
HAICRYPT_CIPHER_BLK_SZ16AES 块大小(字节)
HAICRYPT_PWD_MAX_SZ80密码最大长度
HAICRYPT_KEY_MAX_SZ32密钥最大长度(AES-256)
HAICRYPT_SALT_SZ16盐值大小
HAICRYPT_AUTHTAG_MAX16认证标签最大长度(GCM)
HAICRYPT_AAD_MAX16附加认证数据最大长度
HAICRYPT_PBKDF2_SALT_LEN8PBKDF2 盐值长度
HAICRYPT_PBKDF2_ITER_CNT2048PBKDF2 迭代次数

关键数据结构:

HaiCrypt_Secret 定义了安全关联的密钥材料(haicrypt/haicrypt.h:51-58):

字段类型职责
typunsigned密钥类型:0=未定义,1=预共享 KEK,2=密码
lensize_t密钥材料长度
strunsigned char[]密钥材料内容(最大 HAICRYPT_SECRET_MAX_SZ

HaiCrypt_Cfg 定义了加密会话的完整配置(haicrypt/haicrypt.h:60-85):

字段类型职责
flagsunsigned配置标志位(TX/RX/加密/FEC/GCM)
secretHaiCrypt_Secret安全关联密钥材料
crysprHaiCrypt_Cryspr加密服务提供者实例
key_lensize_tSEK 长度(默认 16 字节 = AES-128)
data_max_lensize_t最大数据包长度(默认 1500 字节)
xportint传输模式:0=独立,1=SRT
km_tx_period_msunsigned int密钥材料发送周期(默认 1000ms)
km_refresh_rate_pktunsigned int密钥刷新率(默认 0x1000000 包)
km_pre_announce_pktunsigned int密钥预通告包数(默认 0x1000 包)

配置标志位:

标志含义
HAICRYPT_CFG_F_TX0x01发送方向(否则为接收)
HAICRYPT_CFG_F_CRYPTO0x02执行加密操作
HAICRYPT_CFG_F_FEC0x04启用前向纠错
HAICRYPT_CFG_F_GCM0x08使用 AES-GCM 模式

加密句柄与方向: HaiCrypt_Handle 是不透明句柄类型,内部指向 hcrypt_Session_str 结构。加密方向通过 HaiCrypt_CryptoDir 枚举区分:HAICRYPT_CRYPTO_DIR_RX(接收解密)和 HAICRYPT_CRYPTO_DIR_TX(发送加密)(haicrypt/haicrypt.h:87-93)。

错误处理与边界条件:

  • 密钥材料长度超过 HAICRYPT_SECRET_MAX_SZ 时截断
  • 数据包长度超过 data_max_len 时拒绝加密
  • 密钥刷新周期到达时自动生成新密钥并预通告
  • GCM 模式下认证失败时丢弃数据包

数据流与调用链

以下时序图展示了 SRT 发送端从应用层调用 send 到数据包通过 UDP 传出的完整数据流,以及接收端从 UDP 收包到应用层调用 recv 获取数据的反向流程。

正在加载图表渲染器...

数据流要点说明:

  1. 发送路径的分层委托:应用层调用 send 后,CUDTSocket 将请求委托给 CUDT 核心。CUDT 负责数据包封装、拥塞窗口检查,然后调用 HaiCrypt 加密载荷,最后通过发送队列调度发送(srtcore/api.h:77-81)。

  2. 加密在发送队列之前执行:加密操作在 CUDT 核心层完成,确保进入发送队列的数据包已经是密文。这样发送队列无需感知加密状态(haicrypt/haicrypt.h:60-65)。

  3. 接收路径的 Socket ID 路由:接收队列从 CChannel 获取原始 UDP 数据包后,根据 SRT 头部中的 Socket ID 查找目标 CUDT 实例,实现多路复用(srtcore/queue.h:395-618)。

  4. 通道层的无状态转发CChannel 仅执行 sendto 操作,不维护连接状态或重传逻辑。所有可靠性保证由上层 CUDT 实现(srtcore/channel.h:114-120)。

  5. 解密在排序之前执行:接收端先解密再排序,确保排序逻辑处理的是明文序列号,避免加密后序列号不可用的问题。

  6. ARQ 重传的闭环:当接收端检测到丢包时,通过 SRT 控制包(NAK/ACK)通知发送端,发送端从发送缓冲区中取出对应数据包重传,整个重传路径不经过应用层。

核心设计决策与取舍

1. 基于 UDP 的用户态协议栈

SRT 选择在 UDP 之上构建可靠传输协议,而非修改内核协议栈。这一决策使得 SRT 可以在用户态快速迭代,无需操作系统内核支持,同时保持对 UDP 生态(NAT 穿透、防火墙兼容)的兼容性。代价是额外的用户态/内核态数据拷贝开销。

2. 多路复用器的共享与引用计数

CMultiplexer 设计允许多个 SRT Socket 共享同一个 UDP 端口和发送/接收队列。通过引用计数 m_iRefCount 管理生命周期,最后一个 Socket 关闭时才销毁多路复用器。这减少了端口消耗和线程开销,但增加了多路复用器的并发复杂度(srtcore/queue.h:586-604)。

3. 加密子系统的独立抽象

HaiCrypt 作为独立模块,通过 HaiCrypt_Cryspr(加密服务提供者)接口支持不同的加密后端实现。这种抽象允许在不同平台上使用硬件加速(如 AES-NI)或软件回退,但增加了间接调用开销(haicrypt/haicrypt.h:30-32)。

4. 密钥材料的带内传输

SRT 选择通过 SRT 协议本身传输密钥材料,而非依赖外部密钥分发机制。HaiCrypt_Cfg 中的 km_tx_period_ms(默认 1000ms)和 km_refresh_rate_pkt(默认 0x1000000 包)控制密钥材料的周期性发送和刷新。这简化了部署但要求协议本身具备足够的安全性(haicrypt/haicrypt.h:79-84)。

5. CUDTSocket 与 CUDT 的双层架构

将 Socket 管理(CUDTSocket)与协议逻辑(CUDT)分离,使得同一个协议引擎可以被不同的 API 层使用。CUDTSocket 处理 Socket 生命周期和配置,CUDT 专注传输逻辑。这种分离提高了代码可维护性,但也增加了间接层(srtcore/api.h:79-81)。

6. 已知限制

  • SRT 当前不支持多路径传输(Multipath),所有数据流通过单一 UDP 连接传输
  • FEC 作为包过滤器的扩展实现,与核心传输逻辑的耦合度较高
  • CChannelCONID() 方法当前返回空字符串,表明通道层与 Socket ID 的关联尚未完全实现(srtcore/channel.h:69-73

技术选型表格

技术用途选型理由替代方案
UDP底层传输协议低延迟、无连接开销、NAT 友好TCP(延迟高)、SCTP(部署少)、QUIC(生态不成熟)
AES-CTR默认加密模式硬件加速支持好、流式加密适合媒体传输AES-CBC(需要填充)、ChaCha20(硬件支持弱)
AES-GCM认证加密模式同时提供加密与完整性验证AES-CTR + HMAC(两遍运算开销大)
PBKDF2密码派生密钥标准成熟、迭代次数可配置Argon2(更新但库依赖重)、bcrypt(不适合密钥派生)
RFC 3394 Key Wrap密钥包装NIST 标准的密钥加密密钥方案自定义密钥包装(安全性难验证)
用户态协议栈协议实现位置跨平台、快速迭代、无需内核支持内核模块(部署复杂)、DPDK(硬件依赖重)
多路复用器模式连接管理多连接共享端口、减少资源消耗每连接独立端口(端口耗尽风险)
引用计数生命周期管理自动化共享资源管理、零开销垃圾回收(延迟不可控)、手动管理(易出错)

模块依赖关系图

以下图表展示了 SRT 核心模块之间的依赖方向,从上层 API 到底层网络 I/O 的完整依赖链。

正在加载图表渲染器...

依赖关系要点说明:

  1. CUDT 是依赖中心:几乎所有核心组件都直接依赖 CUDT,而 CUDT 反向依赖缓冲区、窗口、数据包、拥塞控制、握手、过滤器和加密等模块(srtcore/core.h:54-78)。

  2. 队列层的双向依赖CSndQueueCRcvQueue 既被 CUDT 依赖(作为数据传输通道),也被 CMultiplexer 依赖(作为生命周期管理对象)(srtcore/queue.h:395-618)。

  3. 通道层的单一依赖者CChannel 仅被发送队列和接收队列直接使用,CUDT 不直接操作通道,保持了 I/O 层的隔离(srtcore/channel.h:64-92)。

  4. 加密子系统的叶子节点特征:HaiCrypt 不依赖任何 SRT 核心模块,是完全独立的加密库,仅通过 CUDT 的调用向上提供服务(haicrypt/haicrypt.h:30-93)。

关键配置与启动流程

加密配置

SRT 的加密行为通过 HaiCrypt_Cfg 结构体进行配置。以下是一个典型的加密配置流程:

c
1// 1. 获取加密服务提供者实例
2HaiCrypt_Cryspr cryspr = HaiCryptCryspr_Get_Instance();
3
4// 2. 配置加密参数
5HaiCrypt_Cfg cfg;
6cfg.flags = HAICRYPT_CFG_F_TX | HAICRYPT_CFG_F_CRYPTO;  // 发送方向 + 启用加密
7cfg.cryspr = cryspr;
8cfg.key_len = HAICRYPT_DEF_KEY_LENGTH;     // 16 字节 = AES-128
9cfg.data_max_len = HAICRYPT_DEF_DATA_MAX_LENGTH;  // 1500 字节
10cfg.xport = HAICRYPT_XPT_SRT;              // SRT 传输模式
11
12// 3. 设置密钥材料
13cfg.secret.typ = HAICRYPT_SECTYP_PASSPHRASE;  // 使用密码模式
14cfg.secret.len = password_length;
15memcpy(cfg.secret.str, password, cfg.secret.len);
16
17// 4. 配置密钥刷新策略
18cfg.km_tx_period_ms = HAICRYPT_DEF_KM_TX_PERIOD;           // 1000ms
19cfg.km_refresh_rate_pkt = HAICRYPT_DEF_KM_REFRESH_RATE;    // 0x1000000 包
20cfg.km_pre_announce_pkt = HAICRYPT_DEF_KM_PRE_ANNOUNCE;    // 0x1000 包

多路复用器初始化

CMultiplexer 的初始化流程遵循严格的顺序:构造时所有指针置 NULL,随后依次创建通道、定时器、发送队列和接收队列,最后绑定到指定端口。引用计数从 1 开始,每个新关联的 SRT Socket 递增计数(srtcore/queue.h:595-616)。

连接建立流程

SRT 连接建立涉及以下关键步骤:

  1. 创建 Socket:通过 CUDTUnited 创建 CUDTSocket,内部实例化 CUDT
  2. 配置参数:设置加密、传输、缓冲区等参数
  3. 绑定多路复用器:将 Socket 关联到现有或新建的 CMultiplexer
  4. 执行握手:通过 handshake 模块交换连接参数和密钥材料
  5. 启动数据传输:握手完成后,发送/接收队列开始工作

密钥生命周期管理

SRT 的密钥管理遵循"预通告-激活-过期"三阶段模型:

  • 预通告阶段:新密钥在 km_pre_announce_pkt(默认 4096)个包之前开始发送
  • 激活阶段:新密钥在 km_refresh_rate_pkt(默认 16777216)个包后激活
  • 过期阶段:旧密钥在预通告期结束后失效

这种设计确保了密钥切换过程中的零中断传输,接收端在过渡期间可以同时使用新旧密钥解密。