首选本地用户数据报协议 (UDP) 多人游戏端口网络 API

通过本主题了解如何使用首选的本地用户数据报协议 (UDP) 多人游戏端口 API 改进多人游戏的可靠性。 所有 Xbox 控制台的迭代都允许使用 UDP 3074。 这是一个用于多人游戏网络流量的公开注册的已知端口。 Microsoft Game Development Kit (GDK) 游戏也不例外,可以使用首选的本地 UDP 多人游戏端口网络 API 访问此特殊端口。

首选本地 UDP 多人游戏端口是在后续套接字绑定操作中使用的本地端口;这与远程设备可能用于连接到本地设备的公用端口相反。 前者仅对 UDP 流量有意义,而不适用于传输控制协议 (TCP) 和 HTTP 流量,因为它专门面向多人、实时游戏网络流量。

过去,该端口受限于 UDP 3074。 但近年来,已通过引入回退逻辑提高此端口的可靠性。 此外,用户已能够手动配置该端口,以便处理他们自己独有的网络配置。 这就是为什么在 Microsoft Game Development Kit (GDK) 游戏中对 UDP 3074 进行硬编码不再安全的原因。 相反,Microsoft Game Development Kit (GDK) 游戏应动态查询当前配置的端口。

有三种方法可检索首选的本地 UDP 多人游戏端口。

所有三种变体都提供相同的基本功能。 Microsoft Game Development Kit (GDK) 游戏可根据其特定需要和使用案例调用这三种方式中的任意一种 (或任意组合)。

我们强烈建议所有 Microsoft Game Development Kit (GDK) 游戏都对其主要游戏流量使用首选端口。 此端口已针对对等网络拓扑和客户端服务器网络拓扑进行了优化。 Microsoft Game Development Kit (GDK) 平台确保此特定端口是最有可能适用于每个用户的特定网络环境的端口。 使用此特定端口可充分利用平台的客户支持和诊断流、提高标准化网络地址转换 (NAT) 兼容性,并提供标准化的 UPnP™ 认证设备功能,并且将数据包标识为对于服务质量 (QoS) 路由器和 ISP 算法是实时敏感的。

此首选端口尤其与依赖于对等网络拓扑的游戏相关。 它是唯一一种允许不执行防火墙打洞入站 UDP 数据包就可以通过防火墙的端口。 在 Microsoft Game Development Kit (GDK) 中依赖对等网络拓扑的游戏仍应提供它们自己的公共 IP 地址和端口发现,以及适用于具有适中或严格 NAT 类型的客户端的 NAT 穿越解决方案。 首选端口提高了这些技术的成功率,但不能替代它们。

使用客户端服务器网络拓扑的游戏也可从使用此端口中受益。 故障排除、UPnP™ 和数据包识别仍与在医院、酒店和大学宿舍中常见的强制网络门户和其他基于源的筛选方法相关。

应该将该首选端口视作和任何其他端口一样。 它应该与 Windows 套接字 2 (Winsock) API 配合使用。 游戏应该绑定到该端口上的 IPv4 和 IPv6 或使用双堆栈套接字,并且它应该绑定到 INADDR_ANY/in6addr_any 地址。

正在处理套接字故障

不保证返回的端口可用于建立与特定服务器或对等方的成功套接字连接。 应执行正常的游戏重试和回退逻辑。 任何时候关闭并重新打开套接字时,游戏都应该重新查询最新的首选本地 UDP 多人游戏端口,因为该端口可能随时间变化。

网络初始化

这三种 XNetworkingQueryPreferredLocalUdpMultiplayerPort API 的变体(阻止、异步、基于通知)都将会阻止或延迟完成/通知,直到网络在游戏启动时成功初始化且正常运行。 你可以根据检测网络初始化状态概述分别等待网络初始化,或调用这些 API 并等待它们返回。

暂停和继续

和其他套接字一样,绑定首选本地 UDP 多人端口的套接字应在挂起时关闭,在等待网络初始化之后将其重新创建,让其继续运行。 必须通过 RegisterAppStateChangeNotification 注册暂停和继续事件。 继续时,应假定首选本地 UDP 多人端口已改变,且它要么在聆听对首选本地 UDP 多人端口作出的更改,要么在创建新套接字时重新查询。 有关 WinSock 挂起和继续处理的更多信息,请参阅 WinSock 中的挂起和继续

首选的本地 UDP 多人游戏端口中的更改

游戏可以通过使用 XNetworkingRegisterPreferredLocalUdpMultiplayerPortChanged API 来侦听首选的本地 UDP 多人游戏端口中的更改。

将进行所有尝试以确保首选本地 UDP 多人游戏端口在游戏运行期间不会更改。 但是,由于用户的外部网络条件更改致使任何现有套接字流失效,此时将无法避免该端口发生更改。 当网络连接级别 发生更改或是处于游戏的暂停/恢复周期当中时,该端口尤其可能发生更改。

首选本地 UDP 多人游戏端口更改时,来自将来对等方的更多入站连接可能在任何以前的首选端口上受阻。 这可能不会导致套接字层故障。 但游戏最终可能会停止在绑定到任何以前的首选端口的任何套接字上接收数据包。

发送到现有对等方和自其接收的数据包可能继续正常工作。 关于首选本地 UDP 多人游戏端口发生更改的通知可能不会对任何正在进行的游戏会话造成致命影响。

发生更改通知时,游戏应迁移到绑定在新首选端口上的新套接字。 这种迁移应该尽早进行,并且不要中断任何现有的游戏玩法。 为检测连接丢失并重试套接字连接,游戏应始终使用最新的首选端口。

测试首选的本地 UDP 多人游戏端口的更改

按照以下步骤更改首选的本地 UDP 多人游戏端口。

  1. 运行游戏时,打开 Xbox 导航页。 转到设置应用。
  2. 常规选项卡下,选择网络设置
  3. 选择高级设置,然后选择备用端口选择
  4. 将端口选择设置为手动。 若要选择端口,请使用下拉菜单。
  5. 此端口选择将立即生效,并会向你的游戏发出相应的通知。
  6. 完成测试后,将端口选择设置回自动 以将端口行为恢复为默认值。

注意

在“设置”应用中,你的游戏将受到限制,但即使你的游戏不可见,它仍可运行并将立即收到端口更改通知。 如果你让“设置”应用保持打开超过 10 分钟,而未切换回你的游戏,则你的游戏将暂停。

安全性

绑定到首选本地 UDP 多人游戏端口的套接字的行为就像任何其他套接字一样。 特别是,该套接字不提供任何额外的安全保护。 游戏应该在绑定到通信安全最佳实践规定的首选本地 UDP 多人端口的套接字之上使用自己的安全通信协议。 更多相关信息,请参阅通信安全概述(NDA 主题)要求授权

对等

首选本地 UDP 多人游戏端口将提供最佳已知端口,从该端口可生成对等网格。 该端口会尽可能优化配置,以便允许入站连接通过用户的 NAT 层。 但是,负责执行 NAT 遍历的是游戏,这包括以下方面。

  • NAT 类型检测
  • 检测和交换设备的公用 IP 地址和端口
  • NAT 打洞和遍历

Azure PlayFab Party

在内部,PlayFab Party 默认使用首选本地 UDP 多人游戏端口。 可通过 PlayFab Party API 进行配置。 除非 PlayFab Party 端口更改,否则游戏不应直接绑定到首选本地 UDP 多人游戏端口。

示例用法

下面的示例演示如何将双堆栈套接字绑定到首选本地 UDP 多人游戏端口。 该示例使用阻止 XNetworkingQueryPreferredLocalUdpMultiplayerPort 调用(为简便起见),并且假定游戏之前已经等待网络就绪并且已经调用 WSAStartup

HRESULT
CreateAndBindMultiplayerSocket(
    _Out_ SOCKET* multiplayerSocket
    )
{
    HRESULT hr;
    SOCKET resultSocket = INVALID_SOCKET;

    uint16_t port;
    hr = XNetworkingQueryPreferredLocalUdpMultiplayerPort(&port);
    if (FAILED(hr))
    {
        goto Failure;
    }

    resultSocket = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
    if (resultSocket == INVALID_SOCKET)
    {
        hr = HRESULT_FROM_WIN32(WSAGetLastError());
        goto Failure;
    }

    // Enable both IPv4 and IPv6 on this socket.
    int v6only = 0;
    if (setsockopt(resultSocket, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&v6only, sizeof(v6only)) == SOCKET_ERROR)
    {
        hr = HRESULT_FROM_WIN32(WSAGetLastError());
        goto Failure;
    }

    sockaddr_in6 sa;
    ZeroMemory(&sa, sizeof(sa));
    sa.sin6_family = AF_INET6;
    sa.sin6_port = htons(port);
    sa.sin6_addr = in6addr_any;
    if (bind(resultSocket, (const sockaddr*)&sa, sizeof(sa)) == SOCKET_ERROR)
    {
        hr = HRESULT_FROM_WIN32(WSAGetLastError());
        goto Failure;
    }

    *multiplayerSocket = resultSocket;

Exit:

    return hr;

Failure:

    if (resultSocket != INVALID_SOCKET)
    {
        (void)closesocket(resultSocket);
    }

    goto Exit;
}

另请参阅

首选的本地 UDP 多人游戏端口 API 参考 (XNetworking)

Windows 套接字 2 (Winsock)

通信安全概览(NDA 主题)要求授权