news 2026/10/1 10:44:56

SRT协议握手全解析:从UDP控制包到M1-M4抓包实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SRT协议握手全解析:从UDP控制包到M1-M4抓包实战

SRT协议这几年在直播、远程制作、无界传输这些场景里越来越常见,很多团队从RTMP转过来之后,第一个遇到的拦路虎就是握手。老实说SRT的握手和RTMP那种一次HTTP式的交互完全不同,它是在UDP上自己造了一套逻辑连接,如果你不把握手控制包的结构和时序搞清楚,后面无论是调延迟、开加密还是排查"连不上"的问题,都会非常痛苦。这篇文章我就以一次完整的SRT建连过程为线索,从控制包头讲到握手扩展,再从抓包实战讲到踩坑排查,争取让你看完之后能独立分析一次SRT握手。

我尽量把每个字段为什么存在、每个阶段为什么这样设计讲透,而不只是罗列结构。适合正在接入SRT的流媒体开发者、运维、以及想深入理解SRT协议的测试同学。

1. 先弄清SRT握手到底在解决什么问题

1.1 UDP上的"连接"是如何凭空变出来的

SRT的一个核心前提是使用UDP传输。UDP本身没有连接概念,两个进程之间发数据包就是发数据包,不存在"建立连接"这个动作。但SRT要实现可靠传输、流控、加密、丢包重传,就必须先让通信双方知道"我正在和谁通信",这就需要一个逻辑连接。

这个逻辑连接的标识就是Socket ID。SRT每个数据包和控制包头里都带了一个Destination Socket ID,接收方看到这个ID就能判定这个包属于哪条会话。Socket ID不是在UDP层自动产生的,而是双方在握手阶段互相告知的:先由发起方临时生成一个ID告诉接收方,接收方再生成自己的ID回传。之后双方发的所有包,都要把对方的Socket ID填到目标字段里。

用生活里的例子类比,UDP像是寄明信片,地址写错也能寄出去,但寄件人和收件人之间没有约定暗号。SRT的握手就是两个人第一次见面时互相确认:"我叫A,你记住我的编号,下次写信给我记得写这个编号",然后两人各掏出一本通讯录,把对方的编号记进去。没有这一步,后面所有包都无从归属。

1.2 一次握手要完成的四件大事

很多人误以为SRT握手就是"打个招呼",实际上它要在两个阶段内完成四件事:

第一,建立逻辑连接并同步Socket ID。这是最基础的目标,上面已经说了。

第二,用SynCookie机制抵御UDP洪水攻击。因为UDP可以伪造源地址,如果服务端收到一个握手包就立刻分配资源并回复,攻击者用伪造IP就能打爆服务端内存。SRT借鉴了TCP的SynCookie思路:服务端第一次收到握手包时不分配任何资源,只根据客户端的IP、端口、时间等信息计算出一个4字节的Cookie,在响应包里带回去。客户端必须在下一次握手中原样带回这个Cookie,服务端验证通过后才真正创建会话。这样一来,伪造源地址的攻击者收不到响应,也就无法完成第二次握手,资源自然消耗不掉。

第三,协商传输参数。完整SRT连接需要先确定数据包最大长度(MSS)、流控窗口大小、起始时间基准、SRT延迟、传输类型、方向角色等。这些参数如果各持己见,后面收包解包就会对不上。比如MSS不一致会导致分片重组错乱,延迟参数不匹配会导致TSBPD时间计算混乱,接收端丢包或者播放卡顿。

第四,确认加密密码并协商密钥材料。SRT的一大卖点是端到端加密。在握手阶段,发起方会把密码的散列值(pw_hash)、密码长度(pw_length)以及HMAC密钥直接带在握手包里,接收方用自己配置的passphrase去比对。如果discovery不匹配,直接拒绝对端。握手通过后,双方再走KMREQ/KMRSP两条扩展消息交换真正的AES加密密钥,之后数据面全部走加密。

所以你看,一次握手表面上只有4个包,实际上承载了身份确认、防攻击、参数对齐、安全认证四重任务。这也是为什么SRT握手比RTMP、WebSocket这些基于TCP的握手复杂得多。

1.3 两阶段握手:为什么要拆成两步

SRT握手拆成了诱导握手(Induction)和完成握手(Completion)两个阶段,对应M1/M2和M3/M4四步。这样设计主要是出于安全考虑。

诱导阶段主要做两件事:一是防DoS,服务端通过SynCookie验证客户端是否真实可达;二是做一个版本降级的握手通道。为什么SRT的诱导握手要特意使用一个固定的"较老版本号"(比如0x0102)?因为握手本身也有兼容性问题,新老版本实现必须能先在一个双方都认识的最小公共版本上对话,后续进入完成阶段后再升级到真实配置。有点像两个人第一次见面先说普通话,确认彼此都会说普通话之后,再切换到各自的方言加普通话混合模式。

完成阶段才是真正的参数交换。此时客户端发出M3,里面带真实SRT版本(比如0x0104)、完整参数、SynCookie回传,以及一系列扩展块。服务端验证Cookie和密码散列后,回M4确认。到这一步,双方才真正具备发送数据包的条件。

弄清楚了握手在解决什么问题,我们再来解剖控制包本身,看看每个字节是怎么组织起来的。

2. 控制包解剖:从第一个字节看起

2.1 控制包头:如何区分数据包和控制包

SRT在UDP上的包有两种:数据包和控制包。它们的包头前16字节结构不同,靠第一个bit来区分。数据包的第一个bit是0,控制包的第一个bit是1。

控制包的16字节包头具体长这样:

位域长度说明
F标志1 bit固定为1,表示这是控制包
控制包类型15 bit握手=0,Keepalive=1,ACK=2,NAK=3,拥塞控制=4,对端错误=5,EXT=6
子类型8 bit仅当控制类型为EXT(6)时有效,用于区分具体扩展控制类型
类型特定信息8 bit不同控制类型有的用之,握手包里通常为0
时间戳32 bit发送方在起始时间基准上的相对时间戳
目标Socket ID32 bit该控制包所对应的逻辑连接对端ID

这里有一个关键点:控制包里带的时间戳不是绝对时间,而是相对于握手阶段协商出来的起始时间基准的偏移。所以握手包里的起始时间戳(srt_start_time)非常重要,两边后续所有时间戳都以它为零点来计算。

在Wireshark里,一个SRT握手包通常显示为协议列SRT、Info列类似"Handshake, INDUCTION"的内容。它实际上就是在UDP负载最前面压了一个16字节的控制包头,后边跟着握手消息本体。

2.2 握手消息固定区:56字节的核心字段

控制包头之后是握手消息。握手消息有一个固定区,不管什么阶段、什么版本,这块结构都是固定的。它的字段顺序如下:

偏移字段长度核心作用
0UDT/SRT版本4字节协议版本号,如0x0102、0x0103、0x0104
4包索引/握手类型4字节低16位表示握手类型,诱导/诱导确认/完成/完成确认
8起始时间戳4字节发送方基准时间,用于TSBPD时间换算
12流控窗口4字节接收方通告的可用接收窗口大小
16最大段大小MSS4字节单个数据分片的最大字节数
20传输类型2字节流媒体直播LIVE / 文件传输FILE等
22方向2字节Caller(主动发起方)或Listener(监听方)
24对端Socket ID4字节握手发起方声明的Socket ID
28对端配置4字节携带对端相关配置位
32密码长度4字节0表示不加密,非0表示使用passphrase
36密码哈希4字节passphrase散列结果的前4字节
40HMAC密钥16字节用于后续握手消息完整性校验的密钥

注意这里的"对端配置"在各版本里含义有差异,但总体上它是对端在SrtConfig里声明的配置集合,比如是否启用特定过滤器、是否存在分组协商等。实际调试中我更关注版本号、MSS、流控窗口和加密相关字段。

还要强调一个容易踩的坑:握手消息固定区里面,Version字段在不同阶段承载的含义不一样。在诱导阶段M1/M2里,Version字段不一定代表真实协议版本,M2的Version字段实际上装的是SynCookie值。只有在完成阶段M3/M4里,Version字段才是真的协议版本。所以别拿M2里的版本号去判断对端SRT版本,那是会误判的。

2.3 从抓包角度快速定位握手包

如果你在Wireshark里看到一坨SRT包,最快的定位握手包方法是看两点:一个是控制包类型字段等于0,另一个是Info列里有"Handshake"字样。过滤表达式可以写成:

srt.hs && srt.hs.type == 0

其中srt.hs.type是握手阶段的类型字段,0对应INDUCTION,1对应INDUCTION_ACK,2对应COMPLETION,3对应COMPLETION_ACK。这样你就能直接从一堆UDP包里把四个握手包全部拉出来,按时间排序看它们的交互顺序。

如果Wireshark没有自动识别出SRT协议,需要手动让某个UDP端口按SRT解析。做法是在Wireshark里右键某个UDP包,选择"Decode As",把对应端口指定为SRT。这一步在抓广播流量或者使用非标准端口时特别常用,我建议所有测试SRT的人先把这步记住。

3. 握手扩展与加密协商

3.1 扩展字段的TLV机制

固定区只是握手消息的地基,真正传递现代SRT参数的是扩展区。扩展区紧跟在固定区后面,采用类似TLV的格式逐块排列:

字段长度说明
扩展类型2字节例如1=HSREQ,2=KMREQ,3=KMRSP,4=SRT,5=TSBPDSND,6=TSBPDRCV,7=TRIM
扩展长度2字节后面的数据体字节数
扩展数据变长具体内容随类型变化

扩展可以出现多个,依次拼接。接收方按类型逐个解析,遇到不认识或不需要的扩展类型可以跳过,这就保证了前向兼容。老版本实现即使不认识新扩展,也能通过跳过的方式继续完成握手。

这种TLV设计很务实。SRT从1.2到1.4演进过程中加了很多东西:TSBPD、加密、分组传输、过滤器,但核心固定区一直没变,全靠扩展区做增量。如果当初把所有参数都塞进固定区,现在恐怕已经乱成一锅粥了。

3.2 HS_EXT_SRT与直播关键参数

在所有扩展块里,HS_EXT_SRT(扩展类型4)是完成阶段最重要的一个。它携带了双方真正关心的SRT业务参数,主要包括:

  • SRT版本(2字节):对端运行的SRT版本号,比如0x0104表示1.4.x系列。
  • SRT标志(2字节):一个位掩码,里面包含加密类型、是否启用NAKREPORT、是否启用REXMIT、过滤器类型等信息。
  • 对端类型(2字节):LIVE(实时直播)或FILE(文件传输),不同模式拥塞控制行为差异很大。
  • 对端延迟(4字节):latency,单位是毫秒,这是TSBPD的核心参数,直接影响接收端的抖动容忍度。

TSBPD(TimeStamp-Based Packet Delivery)简单说就是接收端不拿到包就立刻丢给上层,而是根据包时间戳和预设延迟,算好"这个包该什么时刻被上层使用",到点再投递。这样可以吸收网络抖动,让播放器看到的是平滑的码流。但延迟设大了会增加端到端时延,设小了又吸收不了抖动。所以HS_EXT_SRT里协商出来的latency,往往要结合网络状况反复调。

另一组扩展是HS_EXT_TSBPDSND和HS_EXT_TSBPDRCV,分别对应发送方向和接收方向。它们主要告诉对端自己如何配置TSBPD的发送/接收行为,比如是否启用基于时间的发送调度、期望对端如何配合。搞懂这些扩展之间的配合,你就能理解为什么SRT调延迟不是只改一边参数就行:接收端延迟和发送端调度必须对齐,否则实际效果会和自己想的不一样。

3.3 加密协商与密钥材料交换

SRT加密有两层。第一层是握手消息里的passphrase校验。配置了passphrase的caller在M3里会带上密码哈希(pw_hash)和密码长度(pw_length)。listener收到后,用自己的passphrase计算同样的散列,对比前4字节。不一致就直接拒绝或标记错误。

这里有个细节:pw_hash只取SHA-1散列结果的前4字节,本质上是个指纹。它的设计目的不是防暴力破解,而是快速排除"根本不是一个密码"的情况。真正的安全性来自第二层:握手通过后,双方通过KMREQ/KMRSP交换密钥材料(Keying Material),用它生成后续数据加密用的AES对称密钥。密钥材料本身在握手消息的HMAC密钥保护之下,所以即使有人抓到包,也很难在不知道passphrase的情况下还原密钥。

实际使用中我建议:如果排查加密问题,先关掉加密用明文握手跑一遍,通了再开加密。这样做能快速区分问题是出在基本握手链路还是出在密码/密钥协商环节,省掉大量冤枉时间。

4. 抓包实战:完整解析一次SRT握手

4.1 本地环境搭建与抓包命令

为了讲得具体,我在本地搭一个最小环境:一台机器上用ffmpeg起一个SRT listener,另一侧用ffmpeg以caller模式推流,中间用Wireshark抓回环或物理网卡上的UDP包。

listener端:

ffmpeg -i "srt://127.0.0.1:9000?mode=listener&latency=2000" -f null -

caller端:

ffmpeg -f lavfi -i testsrc=size=1280x720:rate=30 -c:v libx264 -preset ultrafast -tune zerolatency -f mpegts "srt://127.0.0.1:9000?mode=caller&latency=2000"

Wireshark里直接抓loopback接口,过滤udp.port == 9000。这样抓到的包很干净,主要就是握手、Keepalive、ACK/NAK、数据包。我习惯再加一个!srt.data过滤,先只看控制面,把握手时序看得清清楚楚。

如果手头没有ffmpeg,也可以用srt-live-transmit这类专用工具,它带的日志比ffmpeg更详细,能看到更多握手内部状态。不过抓包分析角度,ffmpeg + Wireshark已经足够。

4.2 逐包解读M1到M4

抓包后,按时间顺序把四个握手包拉出来,逐个看关键字段。

M1(Caller发起诱导握手)

这个包从caller临时端口出发,目标端口9000。控制包头类型=0,后面的握手消息里:

  • 版本:0x0102(诱导阶段故意用低版本)
  • 包索引/握手类型:0(INDUCTION)
  • 起始时间戳:一个随机整数,作为通话时间基准
  • 流控窗口:比如8192
  • MSS:1500
  • 方向:0(Caller)
  • 对端Socket ID:0,因为此时还没有对端ID
  • 密码长度/哈希:如果配置了passphrase,这里会带,否则为0

M1的作用是投石问路,不需要携带真正的业务配置。关键是让listener能识别这是一个SRT握手,并根据源地址生成SynCookie。

M2(Listener回复诱导确认)

listener收到M1后,不分配资源,根据IP、端口、时间计算SynCookie,在M2里返回。这时候抓包看到的字段是:

  • 版本字段里放的其实是SynCookie(一个看似随机的4字节值)
  • 包索引/握手类型:1(INDUCTION_ACK)
  • 对端Socket ID:listener自己的Socket ID
  • 时间戳:listener的起始基准时间

注意M2里没有携带业务参数。这个包的目的只有一个:向caller证明"我能收到你的包,同时也让你知道你在我这边是可以被放行的"。

M3(Caller发起完成握手)

caller收到M2后,校验SynCookie,然后发送真正的完成握手。M3是SRT握手信息量最大的一个包:

  • 版本:0x0104(真实版本)
  • 包索引/握手类型:2(COMPLETION)
  • SynCookie值:原样放在对应字段里带回
  • 固定区里的流控窗口、MSS等参数更新为真实值
  • 扩展区开始出现HS_EXT_SRT,里面带着SRT版本、标志、对端类型、latency
  • 如果启用了加密,密码长度和密码哈希也会带上

抓包时重点看M3里的扩展区。listener就是在这里做最终决策的:Cookie对不对、密码对不对、参数能不能接受。任何一个不满足,listener都可能不回M4,或者回错误信息。

M4(Listener确认完成握手)

M4是ack包,类型为COMPLETION_ACK。listener告诉caller:"我认可你的参数,连接已建立。" 到这一步,双方的Socket ID、MSS、流控窗口、时间基准、延迟、密钥材料都已经对齐,可以开始发数据了。

如果你开启了加密,接下来还会看到KMREQ和KMRSP两条扩展控制消息往返,之后数据包内容才是密文。抓包时密文数据包负载看起来是随机的,不要误以为是乱码。

4.3 握手成功后的参数对照

握手结束后,我习惯把Wireshark里看到的M3/M4参数整理成一张对照表:

参数Caller声明的值Listener确认的值影响
协议版本1.4.x1.4.x决定功能集
MSS15001500分片大小
流控窗口81928192接收窗口
传输类型LIVELIVE拥塞控制模式
方向CallerListener角色确认
延迟latency2000ms2000msTSBPD缓冲
加密类型AES-128AES-128数据面加密

如果某一栏双方对不上,比如caller声明MSS=1500、listener回应MSS=1200,那后续就可能出现分片重组错乱。Wireshark里直接对比这两个包里的字段是最快的排查手段。

5. 握手失败排查与经验技巧

5.1 常见问题速查表

我把实际项目中遇到过的SRT握手问题整理成了一张速查表,方便你对照排查:

现象可能原因排查手段
握手超时,收不到M2UDP被防火墙丢弃、NAT未映射、listener未启动Wireshark双向抓包确认UDP是否到达;检查listener进程
收到M2但M3后无响应SynCookie校验失败抓包对比M2里的Cookie和M3里带回的Cookie是否一致
收到M2但M3后收到错误密码不一致或加密类型不匹配比对M3里pw_hash与listener本地配置;关掉加密做对照测试
连接建立后立即断开参数协商冲突,比如延迟为0或MSS异常看M3和M4里扩展参数是否一致
握手成功但推流卡顿latency设置偏小调大latency观察TSBPD表现

大多数握手问题都集中在"UDP不通"和"加密配置不一致"两类。建议一上来先用明文、无加密的配置做最小验证,确认基本链路通了,再加加密和复杂参数。

5.2 抓包排查的几个实用技巧

Wireshark里专门做SRT握手分析时,有几个人人能用上的技巧。

第一,强制解码端口。如果你用了一个不常见的端口,Wireshark可能不识别SRT。右键任意UDP包,Decode As,选择SRT,立马就出来完整解析。

第二,用过滤表达式只看握手。srt.hs能筛出所有SRT握手控制包,srt.hs.type == 2能只看完成握手。分析时序时再叠加tcpdump在外面抓,效果更可控。

第三,看握手耗时。我通常统计从M1到M4的时间戳差,一般局域网内这个值在1毫秒以内,公网也不应该超过几十毫秒。如果这个值很大,说明中间链路质量差或服务端处理慢。

第四,抓包时用-s 200限制抓包长度就够了。握手包本身不大,限制快照长度可以减小抓包文件体积,方便回传分析。

sudo tcpdump -i any -s 200 -w srt_handshake.pcap udp port 9000

5.3 个人踩坑记录

最后分享几个我自己印象深刻的坑。

第一个坑是NAT环境下的SynCookie失败。SRT服务端在公网,客户端在内网,中间经过了好几层NAT。M2到了客户端再回M3时,服务端看到的源IP和M1不一样,导致SynCookie校验失败。这个问题排查起来极其隐蔽,因为Wireshark里看起来M1、M2、M3都正常,最后就是连不上。解决办法是在边界设备上做好端口映射,保证同一会话的UDP源地址稳定。

第二个坑是passphrase里多了不可见字符。有一次客户反馈两边都配了密码但握手失败,抓包一对比,M3里的pw_hash和listener本地计算的hash不一致。后来查出来是配置文件里行尾多了一个空格,肉眼完全看不出来。从那以后我调试加密问题时,都会用十六进制去对比配置文件的每个字节。

第三个坑是latency参数两边不一致导致播放端延迟异常。caller设了2000ms,listener设了4000ms,协商时没有报错,连接也建立了,但播放端实际缓冲明显偏大。后来翻源码和抓包对比才理解,TSBPD接收行为以listener的latency为准,发送调度又受caller参数影响,两边不一致就会出现这种"表面成功、实际怪异"的现象。现在我的习惯是:所有SRT端点的latency从同一份配置管理,避免各写各的。

我个人体会是,SRT握手的协议栈结构虽然比传统UDP协议复杂,但只要你把控制包头、握手消息固定区、扩展区这三层拆开理解,再把诱导和完成两个阶段在抓包工具里完整走一遍,绝大部分问题都能肉眼定位。尤其是当你需要给生产环境排查加密或NAT问题时,能熟练指认M3里的SynCookie和pw_hash字段,效率会高出一大截。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/1 10:44:53

Lambda上部署sentence-transformers:从打包失败到性能调优全指南

“用Lambda跑sentence-transformers,听起来就是个挺常规的需求,但真上手的时候,第一课往往是从‘打包失败’开始的。作为常年跟文本向量化、语义检索打交道的人,我这两年在AWS Lambda上部署过好几轮Sentence Transformer相关的服务…

作者头像 李华
网站建设 2026/10/1 10:44:20

跨系统配置同步实战:告别手动复制,用 Git+chezmoi+Syncthing 统一权限

2026 年了,你的配置和权限还在“手动复制”吗 我先说一个每天都要炸几遍的场景:公司给你配了台 Windows 台式机做日常办公,家里有一台 macOS 的笔记本写代码,云上还挂着一台 Linux 服务器跑服务。你周一在 Windows 上改了 .gitco…

作者头像 李华
网站建设 2026/10/1 10:44:05

企业管理软件项目结构设计:从业务模块拆分到代码分层的最佳实践

刚开始带一个十人左右的技术团队接手《看潮企业管理软件》那阵子,我最大的感受不是功能难写,而是代码越写越乱——今天加个销售单据,明天补个库存查询,后天改个审批流程,每个人都在自己觉得顺手的位置放文件。项目开发…

作者头像 李华
网站建设 2026/10/1 10:42:59

QFD实战:从客户抱怨到产线参数的工程转化方法

简介:本资源是一份面向品质管理从业者、制造业工程师及高校相关专业师生的系统性教学课件,聚焦质量功能展开(QFD)这一以市场为导向的核心质量管理方法。课件深入解析QFD如何将顾客需求精准转化为产品特性、技术参数与过程控制要求…

作者头像 李华
网站建设 2026/10/1 10:41:23

OpenClaw(Clawdbot)部署全记录:从WSL2环境坑到8分钟跑通AI Agent

上周在Agent交流群里看到有人问:OpenClaw能做啥?为什么翻了一圈部署文档,最后还是卡在wsl --status那一步,日志贴出来也没人接得上话。这个问题我太有共鸣了——我在Windows笔记本、一台旧Linux工作站,还有一台免费试用…

作者头像 李华
网站建设 2026/10/1 10:40:34

扑克牌目标识别实战:VOC转YOLO与YOLOv8训练指南

简介:面向目标检测入门与实战的扑克牌标注数据集,专门用于识别queen、ten、nine、king、jack、ace六种常见扑克牌面。数据包含363张真实场景图片,每张均配有labelimg生成的Pascal VOC格式xml标注,共726个文件,压缩包大…

作者头像 李华