neko 虚拟浏览器 WebRTC 网络配置完全指南:ICE、端口规划与带宽估计器调优
【免费下载链接】nekoA self hosted virtual browser that runs in docker and uses WebRTC.项目地址: https://gitcode.com/GitHub_Trending/ne/neko
本篇技术指南聚焦于 neko(基于 Docker 运行、通过 WebRTC 传输音视频的虚拟浏览器)的 WebRTC 与网络层配置。你将掌握 ICE 连接建立机制(Trickle/Lite/STUN/TURN)、服务器端口规划(临时 UDP 端口池与 UDP/TCP 多路复用)、NAT 场景下的公网 IP 处理,以及实验性带宽估计器的调优参数,从而在真实部署中实现低延迟、可穿透、稳定的点对点音视频流。文中所有配置项均可在 docker-compose.yaml 与 server/internal/config/webrtc.go 中找到实现依据。
WebRTC 在 neko 中的角色
neko 使用 WebRTC 在客户端与服务器之间建立点对点(peer-to-peer)连接,连接建立基于 Go 生态的 Pion 库。这条连接被用来双向传输音视频流与数据(包括鼠标键盘输入、剪贴板、光标图像/位置等控制信号),是 neko 全部交互能力的传输底座。
从源码结构看,WebRTC 相关实现集中在 server/internal/webrtc/ 目录:
- manager.go 负责创建 PeerConnection、ICE 多路复用监听器、音视频 Track、DataChannel 与带宽估计器;
- peer.go 封装单个对端的 SDP 协商、候选者注入、暂停/切换视频流与估计器读取循环;
- 类型定义位于 server/pkg/types/webrtc.go,配置解析位于 server/internal/config/webrtc.go。
ICE 连接建立机制
ICE(Interactive Connectivity Establishment,交互式连接建立)是一套用于在两台对端(如客户端与服务器)之间寻找最佳连通路径的协议。它帮助双方发现各自的公网 IP 与端口,从而建立直接连接;携带这些信息的 ICE candidate(候选者)会通过信令服务器(Signaling Server)交换,以推进连接过程。
ICE Trickle:候选者边发现边发送
ICE Trickle 允许 ICE candidate 在发现后立即发送,而不是等全部候选者收集完毕再统一发送。这样服务器一旦拿到少量候选者即可开始连接客户端,无需等待全部收集完成,因此能显著缩短建连时间。
| 配置项 | 默认值 | 类型 | 说明 |
|---|---|---|---|
webrtc.icetrickle | true | boolean | 是否使用 Trickle ICE 异步发送候选者 |
环境变量对应NEKO_WEBRTC_ICETRICKLE。从 manager.go 可以看到:启用时,connection.OnICECandidate回调会将每个本地候选者通过SIGNAL_CANDIDATE事件即时推送给客户端;而在 peer.go 中,若关闭 Trickle,则setLocalDescription会通过GatheringCompletePromise阻塞等待 ICE 收集全部完成后才返回 SDP。
ICE Lite:面向公网 IP 的精简模式
ICE Lite 是 ICE 协议的最小实现,适用于直接运行在公网 IP 上的服务器。它默认关闭(false),以便开箱即用地支持更复杂的 ICE 配置。
:::info 当配置了 ICE Servers(STUN/TURN)时,必须关闭 ICE Lite。 :::
环境变量为NEKO_WEBRTC_ICELITE。在 manager.go 中,启用ICELite时服务器不会把后端 ICE Servers 写入 Pion 的webrtc.Configuration;同时 config/webrtc.go 会在「ICE Lite + 后端 ICE 服务器同时存在」时打印警告并忽略后端服务器。此外 manager.go 通过settings.SetLite(...)将 Pion Agent 设置为 Lite 模式。
ICE Servers:STUN 与 TURN
ICE 服务器用于帮助建立客户端与服务器之间的连接,分为两类:
- STUN(Session Traversal Utilities for NAT):用于发现客户端的公网 IP,从而建立客户端与服务器之间的直接连接;
- TURN(Traversal Using Relays around NAT):当无法建立直接连接时,作为中继在客户端与服务器之间转发数据。
单个 ICE 服务器配置由以下字段组成:
| 字段 | 说明 | 类型 |
|---|---|---|
urls | ICE 服务器 URL 列表;若同一服务器在多个 URL 上提供相同凭据,可在此一并列出 | string[] |
username | 服务器要求认证时使用的用户名 | string |
credential | 服务器要求认证时使用的凭据 | string |
对应的 Go 类型定义见 server/pkg/types/webrtc.go。neko 在未配置任何 ICE 服务器时,会自动使用内置默认 STUN 服务器stun:stun.l.google.com:19302(常量defStunSrv,见 config/webrtc.go)。
多 ICE 服务器配置示例
YAML 形式(适用于配置文件):
- urls: "turn:<MY-COTURN-SERVER>:3478" username: "neko" credential: "neko" - urls: "stun:stun.l.google.com:19302"JSON 形式(可配合环境变量使用):
[ { "urls": "turn:<MY-COTURN-SERVER>:3478", "username": "neko", "credential": "neko" }, { "urls": "stun:stun.l.google.com:19302" } ]:::tip 可以在docker-compose.yaml中把 ICE 服务器以JSON 字符串形式写入NEKO_WEBRTC_ICESERVERS_FRONTEND与NEKO_WEBRTC_ICESERVERS_BACKEND环境变量:
NEKO_WEBRTC_ICESERVERS_FRONTEND: | [{ "urls": [ "turn:<MY-COTURN-SERVER>:3478" ], "username": "neko", "credential": "neko" },{ "urls": [ "stun:stun.nextcloud.com:3478" ] }]:::
这种「JSON 字符串自动解码」的能力来自配置层的utils.JsonStringAutoDecode解码钩子(见 config/webrtc.go),也就是说同一配置既可以是 YAML 数组,也可以是通过环境变量传入的 JSON 字符串。
Frontend 与 Backend 分组
ICE 服务器被划分为两组:
| 配置项 | 默认值 | 类型 | 说明 |
|---|---|---|---|
webrtc.iceservers.frontend | [] | array | 发送给客户端的 ICE 服务器,用于建立客户端与服务器之间的连接 |
webrtc.iceservers.backend | [] | array | 服务器端收集 ICE candidate 时使用的 ICE 服务器,可能包含私网 IP 等不应下发给客户端的信息 |
对应环境变量为NEKO_WEBRTC_ICESERVERS_FRONTEND与NEKO_WEBRTC_ICESERVERS_BACKEND。二者在服务端的用途不同:前端服务器通过 manager.go 的ICEServers()暴露给客户端;后端服务器则在 manager.go 中被写入 Pion 配置用于收集候选者。若两者都未配置,则统一回落到全局webrtc.iceservers(v2 兼容项)或默认 STUN 服务器,并同时填充 frontend 与 backend(config/webrtc.go)。
Coturn 服务器部署示例
docker-compose 中部署 Coturn 的完整示例
services: coturn: image: 'coturn/coturn:latest' network_mode: "host" command: | -n --realm=localhost --fingerprint --listening-ip=0.0.0.0 --external-ip=<MY-COTURN-SERVER> --listening-port=3478 --min-port=49160 --max-port=49200 --log-file=stdout --user=neko:neko --lt-cred-mech将<MY-COTURN-SERVER>替换为你的局域网或公网 IP,并在防火墙放行49160-49200/udp与3478/tcp。--user用于指定 TURN 服务器的用户名与密码,--lt-cred-mech用于启用长期凭证(long-term credentials)认证机制。注意--external-ip必须与docker-compose.yaml中 ICE 服务器配置里的 TURN 地址保持一致,否则中继候选者无法被客户端正确使用。
网络规划:端口、防火墙与反向代理边界
WebRTC 是点对点协议,要求客户端与服务器之间能建立直接连接,可通过以下两种方式达成:
- 为服务器使用公网 IP(若部署在私网,则至少保证客户端可达);
- 在无法直连时,使用 TURN 服务器 中继数据。
所有已配置端口会连同服务器 IP 一起写入 ICE candidate 下发给客户端,因此必须确保这些端口在服务器防火墙上处于开放状态、未被重映射到其他端口,且从客户端可达。
:::danger 牢记 WebRTC 不使用 HTTP 协议,因此无法通过 nginx 或其他反向代理转发 WebRTC 流量。如果你的服务器只暴露了443端口,则必须额外暴露 WebRTC 端口,或使用 TURN 服务器。 :::
neko 支持两种连接方式:
- 临时 UDP 端口池(Ephemeral UDP port range):服务器用于与客户端建连的一段 UDP 端口范围。每次建立新连接都会使用该范围内的一个新端口,该范围必须在服务器防火墙中放行;
- UDP/TCP 多路复用(Multiplexing):服务器用单个端口承载多个连接,同样需要在防火墙中放行。
临时 UDP 端口池(EPR)
| 配置项 | 默认值 | 类型 | 说明 |
|---|---|---|---|
webrtc.epr | 未设置(默认59000-59100) | string | 限制 ICE UDP 连接可分配的临时端口池 |
环境变量为NEKO_WEBRTC_EPR,格式为min-max,例如59000-59100。该范围包含 101 个端口,需在防火墙全部放行。端口数量决定可承载的并发连接规模,可按预期并发数酌情增减。
未配置epr、tcpmux、udpmux中任何一个时,config/webrtc.go 会启用默认范围59000-59100并打印告警日志。解析逻辑(config/webrtc.go)会校验端口格式与大小关系:min大于max会直接Panic,非法端口会解析失败。
:::tip 务必注意 在docker-compose.yaml中指定临时 UDP 端口范围时,端口映射必须同样使用 UDP 协议:
environment: NEKO_WEBRTC_EPR: "59000-59100" ports: - "59000-59100:59000-59100/udp"必须将相同端口原样暴露到宿主机,不要重映射。例如49000-49100:59000-59100/udp是错误写法,59000-59100:59000-59100/udp才是正确写法,否则客户端收到的候选者端口与真实监听端口不一致,导致连接失败。 :::
UDP/TCP 多路复用(Mux)
| 配置项 | 默认值 | 类型 | 说明 |
|---|---|---|---|
webrtc.udpmux | 未设置 | int | 所有对端共用的单个 UDP mux 端口,设置后取代 EPR |
webrtc.tcpmux | 未设置 | int | 所有对端共用的单个 TCP mux 端口 |
环境变量为NEKO_WEBRTC_UDPMUX与NEKO_WEBRTC_TCPMUX。服务器仅用59000一个端口同时承载 UDP 与 TCP 连接。可以只启用其中一个协议,也可以两个都启用。UDP 通常延迟更低,但部分网络会屏蔽 UDP,因此保留 TCP 作为回退是稳妥做法。
对应实现中,manager.go 在Start()阶段分别创建 TCP listener(ice.NewTCPMuxDefault,读写缓冲分别为 50 包与 4MB)与 UDP listener(ice.NewMultiUDPMuxFromPort);随后在 manager.go 按启用的 mux 组合设置网络类型(UDP4/UDP6/TCP4/TCP6)。注意当UDPMux存在时,EPR 端口池将不再生效——两者是互斥的。
:::tip 务必注意 在docker-compose.yaml中指定 mux 端口时,必须正确区分协议:
environment: NEKO_WEBRTC_UDPMUX: "59000" NEKO_WEBRTC_TCPMUX: "59000" ports: - "59000:59000/udp" - "59000:59000/tcp"同样地,必须将端口原样暴露,不做重映射,例如使用59000:59000/udp而不是49000:59000/udp。 :::
服务器 IP 地址
服务器 IP 会写入 ICE candidate 下发给客户端,供其建立连接。默认情况下,服务器会自动解析自身的公网 IP。如果服务器位于 NAT 之后、希望指定其他 IP,或仅在局域网内使用 neko,则可以手动指定服务器 IP。
NAT 1-to-1
| 配置项 | 默认值 | 类型 | 说明 |
|---|---|---|---|
webrtc.nat1to1 | 空 | strings | 1:1 (D)NAT 的外部 IP 列表,以及该外部 IP 对应的候选者类型 |
环境变量为NEKO_WEBRTC_NAT1TO1,示例值10.10.0.5。当前只能指定一个地址。因此如果你希望同时从内网与公网访问实例,路由器必须支持 NAT loopback(hairpinning,NAT 回环)。
从源码看,该列表通过settings.SetNAT1To1IPs(manager.config.NAT1To1IPs, webrtc.ICECandidateTypeHost)写入 Pion SettingEngine(manager.go),即用指定的外部 IP 替换 host 类型候选者。
IP 获取 URL
| 配置项 | 默认值 | 类型 | 说明 |
|---|---|---|---|
webrtc.ip_retrieval_url | https://checkip.amazonaws.com | string | 用于获取外部 IP 的 URL 地址 |
环境变量为NEKO_WEBRTC_IP_RETRIEVAL_URL。当未指定nat1to1时,服务器会向该 URL 发送 HTTP GET 请求以获取自身公网 IP(config/webrtc.go)。获取成功后将结果追加进NAT1To1IPs;失败则仅打印警告并继续(服务不会因此退出)。如果所在网络无法访问该默认 URL(例如被墙或离线环境),请替换为可用的公网 IP 查询服务,或直接配置NEKO_WEBRTC_NAT1TO1。
带宽估计器(Bandwidth Estimator)
:::danger 带宽估计器是实验性功能,可能无法按预期工作。 :::
带宽估计器允许服务器估算客户端与服务器之间的可用带宽,并根据可用带宽在不同视频质量(码率档位)之间自动切换。默认关闭。
全部参数一览
| 配置项 | 默认值 | 类型 | 说明 |
|---|---|---|---|
webrtc.estimator.enabled | false | boolean | 启用带宽估计器 |
webrtc.estimator.passive | false | boolean | 被动模式:只做估算、不切换视频管线 |
webrtc.estimator.debug | false | boolean | 启用带宽估计器的调试日志 |
webrtc.estimator.initial_bitrate | 1000000 | int | 估计器的初始码率(bps) |
webrtc.estimator.read_interval | 2s | duration | 读取并处理带宽估算报告的频率 |
webrtc.estimator.stable_duration | 12s | duration | 连接稳定(上升或中性趋势)持续多久后升级码率 |
webrtc.estimator.unstable_duration | 6s | duration | 连接不稳定(下行趋势)持续多久后降级码率 |
webrtc.estimator.stalled_duration | 24s | duration | 带宽估算停滞持续多久后降级 |
webrtc.estimator.downgrade_backoff | 10s | duration | 上一次降级后再次降级前的最短等待时间 |
webrtc.estimator.upgrade_backoff | 5s | duration | 上一次升级后再次升级前的最短等待时间 |
webrtc.estimator.diff_threshold | 0.15 | float | 估算码率与当前流码率之间的差异达到多大才触发升级/降级 |
对应环境变量为NEKO_WEBRTC_ESTIMATOR_ENABLED、NEKO_WEBRTC_ESTIMATOR_PASSIVE、NEKO_WEBRTC_ESTIMATOR_DEBUG、NEKO_WEBRTC_ESTIMATOR_INITIAL_BITRATE等(规则同 Viper 的webrtc.estimator.*键)。这些参数的默认值与类型定义均可在 server/internal/config/webrtc.go 的WebRTCEstimator结构体及 webpage/docs/configuration/help.json 中核对。
工作原理:从 RTCP 反馈到码率切换
估计器在连接层通过 Pion 的GCC(Google Congestion Control)发送端带宽估计实现。在 manager.go 中,启用后会将一个拥塞控制器(cc.NewInterceptor+gcc.NewSendSideBWE)注册进 interceptor registry,并为引擎配置TWCC(Transport- Wide Congestion Control)RTP 头扩展,用于收集传输层拥塞反馈;同时默认使用gcc.NewNoOpPacer(不实际限速发包,只做估算)。
随后,每个对端在 peer.go 的estimatorReader循环中以read_interval(默认 2 秒)为周期执行决策:
- 读取
estimator.GetTargetBitrate()得到估计目标码率; - 通过 utils/trenddetector.go 的
TrendDetector判定码率趋势方向(NEUTRAL/UPWARD/DOWNWARD,内部采用Kendall's Tau秩相关算法,并配置RequiredSamples=8、DownwardTrendThreshold=-0.5); - 计算目标码率与当前流码率的比值
diff; - 降级:当趋势向下或长期停滞(
stalled_duration)时,若差异未超阈值且超过降级退避时间与不稳定持续时间,则通过SetVideo请求选择更低码率的流(StreamSelectorTypeLower); - 升级:当趋势中性/向上、估算码率超出当前流码率
diff_threshold以上、且稳定持续满stable_duration并超过升级退避时间时,请求选择更高码率的流(StreamSelectorTypeHigher)。
流档位选择由 server/internal/capture/streamselector.go 完成,它支持按 ID 或按码率选取 lower/higher/nearest 流;若已处于最低/最高档,GetStream返回ErrWebRTCStreamNotFound,估计器会记录「already on the lowest/highest stream」日志。
值得注意的是,估计器只在videoAuto开启、视频未禁用、未暂停、且非passive模式时才做切换决策(peer.go)。客户端侧是否开启「自动」由视频信号(PeerVideo.Auto)控制,passive模式则让估计器只输出估算结果而不动视频管线,便于先观察估算质量再决定是否启用。配合debug: true可以查看每个周期的diff、target_bitrate、stream_bitrate与direction日志,是调优stable_duration/unstable_duration/diff_threshold等参数的重要依据。
与多档位视频流的配合
带宽估计器的价值建立在多个不同码率的视频管线之上。neko 通过capture.video.pipelines(多管线映射)与capture.video.ids(有序 ID 列表)配置多档位流,见 server/internal/config/capture.go 与 webpage/docs/configuration/capture.md。只有video.ids中列出的流才会参与估计器的升降级选择;若只配置单档main流,估计器即使启用也无法产生实际切换效果。
快速上手检查清单
综合以上配置,一次典型的公网部署至少需要核对以下项:
- 连接方式:三选一——配置
NEKO_WEBRTC_EPR(临时 UDP 端口池)、或NEKO_WEBRTC_UDPMUX/NEKO_WEBRTC_TCPMUX(单端口多路复用,建议同时启用 TCP 作为 UDP 被屏蔽时的回退); - 端口放行:将上述端口以相同端口号、正确协议原样映射到宿主机并在防火墙放行,严禁端口重映射;WebRTC 流量无法走 HTTP 反向代理;
- NAT 场景:公网直连可开启
NEKO_WEBRTC_ICELITE;NAT 之后配置NEKO_WEBRTC_NAT1TO1(公网 IP),或确认路由器支持 NAT hairpinning;也可保留NEKO_WEBRTC_IP_RETRIEVAL_URL自动获取公网 IP; - 跨网络复杂拓扑:配置 frontend/backend 两组 ICE 服务器,必要时部署 Coturn 作为 TURN 中继(此时必须关闭 ICE Lite);
- 可选优化:启用带宽估计器并在
capture.video.pipelines中配置多档码率流,实现按网络状况自动升降级。
官方仓库的 docker-compose.yaml 给出了一个最小可用示例(NEKO_WEBRTC_EPR: 52000-52100配合NEKO_WEBRTC_ICELITE: 1与对应 UDP 端口映射),可作为部署起点;配置项的完整清单与默认值可在 webpage/docs/configuration/help.json 中一次性查阅。
【免费下载链接】nekoA self hosted virtual browser that runs in docker and uses WebRTC.项目地址: https://gitcode.com/GitHub_Trending/ne/neko
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考