You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 05:17:26