Unity Relay连接DOTS客户端与服务器失败问题求助
Unity Relay + DOTS 联网连接失败问题修复方案
问题核心分析
你的代码存在3个关键错误导致客户端无法连接:
- 错误使用普通网络驱动:直接用
NetworkStreamDriver连接Relay服务器IP/端口,没有使用Unity Relay专属的RelayNetworkDriver,缺少Relay身份验证必需的密钥、分配ID等核心信息。 - Host逻辑自冲突:在
StartRelayHost中直接调用StartRelayClient,导致刚创建的服务器世界被DestoryLocalSimulationWorld()销毁,且重复加载场景,完全破坏了服务器初始化流程。 - 服务器监听地址错误:服务器监听本地默认地址(
ClientServerBootstrap.DefaultListenAddress),而Relay模式下服务器需要连接到Relay节点,通过Relay中转等待客户端连接,而非直接监听本地端口。
修正后的代码实现
1. 依赖准备
确保项目已导入Unity.Networking.Transport.Relay包,使用RelayNetworkDriver替代普通NetworkStreamDriver。
2. 连接管理器修正代码
using Unity.Networking.Transport; using Unity.Networking.Transport.Relay; using Unity.Services.Relay.Models; using UnityEngine.SceneManagement; public class ConnectionManager : MonoBehaviour { private RelayNetworkDriver m_ServerDriver; private RelayNetworkDriver m_ClientDriver; public async void StartRelayHost(int maxPlayers) { // 清理旧世界并加载游戏场景(仅执行一次) DestroyLocalSimulationWorld(); SceneManager.LoadScene(1); // 创建Relay主机分配 var allocation = await RelayService.Instance.CreateAllocationAsync(maxPlayers); var joinCode = await RelayService.Instance.GetJoinCodeAsync(allocation.AllocationId); Debug.Log($"Relay 房间码: {joinCode}"); // 初始化服务器世界 var serverWorld = ClientServerBootstrap.CreateServerWorld("ServerWorld"); World.DefaultGameObjectInjectionWorld = serverWorld; // 配置Relay服务器驱动 var serverRelayData = new RelayServerData(allocation, "udp"); m_ServerDriver = new RelayNetworkDriver(); m_ServerDriver.Bind(serverRelayData); m_ServerDriver.Listen(); // 将驱动注册到服务器实体管理器 var driverEntity = serverWorld.EntityManager.CreateEntity(); serverWorld.EntityManager.AddComponentData(driverEntity, m_ServerDriver); } public async void StartRelayClient(string joinCode) { DestroyLocalSimulationWorld(); SceneManager.LoadScene(1); // 通过房间码获取Relay客户端分配信息 var allocation = await RelayService.Instance.JoinAllocationAsync(joinCode); // 初始化客户端世界 var clientWorld = ClientServerBootstrap.CreateClientWorld("ClientWorld"); World.DefaultGameObjectInjectionWorld = clientWorld; // 配置Relay客户端驱动并连接 var clientRelayData = new RelayClientData(allocation, "udp"); m_ClientDriver = new RelayNetworkDriver(); m_ClientDriver.Bind(clientRelayData); m_ClientDriver.Connect(); // 将驱动注册到客户端实体管理器 var driverEntity = clientWorld.EntityManager.CreateEntity(); clientWorld.EntityManager.AddComponentData(driverEntity, m_ClientDriver); } private void DestroyLocalSimulationWorld() { if (World.DefaultGameObjectInjectionWorld != null) { World.Destroy(World.DefaultGameObjectInjectionWorld); } } private void Update() { // 轮询服务器驱动事件 if (m_ServerDriver.IsCreated) { m_ServerDriver.ScheduleUpdate().Complete(); NetworkConnection connection; while ((connection = m_ServerDriver.Accept()) != default) { Debug.Log("客户端通过Relay连接成功"); // 处理新连接的业务逻辑 } } // 轮询客户端驱动事件 if (m_ClientDriver.IsCreated) { m_ClientDriver.ScheduleUpdate().Complete(); NetworkEvent.Type eventType; while ((eventType = m_ClientDriver.PopEvent(out _, out _)) != NetworkEvent.Type.Empty) { if (eventType == NetworkEvent.Type.Connect) { Debug.Log("客户端成功接入Relay网络"); } } } } private void OnDestroy() { // 释放驱动资源 if (m_ServerDriver.IsCreated) m_ServerDriver.Dispose(); if (m_ClientDriver.IsCreated) m_ClientDriver.Dispose(); } }
3. Relay入口逻辑修正
// 创建Relay主机 public async void CreateRelayHost() { try { await _connectionManager.StartRelayHost(3); } catch (RelayServiceException e) { Debug.LogError($"Relay主机创建失败: {e.Message}"); } } // 客户端通过房间码连接 public void JoinRelayRoom(string joinCode) { _connectionManager.StartRelayClient(joinCode); }
关键注意事项
- Relay驱动唯一性:服务器和客户端必须使用
RelayNetworkDriver,并传入对应的RelayServerData/RelayClientData,这些数据包含Relay连接必需的验证信息,不能直接用Relay服务器的IP/端口做普通连接。 - 多进程测试:测试时需启动两个独立Unity进程(一个作为主机,一个作为客户端),禁止在同一进程内同时启动主机和客户端,避免世界与驱动冲突。
- 场景加载顺序:确保场景加载完成后再初始化NetCode世界,防止场景加载重置实体管理器。
内容的提问来源于stack exchange,提问作者Marek Kurowski
相关产品推荐
相关产品推荐

