.net core服务发布经验总结
项目:NewRingGame(Unity WebGL + .NET 7 API)
环境:CentOS 7、宝塔面板、同机部署
WebGL:https://mini.vrast.cn/
API:https://server.vrast.cn
部署目录:/www/wwwroot/ringgame
一、背景
《恶魔轮盘》联机版采用 Unity WebGL 客户端 + .NET 7 服务端 架构。首次上生产时,WebGL 与 API 部署在同一台 CentOS 7 服务器,通过宝塔面板管理 Nginx、进程与 FTP。
联调过程中,HTTP 登录接口已经可用,但游戏界面长期停在 「连接 Hub…」,无法进入在线大厅。本文记录从部署到联机打通的完整排查过程与最终解法。
二、部署阶段:服务器与环境
2.1 .NET 7 运行时(CentOS 7)
CentOS 7 官方源没有 .NET 7 包,不能照搬 Ubuntu 的 apt install。
做法: 使用微软安装脚本安装 ASP.NET Core 7 运行时:
1 | curl -sSL https://dot.net/v1/dotnet-install.sh | bash /dev/stdin --runtime aspnetcore --channel 7.0 |
发布包中附带 install-dotnet-runtime.sh、run-prod.sh,便于在宝塔里一键启动。
2.2 宝塔启动命令写法
错误示例: 把环境变量写在整条命令最前面,宝塔进程守护可能解析失败。
1 | ASPNETCORE_ENVIRONMENT=Production nohup dotnet ... |
推荐:
- 宝塔「启动文件」填:
/www/wwwroot/ringgame/run-prod.sh - 或 systemd:
ExecStart=/usr/bin/dotnet /www/wwwroot/ringgame/NewRingGame.Server.dll - 环境变量在
run-prod.sh或 systemdEnvironment=中设置
2.3 FTP 上传 553 Permission denied
宝塔站点目录属主多为 www:www,权限 drwxr-xr-x,FTP 用户不在 www 组时无法写入。
做法:
1 | chmod 775 /www/wwwroot/ringgame |
GM 面板发布默认 FTP 主机 server.vrast.cn,远端目录 /www/wwwroot/ringgame。
2.4 MySQL SSL 握手失败
宝塔本地 MySQL 连接时,若未配置 SSL,.NET 驱动可能报 SSL 相关错误。
做法: 连接串增加:
1 | SslMode=None; |
三、服务端代码与配置
3.1 反向代理与 CORS
Nginx 终止 HTTPS 后,Kestrel 收到的是 HTTP 请求,需正确识别客户端 IP 与原始协议。
Program.cs 已配置:
UseForwardedHeaders():识别X-Forwarded-For、X-Forwarded-Proto- 生产环境启用 CORS,允许 WebGL 站点跨域带凭证访问
3.2 客户端网络配置
Assets/Resources/Network/NetworkSettings.json 生产段示例:
1 | { |
派生地址:
- API:
https://server.vrast.cn/api - Hub:
wss://server.vrast.cn/hub/game?access_token={token}
3.3 登录与 Hub 鉴权流程
POST /api/auth/login→ 返回 JWTtoken- WebSocket 连接
/hub/game?access_token=... GameHub.OnConnectedAsync校验 token,失败则Context.Abort()
因此:登录 200 只说明 HTTP 正常;卡在「连接 Hub…」说明 WebSocket 阶段有问题。
四、联机故障:「连接 Hub…」
4.1 现象
| 项目 | 状态 |
|---|---|
GET /health |
200 |
POST /api/auth/login |
200,有 token |
| CORS 预检 | 正常,Access-Control-Allow-Origin: https://mini.vrast.cn |
| 游戏界面 | 停在「连接 Hub…」 |
| DevTools → Network → Socket | 无连接,或 Unity 侧无后续日志 |
Console 可见 [GameHub] 连接 wss://server.vrast.cn/hub/game,但无「握手成功」或明确报错。
4.2 分层排查
第一层:服务端 HTTP 是否正常
1 | curl -s https://server.vrast.cn/health |
第二层:浏览器原生 WebSocket 是否正常
在 https://mini.vrast.cn/ 控制台执行(替换真实 token):
1 | const ws = new WebSocket("wss://server.vrast.cn/hub/game?access_token=你的token"); |
若输出 「WS 成功」,则 Nginx 反代与 Hub 均已正常,问题在 Unity WebGL 客户端。
第三层:Unity 客户端
WebGL 上 System.Net.WebSockets.ClientWebSocket 不可靠;配合 ConfigureAwait(false)、Task.Run 会导致异步回调无法继续,界面永久停在「连接 Hub…」。
五、Nginx 配置(宝塔)
5.1 全局:WebSocket Upgrade 映射
宝塔 → 软件商店 → Nginx → 配置修改,在 http { 内、server { 前加入:
1 | map $http_upgrade $connection_upgrade { |
5.2 站点:server.vrast.cn
原配置主要问题:
| 问题 | 原值 | 修正 |
|---|---|---|
| Host 头 | 127.0.0.1:$server_port |
$host |
| 转发协议 | 缺失 | X-Forwarded-Proto $scheme |
| Connection | 固定 "upgrade" |
$connection_upgrade |
| Hub 路径 | 与普通 API 混用 | 单独 location /hub/ |
| 发送超时 | 30s |
86400s(长连接) |
/hub/ 示例:
1 | location /hub/ { |
5.3 宝塔保存配置的坑
直接删改 SSL / 错误页注释块会导致 「配置文件保存失败」。必须保留:
#SSL-START内的#error_page 404/404.html;#ERROR-PAGE-START…#ERROR-PAGE-END整段#CERT-APPLY-CHECK--START/END#HTTP_TO_HTTPS_START/END
只改 location 反代部分,不要动上述标记块。
保存后执行:
1 | nginx -t && nginx -s reload |
六、WebGL 客户端修复
6.1 问题根因
| 问题 | 说明 |
|---|---|
ConfigureAwait(false) |
WebGL 无线程池,continuation 无法执行 |
Task.Run |
WebGL 不支持,接收循环无法启动 |
ClientWebSocket |
浏览器测试 WS 成功,Unity 侧仍挂起 |
6.2 解决方案
WebGL 改用 浏览器原生 WebSocket(jslib):
| 文件 | 作用 |
|---|---|
Assets/Plugins/WebGL/BrowserWebSocket.jslib |
JS 层 WebSocket 封装 |
Assets/Scripts/Network/BrowserWebSocketTransport.cs |
C# 桥接 |
Assets/Scripts/Network/SignalRWebSocketClient.cs |
WebGL 自动选择传输层 |
其他改动:
GameHubClient:20 秒连接超时NetworkConfig:443 端口省略:443(wss://server.vrast.cn/hub/game)- 日志使用
UnityEngine.Debug.Log,避免与DemonRoulette.Debug命名空间冲突
6.3 成功标志
Console 应出现:
1 | [GameHub] 连接 wss://server.vrast.cn/hub/game |
随后进入在线大厅(房间列表)。
6.4 jslib 运行时错误:DemonRouletteWs is not defined
登录后 Hub 连接阶段,浏览器弹出 Unity 运行时错误,Console 完整堆栈类似:
1 | Uncaught ReferenceError: DemonRouletteWs is not defined |
原因: 第一版 BrowserWebSocket.jslib 在 mergeInto 外部声明了全局变量:
1 | // ❌ 错误写法:打包后该变量不会进入 framework.js 作用域 |
Unity / Emscripten 只会把 mergeInto(LibraryManager.library, ...) 内的符号打进 framework.js。外部 var 在 WebGL 运行时不可见,C# 通过 [DllImport("__Internal")] 调用 _DemonRoulette_WsCreate 时就会崩溃。
正确写法: 使用 Emscripten 的 $ 依赖语法保存状态,并 autoAddDeps:
1 | // ✅ 正确写法:Assets/Plugins/WebGL/BrowserWebSocket.jslib |
如何确认已部署新包:
- 在 DevTools → Sources 或 Network 里打开
WebGL-Staging.framework.js(解压.br后) - 搜索
DemonRouletteWs→ 不应存在 - 搜索
drrWsState→ 应存在
若仍能看到 DemonRouletteWs,说明上传的是旧 Build,或 Service Worker / IndexedDB 缓存了旧 framework.js(URL 带 ?v=legacy.1.1.1... 时尤其容易误判「已更新」)。
6.5 发布注意
- Release Build 重新打 WebGL 包并上传
mini.vrast.cn - 强刷或清缓存:Service Worker 可能缓存旧包(项目内有
WebCacheRepair) - DevTools → Application → Service Workers → Unregister(如有必要)
七、经验清单(Checklist)
服务端
- API 监听
127.0.0.1:8080,Nginx 反代到 443 -
curl /health与curl login均正常 - MySQL 连接串含
SslMode=None(宝塔本地库) -
ForwardedHeaders、CORS 已启用
Nginx
- 全局
map $http_upgrade $connection_upgrade -
Host为$host,非127.0.0.1 -
X-Forwarded-Proto $scheme -
/hub/单独 location,proxy_buffering off - 宝塔 SSL/ERROR-PAGE 注释块未删
客户端
-
NetworkSettings.jsonrelease 段指向生产域名 - WebGL 使用
BrowserWebSocket.jslib(非纯 ClientWebSocket) - 浏览器原生 WS 测试通过后再查 Unity 包版本
- 上传后清 SW / 站点缓存
八、相关路径速查
| 用途 | 路径/地址 |
|---|---|
| WebGL 站点 | https://mini.vrast.cn/ |
| API / Hub | https://server.vrast.cn |
| 服务端部署 | /www/wwwroot/ringgame |
| GM 发布面板 | server/scripts/dev-manager/app.py |
| 网络配置 | Assets/Resources/Network/NetworkSettings.json |
| Hub 服务端 | server/src/NewRingGame.Server/Hubs/GameHub.cs |
| 登录 UI | Assets/Scripts/UI/OnlineNetworkUiBinder.cs |
九、小结
本次联机打通,问题并不在「登录接口」,而在 Hub WebSocket 全链路:
- Nginx 需正确转发 WebSocket(Host、Upgrade、Forwarded-Proto、
/hub/独立规则) - 服务端 在反代后需识别 HTTPS 与真实 IP
- WebGL 客户端 不能使用桌面端惯用的
ClientWebSocket+ConfigureAwait(false),必须走浏览器原生 WebSocket
排查顺序建议:HTTP 健康检查 → 登录 API → 浏览器 WS 测试 → Unity 包与缓存。当浏览器 new WebSocket(...) 已成功而 Unity 仍卡住时,应优先怀疑 WebGL 网络实现,而非继续改 Nginx。
归档日期:2026-06-06
本文标题:.net core服务发布经验总结
文章作者:Keyle
发布时间:2026-06-12
最后更新:2026-06-12
原始链接:https://vrast.cn/posts/4461/
版权声明:©Keyle's Blog. 本站采用署名-非商业性使用-相同方式共享 4.0 国际进行许可