SRS WebRTC Signaling 信令服务实战:Docker 部署、源码构建与 HTTPS/WSS 反向代理指南
【免费下载链接】srsSRS is a simple, high-performance, AI-driven real-time media server supporting RTMP, WebRTC, HLS, HTTP-FLV, HTTP-TS, SRT, MPEG-DASH, and GB28181, with codec support for H.264, H.265, AV1, VP9, AAC, Opus, and G.711.项目地址: https://gitcode.com/GitHub_Trending/sr/srs
SRS(Simple Realtime Server)采用「信令面与媒体面分离」的架构:媒体面由 SRS 的 RTC 模块负责(WHIP/WHEP、SRTP/SRTCP、ICE/DTLS),而信令面需要一个轻量级服务来协调浏览器之间的房间加入、发布状态与呼叫控制。trunk/3rdparty/signaling正是 SRS 官方随仓库发布的demo WebRTC signaling——一个基于 Go + WebSocket 的极简信令服务。本文将以 signaling/README.md 为骨架,完整讲解如何用 Docker 一键跑通 SRS + signaling 的 WebRTC 通话演示、如何从源码构建运行,并结合 main.go 与前端 SDK 源码剖析其信令协议与房间模型,最后给出通过 httpx-static 将演示服务升级为 HTTPS/WSS 的完整方案。读完本文,你可以独立搭建一套可演示、可学习、可二次开发的一对一/多人 WebRTC 信令系统。
一、signaling 是什么:SRS WebRTC 架构中的信令面
在 SRS 的 WebRTC 解决方案中,浏览器之间的媒体流通过 SRS 的 SFU(Selective Forwarding Unit)进行转发,这一过程使用标准化的 WHIP(WebRTC-HTTP Ingestion Protocol,推流)与 WHEP(WebRTC-HTTP Egress Protocol,拉流)完成 SDP 交换;而「谁在哪个房间、谁开始发布、谁要离开、互相发送控制消息」这类协调工作,则需要信令服务来完成。signaling 正是承担这一角色的官方 demo 实现。
该组件位于仓库trunk/3rdparty/signaling/目录,核心构成如下:
| 文件/目录 | 作用 |
|---|---|
| README.md | 使用与构建文档(本文主体) |
| main.go | Go 实现的服务端,提供 WebSocket 信令与静态页面服务 |
| Makefile | 构建脚本,产出objs/signaling二进制 |
| Dockerfile | 基于 Ubuntu 的两阶段镜像构建 |
| go.mod | Go 模块定义,依赖go-oryx-lib与golang.org/x/net |
| auto/pub.sh | 打 tag 发布脚本 |
| www/demos/ | 一对一会话、多人房间等 H5 演示页面与前端 SDK |
从 go.mod 可以看到,服务端基于 Go 1.16,仅依赖两个模块:github.com/ossrs/go-oryx-lib(日志、错误处理等基础库)与golang.org/x/net(其中提供websocket包)。整个服务端核心逻辑集中在单个 main.go(约 350 行)中,非常适合作为学习与二次开发的起点。
二、快速体验:Docker 一键跑通 SRS + signaling
2.1 启动 SRS(RTC 配置)
首先在 Docker 中启动 SRS,使用 RTC 专用配置 trunk/conf/rtc.conf,并通过环境变量CANDIDATE指定本机公网/局域网 IP(用于 ICE candidate 协商):
docker run --rm --env CANDIDATE=$(ifconfig en0 inet| grep 'inet '|awk '{print $2}') \ -p 1935:1935 -p 8080:8080 -p 1985:1985 -p 8000:8000/udp \ registry.cn-hangzhou.aliyuncs.com/ossrs/srs:4 \ objs/srs -c conf/rtc.conf说明:
CANDIDATE应替换为实际可达的 IP(如ifconfig输出中的 en0 地址,或云主机公网 IP)。更多镜像与版本可参考镜像仓库页面。
各端口在 SRS 中的作用:
| 端口 | 协议 | 用途 |
|---|---|---|
| 1935 | TCP | RTMP 推拉流 |
| 8080 | TCP | SRS HTTP 服务(HLS、HTTP-FLV 等) |
| 1985 | TCP | SRS HTTP API(HTTP API、WHIP/WHEP SDP 交换) |
| 8000 | UDP | WebRTC 媒体流(SRTP/SRTCP) |
2.2 启动 signaling 服务
在另一个终端启动 signaling,监听 1989 端口:
docker run --rm -p 1989:1989 registry.cn-hangzhou.aliyuncs.com/ossrs/signaling:1说明:signaling 镜像会同时托管静态演示页面(
www目录),因此 1989 端口既是 WebSocket 信令端口,也是 H5 演示页面的 HTTP 端口。
2.3 打开 H5 演示
浏览器访问即可进入一对一通话演示(带autostart=true自动开始):
- WebRTC: One to One over SFU(SRS)
这是 README 给出的最小验证路径:两个浏览器分别打开上述地址,加入同一房间后即可互通音视频。整套环境仅需两个容器,无需任何代码。
三、从源码构建与运行
如果希望调试、修改或深入学习,可以从源码构建。
3.1 构建并运行 SRS
在已就绪的 SRS 仓库中,进入trunk目录执行配置与编译(rtc.conf位于 trunk/conf/rtc.conf):
cd ~/git/srs/trunk && ./configure && make && ./objs/srs -c conf/rtc.conf3.2 构建并运行 signaling
在 signaling 目录执行make,产物为objs/signaling:
cd ~/git/srs/trunk/3rdparty/signaling && make && ./objs/signaling从 Makefile 可以看到构建细节:make会先执行gofmt -w .统一格式(生成.format.txt标记),然后以go build -mod=vendor -o objs/signaling .进行编译。目标文件仅一个二进制 +www静态目录,非常轻量。
运行后浏览器打开演示列表:http://localhost:1989/demos
3.3 服务端启动参数
从 main.go 可以看到,signaling 支持两个命令行参数:
| 参数 | 默认值 | 说明 |
|---|---|---|
-listen | 1989 | TCP 监听端口(信令 WebSocket + 静态页面共用) |
-root | ./www | 静态页面根目录;若为相对路径且可执行文件路径为绝对路径,则自动拼接到可执行文件所在目录 |
启动日志会输出Signaling ok, root=..., home page is ...,其中 home page 即演示首页地址。
四、信令协议与核心源码剖析
4.1 两个 HTTP 端点
signaling 在同一个端口上提供了三个路由(见 main.go):
/—— 静态文件服务(http.FileServer),托管www演示页面;/sig/v1/versions—— 返回字符串1.0,用于版本探活;/sig/v1/rtc—— WebSocket 信令端点,核心逻辑所在。
4.2 WebSocket 消息帧格式
客户端通过ws://host:1989/sig/v1/rtc?room=<房间名>&display=<昵称>建立连接(见 srs.sig.js)。消息为 JSON 文本帧,外层带事务 IDtid:
{ "tid": "a1b2c3d", "msg": { "action": "join", "room": "live", "display": "alice" } }服务端响应同样携带tid回包;而服务端主动广播的通知(notify)则不带tid,通过event字段区分事件类型。
4.3 三种 Action:join / publish / control
从 main.go 可以完整还原服务端对三类消息的处理:
join(加入房间):解析msg.room与msg.display,通过sync.Map的LoadOrStore获取/创建房间,创建Participant并调用Room.Add加入。Add会做同名检查(main.go),若房间内已存在相同display则返回错误。成功后向本人返回{action, room, self, participants}(房间当前成员列表),并向其他成员广播event: "join"。
publish(宣布发布):找到房间内对应display的参与者,将其Publishing置为true,随后向其他成员广播event: "publish"。收到该事件的成员即可知道「有人开始推流了」,从而发起 WHEP 拉流。
control(控制消息):携带call与data两个透传字段(如call: "refresh"、call: "alert"+ 任意data),服务端将其原样广播给房间内其他成员,实现演示中的「刷新对方页面」「给对方弹窗」等自定义控制。
4.4 服务端数据模型:Room 与 Participant
服务端的数据模型非常清晰(main.go):
Participant:Display(昵称)、Publishing(是否推流中)、Out(该连接的消息发送通道chan []byte);Room:Name(房间名)、Participants(成员切片),并以sync.RWMutex保护并发读写;- 全局
rooms sync.Map:以房间名为 key 持有所有房间。
连接的读写模型采用双 goroutine + 通道:读取 goroutine 循环Read消息并送入inMessages,处理 goroutine 逐个处理并回包,写入 goroutine 循环从outMessages取数据Write回客户端。连接断开时(main.go),服务端会先向房间内其他成员广播event: "leave",再将自身从房间移除——这正是「有人离开」通知的来源,前端收到leave事件后会隐藏播放器(见 one2one.html)。
4.5 广播通知的完整结构
Room.Notify(main.go)在加锁快照成员列表后,向除发送者外的每个成员投递以下结构:
{ "action": "notify", "event": "join | publish | leave | control", "param": "", "data": "", "room": "房间名", "self": { "display": "接收者", "publishing": false }, "peer": { "display": "发送者", "publishing": false }, "participants": [ "房间内全部成员" ] }其中control事件的param对应call字段、data对应data字段。该结构同时覆盖了self(自己)、peer(对方)与participants(全量成员),足以支撑一对一与多人的房间状态同步。
4.6 前端 SDK:srs.sig.js
演示页面的信令客户端封装在 srs.sig.js 中,提供三个核心 API:
connect(schema, host, room, display):建立ws(s)://host/sig/v1/rtc?room=..&display=..连接,返回 Promise;send(message):为消息生成 7 位十六进制tid,将{resolve, reject}存入_internals.msgs后发送;收到同tid响应即 resolve(srs.sig.js);onmessage(msg):回调,用于接收服务端主动广播的 notify 事件。
此外SrsRtcSignalingParse(srs.sig.js)负责从 URL 解析wss、wsh、wsp、host、room、display、autostart等参数,并把与信令无关的查询参数原样透传给媒体层(如 WHIP/WHEP 的 token)。
五、H5 演示与 WHIP/WHEP 媒体流打通
5.1 演示页面
www/demos/下提供两个核心演示(入口见 index.html):
one2one.html:一对一视频通话;room.html:多人视频房间(Video Room)。
两个页面都支持autostart=true自动启动,便于直接粘贴 URL 分享。
5.2 一对一流程拆解
以 one2one.html 为例,完整流程为:
sig.connect(...)建立信令连接;sig.send({action:'join', room, display})加入房间,返回成员列表;startPublish:将webrtc://地址转换为 WHIP URL(http(s)://host:1985|443/rtc/v1/whip/?app=<room>&stream=<display>)并调用SrsRtcWhipWhepAsync.publish()推流(见 srs.sdk.js);sig.send({action:'publish', room, display})广播「我在推流」;- 对房间内已存在的
publishing成员调用startPlay,使用 WHEP URL(/rtc/v1/whep/?app=<room>&stream=<display>)拉流播放(srs.sdk.js); - 之后新成员加入或推流时,通过 notify 事件(
publish、join、leave)触发对应的开始/停止播放。
媒体 URL 的端口规则(one2one.html 中convertToWhipUrl/convertToWhepUrl):HTTP 场景走 1985,HTTPS 场景走 443。而信令端口默认 1989;当演示页面通过 SRS 的 8080 HTTP 服务打开时,index.html会自动追加&wsp=1989让信令连接到 1989 端口(见 index.html)。
5.3 演示 URL 参数一览
| 参数 | 含义 | 默认值 |
|---|---|---|
host | SRS 媒体服务器地址 | 当前页面 hostname |
room | 加入的房间名 | 随机生成 |
display | 用户昵称 | 随机生成 |
wss | WebSocket 协议(ws/wss) | 依页面协议推断 |
wsh | WebSocket 服务器地址 | 当前页面 hostname |
wsp | WebSocket 端口 | 当前页面端口 |
autostart | 页面加载后自动开始 | 关闭 |
5.4 双人以上时的 FFmpeg 合流提示
当participants.length >= 2时,one2one 页面会展开「FFmpeg 合流转直播」面板(见 one2one.html),给出将两路 RTMP 流用-filter_complex overlay合成为一路并推流到rtmp://host/<room>/merge的完整命令,并支持一键将输出对接视频号推流地址(填写推流地址与密钥后点击「应用」)。这一功能演示了 WebRTC 通话与 RTMP 直播体系的互转能力。
六、HTTPS/WSS 生产化:httpx-static 反向代理
WebRTC 的getUserMedia在非 localhost 环境强制要求 HTTPS(浏览器安全策略,见 srs.sdk.js 中HttpsRequiredError的抛出逻辑),因此将信令升级为 WSS 是实际部署的必经之路。SRS 仓库提供了 httpx-static 组件,一个 Go 实现的「HTTP/HTTPS 静态服务器 + API 反向代理」,可一站完成证书终止与多后端代理。
6.1 构建并运行 httpx-static
cd ~/git/srs/trunk/3rdparty/httpx-static && make && ./objs/httpx-static -http 80 -https 443 -ssk server.key -ssc server.crt \ -proxy http://127.0.0.1:1989/sig -proxy http://127.0.0.1:1985/rtc \ -proxy http://127.0.0.1:8080/其中server.key/server.crt为自签或正式证书;若需要生成自签证书,httpx-static 的用法说明中给出了openssl genrsa+openssl req -x509的标准命令。
6.2 参数说明
从 httpx-static/main.go 可确认各参数语义:
| 参数(短/长) | 说明 |
|---|---|
-t/-http | HTTP 监听端口,0表示禁用 |
-s/-https | HTTPS 监听端口,如443 |
-r/-root | 静态文件根目录,默认./html(相对可执行文件路径) |
-k/-ssk | 自签或文件型证书的私钥文件 |
-c/-ssc | 自签或文件型证书的证书文件 |
-p/-proxy | 反向代理规则,可重复指定多个,按路径前缀匹配转发到后端 |
-d/-domains | HTTPS 允许的域名(配合 Let's Encrypt 时用于校验,留空允许所有) |
-l/-lets | 是否使用 Let's Encrypt CA(否则使用自签证书) |
代理规则的匹配是按路径前缀进行的,因此上面三条规则分别把/sig(signaling 信令)、/rtc(SRS 的 WHIP/WHEP API)、/(SRS 的 HTTP 服务与静态资源)分发到对应后端。README 中的另一条典型规则-proxy http://127.0.0.1:8888/api/webrtc也印证了这一「路径前缀 → 后端」的模型。此外-proxy还支持?trimPrefix=/x、?addPrefix=/y、?keepUpsreamServer=true、?modifyRequestHost=false等高级修饰参数(见 main.go)。
6.3 通过 HTTPS/IP 访问演示
部署完成后,以下地址均可访问演示:
- http://localhost/demos/
- https://localhost/demos/
- https://192.168.3.6/demos/
此时浏览器页面为 HTTPS(wss=自动推断为wss),媒体 API 走 443(443 上/rtc被代理到 SRS 1985),信令走wss://.../sig(被代理到 signaling 1989),三者通过同一域名 + 端口即可统一访问,规避了混合内容与getUserMedia安全限制。
七、从 demo 走向自研:源码结构给出的启示
需要明确的是,signaling 官方定位为demo(README 第一句即声明 "A demo WebRTC signaling"),从源码结构可以推断其面向演示与教学的设计取舍:
- 单进程内存态:房间数据存于进程内
sync.Map,无持久化、无水平扩展能力;多实例部署时需要自行引入共享状态(如 Redis)与一致性方案; - 无鉴权与配额:
join/control不校验身份与权限,任何人可加入任意房间;生产环境需在信令层或上层网关补充 Token 校验; - 媒体面完全交由 SRS:signaling 本身不处理 SDP/ICE,仅做事件转发,媒体质量、NAT 穿透(STUN/TURN)能力取决于 SRS 的 RTC 配置;
- 协议简洁可扩展:
join/publish/control+notify的四事件模型足够通用,新增动作(如踢人、举手、录制开关)只需在 main.go 的消息分发处仿照现有分支扩展,并在 srs.sig.js 的send/onmessage上对接即可。
如果需要更完整的多方音视频能力,SRS 仓库的trunk/research与srs-bench等目录也提供了压力测试与协议验证的参考实现,可作为扩展阅读。
总结
本文完整走通了 SRS WebRTC signaling 的三种使用形态:Docker 一键体验(两个容器 + 一个 URL 即可看到一对一通话)、源码构建(make产出单二进制,-listen/-root两个参数即可运行)、HTTPS/WSS 生产化(httpx-static 按路径前缀反代/sig、/rtc、/三路后端)。同时结合 main.go 与 srs.sig.js 剖析了信令协议:/sig/v1/rtcWebSocket 端点、tid事务帧、join/publish/control三种动作、Room/Participant内存模型与join/publish/leave/control四类广播事件。无论你是要快速验证 SRS 的 WebRTC 能力,还是要在此 demo 基础上构建自己的房间信令服务,本文提供的部署命令、协议字段与源码定位都可以直接作为起点。
进一步阅读:
- 信令服务完整源码:trunk/3rdparty/signaling/main.go
- 前端信令 SDK:trunk/3rdparty/signaling/www/demos/js/srs.sig.js
- WHIP/WHEP 媒体 SDK:trunk/3rdparty/signaling/www/demos/js/srs.sdk.js
- 演示页面源码:trunk/3rdparty/signaling/www/demos/one2one.html
- HTTPS/WSS 反代组件:trunk/3rdparty/httpx-static/main.go
- SRS RTC 配置示例:trunk/conf/rtc.conf
【免费下载链接】srsSRS is a simple, high-performance, AI-driven real-time media server supporting RTMP, WebRTC, HLS, HTTP-FLV, HTTP-TS, SRT, MPEG-DASH, and GB28181, with codec support for H.264, H.265, AV1, VP9, AAC, Opus, and G.711.项目地址: https://gitcode.com/GitHub_Trending/sr/srs
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考