UE5 Window 部署文档

CS2 Pixel Streaming 公网部署文档

适用场景

  • UE5 Pixel Streaming 项目(CS2.exe)
  • 服务器在路由器/NAT 后面,只能开 TCP 端口,无法开 UDP 端口
  • 需要让公网用户通过浏览器访问像素流

如果你的网络环境能随便开 UDP 端口,这套方案是杀鸡用牛刀,直接用 PixelStreaming 默认配置即可。本文档专门解决"UDP 不通"这个痛点。


整体架构

复制代码
外网浏览器
    │ TCP 8007 (HTTP + WebSocket)
    ▼
nginx (8007) ──/cs2/───▶ Cirrus HTTP  (127.0.0.1:8018)
              └/cs2ws/─▶ Cirrus WS    (127.0.0.1:8018)
    │
    │ TCP 3478 (TURN over TCP) ──直连服务器──▶ coturn (3478)
    │                                            │
    │                                            │ 内网 UDP
    │                                            ▼
    │                                         CS2.exe (8890)
    │                                            ▲
    └──────信令(WS)──────▶ Cirrus ──────信令──┘

关键设计 :WebRTC 媒体流走 TURN-over-TCP,所有外网流量只用 TCP(8007 + 3478)。

coturn 把 relay 地址通告为局域网 IP (192.168.0.184),CS2.exe 通过内网 UDP 与 coturn 通信,完全不需要把 UDP 端口暴露到公网


网络端口规划

端口 协议 用途 暴露到公网
8007 TCP nginx 反向代理入口
3478 TCP TURN 服务端口
8018 TCP Cirrus 信令服务器(HTTP+WS) 否(只本机)
8890 TCP Cirrus ↔ CS2.exe 信令
49160-49200 UDP TURN relay(内网用)

外网只需开 TCP 8007 和 TCP 3478 两个端口


前置准备(换服务器时全部要重做)

1. 确认服务器信息

记录以下三个值,后续配置全部围绕它们:

变量 示例 怎么查
公网 IP 公网IP 浏览器搜"IP" 或 curl ifconfig.me
内网 IP 192.168.0.184 PowerShell ipconfig,看主网卡的 IPv4 地址
内网网关 192.168.0.1 同上,看"默认网关"

2. 防火墙开放 TCP 端口

PowerShell 管理员运行:

powershell 复制代码
New-NetFirewallRule -DisplayName "nginx-8007"  -Direction Inbound -Protocol TCP -LocalPort 8007 -Action Allow
New-NetFirewallRule -DisplayName "TURN-TCP-3478" -Direction Inbound -Protocol TCP -LocalPort 3478 -Action Allow

3. 路由器端口转发

在路由器后台(本文档以 H3C GR3200 为例)的「虚拟服务器」(或「端口映射」)里加两条:

名称 协议 外部端口 内部 IP 内部端口
nginx-8007 TCP 8007 192.168.0.184 8007
TURN-3478 TCP 3478 192.168.0.184 3478

注意:内部 IP 必须和当时记录的服务器内网 IP 一致。建议在路由器 DHCP 设置里把服务器 MAC 绑定固定 IP,避免重启后变化。

4. 云安全组(仅云服务器需要)

如果是阿里云/腾讯云/华为云的 ECS,还要在安全组入方向放行:

  • TCP 8007
  • TCP 3478

授权对象 0.0.0.0/0

5. 端口验证

任意外网设备(手机 4G 关 WiFi 是最快的方式)访问端口检测网站,验证 8007 和 3478 都显示「开启」:

或在 PowerShell:

powershell 复制代码
Test-NetConnection 你的公网IP -Port 8007
Test-NetConnection 你的公网IP -Port 3478

服务器侧配置(共 4 个文件)

工程目录: C:\Users\Administrator\Desktop\CS2Build\

PixelStreaming 信令目录: Windows\CS2\Samples\PixelStreaming\WebServers\SignallingWebServer\

文件 1: turnserver.conf

路径: 信令目录\turnserver.conf

conf 复制代码
listening-port=3478
listening-ip=0.0.0.0

relay-ip=192.168.0.184
external-ip=192.168.0.184

min-port=49160
max-port=49200

realm=PixelStreaming
lt-cred-mech
user=PixelStreamingUser:AnotherTURNintheroad

fingerprint
no-multicast-peers
no-tls
no-dtls
no-cli

verbose
log-file=stdout

denied-peer-ip=0.0.0.0-0.255.255.255
denied-peer-ip=10.0.0.0-10.255.255.255
denied-peer-ip=100.64.0.0-100.127.255.255
denied-peer-ip=169.254.0.0-169.254.255.255
denied-peer-ip=172.16.0.0-172.31.255.255
denied-peer-ip=192.0.0.0-192.0.0.255
denied-peer-ip=192.0.2.0-192.0.2.255
denied-peer-ip=192.88.99.0-192.88.99.255
denied-peer-ip=198.18.0.0-198.19.255.255
denied-peer-ip=198.51.100.0-198.51.100.255
denied-peer-ip=203.0.113.0-203.0.113.255
denied-peer-ip=240.0.0.0-255.255.255.255

allowed-peer-ip=127.0.0.1
allowed-peer-ip=192.168.0.184

换服务器时要改:

  • relay-ip → 新服务器的内网 IP
  • external-ip → 新服务器的内网 IP(故意写内网,不要写公网!这是绕开 UDP 端口的关键)
  • allowed-peer-ip 第二行 → 新服务器的内网 IP
  • 账号密码可以改,但要和文件 2 里的对应一致

文件 2: config.json

路径: 信令目录\config.json

json 复制代码
{
	"UseFrontend": false,
	"UseMatchmaker": false,
	"UseHTTPS": false,
	"LogToFile": true,
	"LogVerbose": true,
	"HomepageFile": "player.html",
	"AdditionalRoutes": {},
	"EnableWebserver": true,
	"MatchmakerAddress": "",
	"MatchmakerPort": 9999,
	"PublicIp": "公网IP",
	"HttpPort": 8018,
	"HttpsPort": 443,
	"StreamerPort": 8890,
	"SFUPort": 8891,
	"MaxPlayerCount": -1,
	"peerConnectionOptions": "{\"iceServers\":[{\"urls\":[\"turn:公网IP:3478?transport=tcp\"],\"username\":\"PixelStreamingUser\",\"credential\":\"AnotherTURNintheroad\"}],\"iceTransportPolicy\":\"relay\"}"
}

换服务器时要改:

  • PublicIp → 新服务器的公网 IP
  • peerConnectionOptions 里的 turn:公网IP:3478 → 新服务器的公网 IP(浏览器从外网连 TURN 用的)
  • 账号密码(username / credential) → 与 turnserver.conf 里的一致

关键参数解释:

  • iceTransportPolicy: "relay" 强制只走 TURN 中继,不尝试 P2P 直连(P2P 在我们这种网络环境下永远会失败,跳过省时间)
  • ?transport=tcp 强制走 TURN-TCP,不走 UDP

文件 3: start_cs2_all.bat

路径: Windows\start_cs2_all.bat

bat 复制代码
@echo off
chcp 65001 >nul
title CS2 Pixel Streaming

echo [1/4] Starting coturn TURN server (TCP 3478)...
start "CS2_TURN" cmd /k "cd /d C:\Users\Administrator\Desktop\CS2Build\Windows\CS2\Samples\PixelStreaming\WebServers\SignallingWebServer && platform_scripts\cmd\coturn\turnserver.exe -c turnserver.conf"

timeout /t 3 /nobreak >nul

echo [2/4] Starting Pixel Streaming SignallingWebServer...
start "CS2_Signalling" cmd /k "cd /d C:\Users\Administrator\Desktop\CS2Build\Windows\CS2\Samples\PixelStreaming\WebServers\SignallingWebServer && platform_scripts\cmd\node\node.exe cirrus.js"

timeout /t 5 /nobreak >nul

echo [3/4] Starting CS2.exe...
start "CS2_UE" "C:\Users\Administrator\Desktop\CS2Build\Windows\CS2.exe" -AudioMixer -PixelStreamingIP=127.0.0.1 -PixelStreamingPort=8890 -log -RenderOffScreen

timeout /t 2 /nobreak >nul

echo [4/4] Done.
echo Local URL:  http://127.0.0.1:8018/player.html
echo Remote URL: http://公网IP:8007/cs2/player.html
pause

注意:

  • 启动顺序必须是 TURN → Cirrus → CS2.exe,前者没起来后者会连不上
  • Cirrus 启动不再传 --PublicIp=127.0.0.1 这个参数,让它自己读 config.json
  • -RenderOffScreen 让 UE 不开窗口,服务器无显示器也能跑

换服务器时要改:

  • 所有 C:\Users\Administrator\Desktop\CS2Build\ 路径(如果工程放在别的目录)
  • 最后两行 echo 的 URL

文件 4: nginx.conf(片段)

路径: E:\nginx-1.24.0\conf\nginx.conf

http {} 块里,确保有以下 map(用于 WebSocket Upgrade):

nginx 复制代码
map $http_upgrade $connection_upgrade {
    default upgrade;
    '' close;
}

server { listen 8007; ... } 里加两个 location:

nginx 复制代码
location /cs2/ {
    proxy_pass http://127.0.0.1:8018/;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_buffering off;
    proxy_cache off;
    proxy_read_timeout 86400;
    proxy_send_timeout 86400;
}

location /cs2ws/ {
    proxy_pass http://127.0.0.1:8018/;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection $connection_upgrade;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_read_timeout 86400;
    proxy_send_timeout 86400;
    proxy_buffering off;
}

/cs2/ 给 HTML/JS 等静态资源走,/cs2ws/ 专门给 WebSocket 信令走(player 端通过 ?ss= 指定)。

修改后:

cmd 复制代码
cd /d E:\nginx-1.24.0
nginx.exe -s reload

启动 / 重启流程

完整重启

  1. 关掉旧进程:

    cmd 复制代码
    taskkill /f /im CS2.exe
    taskkill /f /im node.exe
    taskkill /f /im turnserver.exe
  2. 双击运行 Windows\start_cs2_all.bat,等 4 个步骤跑完。

  3. 会出现 3 个黑窗口:

    • CS2_TURN --- coturn,正常应输出 IPv4. TCP listener opened on : 0.0.0.0:3478
    • CS2_Signalling --- cirrus.js,正常应输出 WebSocket listening for Streamer connections on :8890Http listening on *: 8018
    • CS2_UE --- CS2 引擎日志,等 30 秒左右出现 PixelStreaming 相关日志即可
  4. nginx 应该已经在跑,如果没跑:

    cmd 复制代码
    cd /d E:\nginx-1.24.0
    start nginx.exe

访问地址

  • 服务器本机测试: http://127.0.0.1:8018/player.html
  • 外网用户访问: http://公网IP:8007/cs2/player.html

故障排查

浏览器一直黑屏

打开 F12 控制台:

现象 原因 解决
WebSocket 报 1006 nginx /cs2ws/ 没配对,或 cirrus 没起来 看 cirrus 窗口日志,检查 nginx 配置
ICE candidate 没出现 relay 类型 TURN 没生效,iceTransportPolicy 没读到 检查 config.json 的 peerConnectionOptions JSON 是否完整、转义对了
Set video source 出现但画面不动 UE 端没产帧 看 CS2_UE 窗口日志,常见是 GPU/编码器没初始化
ICE 候选出现 公网IP:50xxx TURN 工作了 应该能看到画面

TURN 窗口报 bind: address already in use

3478 被占用。查谁占着:

powershell 复制代码
netstat -ano | findstr ":3478"

kill 掉占用的进程,或者改 turnserver.conf 的端口(同时改 config.json 和路由器转发)。

TURN 窗口启动了但浏览器连不上

最可能是路由器虚拟服务器规则没生效或失效。重新走"前置准备"的端口验证步骤。

远程访问黑屏但本机 (127.0.0.1:8018) 正常

99% 是 TURN 链路没通。手动外网测 3478:

powershell 复制代码
Test-NetConnection 公网IP -Port 3478

返回 True 才说明路由器+防火墙整条链路打通。

路由器换了 / 重启后服务器内网 IP 变了

turnserver.confrelay-ip / external-ip / allowed-peer-ip 都要改成新的内网 IP,然后重启 turnserver。建议在路由器里把服务器 MAC 绑定 IP,一劳永逸。


换服务器时的修改清单

按这个清单逐项过一遍,基本不会漏:

  • 记录新服务器公网 IP、内网 IP
  • Windows 防火墙开 TCP 8007、TCP 3478
  • 路由器虚拟服务器加 TCP 8007、TCP 3478 转发到新内网 IP
  • 云安全组(如有)放行 TCP 8007、TCP 3478
  • 外网端口检测,8007 和 3478 显示"开启"
  • 修改 turnserver.conf(3 处内网 IP)
  • 修改 config.json(2 处公网 IP)
  • 修改 start_cs2_all.bat(末尾 URL,若工程路径变了还要改路径)
  • 修改 nginx.conf(若有需要)并 reload
  • 重启 nginx,运行 start_cs2_all.bat
  • 外网手机/电脑访问 http://公网IP:8007/cs2/player.html,看到画面

附:为什么这套架构能跑通(原理)

外网用户只能通过 TCP 8007 和 TCP 3478 访问服务器,而 WebRTC 媒体流默认走 UDP,无法穿透。本架构的核心 trick:

  1. 强制 TURN 中继 : 在 peerConnectionOptions 里设 iceTransportPolicy: "relay",浏览器和 streamer 都不再尝试 P2P 直连,统一走 TURN 中继。

  2. TURN 在外网用 TCP : TURN URL 加 ?transport=tcp,浏览器和服务器之间的 TURN 控制+数据通道全在 TCP 3478 这一条连接上完成,不需要单独的 UDP 端口。

  3. TURN 在内网用 UDP : coturn 的 relay 地址配成内网 IP (192.168.0.184),CS2.exe 通过内网 UDP 与 coturn 通信。这部分流量不出网卡,路由器看不见,自然不需要开 UDP 端口。

  4. TURN 同时是浏览器和 streamer 的 TURN 客户端: cirrus 把同一份 ICE 配置发给两边,两边都 allocate 到 TURN,所有数据通过 TURN 内部转发,绕开所有 NAT 问题。

性能代价:

  • TURN-TCP 比直连 UDP 慢,延迟会高一些(通常 +20~80ms)
  • TCP 的拥塞控制对实时视频不友好,丢包重传会导致卡顿放大
  • 多个用户同时观看会消耗服务器带宽(每路视频都要 TURN 转一遍)

如果以后服务器能开 UDP 端口,把 peerConnectionOptions 里的 iceTransportPolicy 删掉、?transport=tcp 也删掉,自动会优先走 UDP,体验会好很多。


文件清单速查

复制代码
C:\Users\Administrator\Desktop\CS2Build\
├── 部署文档.md                            ← 本文档
├── Windows\
│   ├── start_cs2_all.bat                  ← 一键启动脚本
│   ├── CS2.exe                            ← UE5 引擎
│   └── CS2\Samples\PixelStreaming\WebServers\SignallingWebServer\
│       ├── cirrus.js                      ← 信令服务器(不要改)
│       ├── config.json                    ← 信令配置(改公网 IP + TURN)
│       ├── turnserver.conf                ← TURN 配置(改内网 IP)
│       ├── start_turnserver.bat           ← 单独启动 TURN(调试用)
│       └── platform_scripts\cmd\coturn\
│           └── turnserver.exe             ← coturn 程序

E:\nginx-1.24.0\
├── nginx.exe
└── conf\nginx.conf                        ← 加 /cs2/ 和 /cs2ws/ location

start_cs2_all.bat

bash 复制代码
@echo off
chcp 65001 >nul
title CS2 Pixel Streaming

echo [1/4] Starting coturn TURN server (TCP 3478)...
start "CS2_TURN" cmd /k "cd /d C:\Users\Administrator\Desktop\CS2Build\Windows\CS2\Samples\PixelStreaming\WebServers\SignallingWebServer && platform_scripts\cmd\coturn\turnserver.exe -c turnserver.conf"

timeout /t 3 /nobreak >nul

echo [2/4] Starting Pixel Streaming SignallingWebServer...
start "CS2_Signalling" cmd /k "cd /d C:\Users\Administrator\Desktop\CS2Build\Windows\CS2\Samples\PixelStreaming\WebServers\SignallingWebServer && platform_scripts\cmd\node\node.exe cirrus.js"

timeout /t 5 /nobreak >nul

echo [3/4] Starting CS2.exe...
start "CS2_UE" "C:\Users\Administrator\Desktop\CS2Build\Windows\CS2.exe" -AudioMixer -PixelStreamingIP=127.0.0.1 -PixelStreamingPort=8890 -log -RenderOffScreen

timeout /t 2 /nobreak >nul

echo [4/4] Done.
echo Local URL:  http://127.0.0.1:8018/player.html
echo Remote URL: http://公网IP:8007/cs2/player.html
pause