> ## Documentation Index
> Fetch the complete documentation index at: https://devdocs.xbox.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 使用直接点对点连接

> 在 PlayFab Party 中启用直接点对点连接,通过在端点之间直接路由流量而不是通过透明云中继来降低延迟。

# PlayFab Party 和直接点对点连接

本页解释如何使用示例游戏代码在 PlayFab Party 中启用和使用直接点对点连接。它还包括帮助你评估何时在游戏中使用直接点对点连接的注意事项。

## 何时使用直接点对点连接

PlayFab Party 支持多种通信拓扑。从概念上讲,所有聊天或数据消息都是从一台对等设备或用户直接发送给其他设备。然而,Party 会自动利用透明云中继服务,以避免建立直接点对点连接时常见的环境和安全问题。你可以通过利用其后台服务质量(QoS)测量来实现低延迟数据传输。(有关更多信息,请参阅 [PlayFab Party QoS 测量](/services/playfab/multiplayer/networking/concepts-regions)。)你可以选择启用直接点对点连接来进一步降低数据传输延迟。由于下文“安全考虑”中详述的安全问题,Microsoft 建议你使用我们的云中继服务,而不是启用直接点对点连接,除非你的游戏对延迟有严格要求。

### 平台支持

直接对等连接受 Party 库的 Windows 10、Microsoft Game Core、Nintendo Switch、PlayStation®4 和 PlayStation®5 版本支持。库的其他版本无论通过客户端 API 指定的直接对等连接选项如何,都将始终使用云中继进行数据传输。

“PlayStation”是 Sony Interactive Entertainment Inc. 的注册商标或商标。

### 安全考虑

由于使用直接对等连接在客户端之间直接发送数据,所有客户端都需要知道如何相互连接。这是通过在一起进行游戏会话的客户端之间共享客户端 IP 地址来完成的。例如,如果一款 16 人多人游戏使用直接对等连接来更新玩家位置,则 16 个游戏客户端中的每一个都需要知道其他 15 个客户端的 IP 地址,以便知道向谁发送数据。

在游戏客户端之间共享 IP 地址存在安全风险。共享 IP 地址可能允许恶意行为者检测该 IP 地址,并利用它在游戏之外恶意攻击其他玩家。执行这些攻击的常见方法是通过拒绝服务(DOS)攻击,或称为分布式拒绝服务(DDOS)的变种攻击。这些攻击的工作方式是试图用多余的网络流量压垮一个网络。如果攻击者能够向受害者发送足够的网络流量,受害者的网络硬件(调制解调器和路由器)必须花费所有时间处理多余的流量,而无暇处理其正常工作(处理合法的网络连接)。这实际上意味着受害者在攻击持续期间无法使用其网络。这经常被称为“被踢下线”。

总而言之,成功的直接对等连接可能会在某些设备之间提供更低的延迟。然而,尝试建立它还要求用户向他人披露其 IP 地址,这可能使恶意用户能够在游戏之外攻击其设备和互联网连接。出于策略原因,某些平台可能也不允许直接对等连接。请确保使用适合你性能和安全目标的直接对等连接选项。如果你在权衡风险后决定使用直接对等连接,请使用以下示例按网络和按设备为你的游戏选择加入。

### LAN 场景

Party 中的直接对等连接可用于在 LAN 场景中实现极低延迟。然而,即使在这些场景中,也需要有限的 Internet 连接来支持用户身份验证、LiveOps 数据和见解,以及语音聊天无障碍功能。

### 上行带宽考虑

使用直接点对点连接可能会增加游戏的上行带宽使用量。通过云中继服务传输游戏或语音消息时,Party 向服务发送单条消息;服务随后将消息复制并转发给每个目标设备。通过直接点对点连接传输游戏或语音消息时,Party 会为每个已建立直接点对点连接的目标设备复制并通过直接连接发送消息。因此,你的游戏的上行带宽使用量将按与已建立直接点对点连接的设备数量成比例地扩展。在启用直接点对点连接之前,请考虑这种上行带宽的增加对你的游戏是否可接受。

## 如何在游戏中使用直接点对点连接

### 在网络中启用直接点对点连接

通过 [`PartyManager::CreateNewNetwork()`](/services/playfab/multiplayer/networking/reference/classes/PartyManager/methods/partymanager_createnewnetwork) 创建网络时,你可以通过提供给该调用的 [`PartyNetworkConfiguration`](/services/playfab/multiplayer/networking/reference/structs/partynetworkconfiguration) 指定各种网络配置参数。可以使用 `PartyNetworkConfiguration::directPeerConnectivityOptions` 字段指定网络中设备是否以及如何支持直接点对点连接。

以下示例展示了一个网络配置,该配置指定应尝试在网络中所有设备之间进行直接点对点连接,而不考虑平台类型或登录提供商。

```cpp theme={null}
PartyNetworkConfiguration configuration = {};
configuration.directPeerConnectivityOptions = PartyDirectPeerConnectivityOptions::AnyPlatformType | PartyDirectPeerConnectivityOptions::AnyEntityLoginProvider;
// Initialize the rest of the network configuration parameters appropriately for your game before using.
```

作为将初始用户成功验证进入网络的一部分,如果网络配置允许,设备可能会尝试与已加入网络的其他设备建立直接点对点连接。对于成功的尝试,设备之间的端点消息和聊天数据将使用这些直接连接进行传输。对于因设备之间的环境不兼容而失败的尝试,这些设备之间的所有通信将改为通过透明云中继服务器传输。如果网络配置不允许设备尝试直接对等连接,则它们永远不会交换 IP 地址信息,并且始终通过透明云中继服务器传输端点消息和聊天数据。

<Note>
  建立直接对等连接是尽力而为的,可能因环境因素、每个设备的连接选项或平台政策而无法实现。有关评估是否已与特定设备建立直接点对点连接的更多信息,请参阅[评估连接类型和延迟](#evaluating-the-connection-type-and-latency)。
</Note>

### 按设备约束直接对等连接

除了网络配置中的直接对等连接选项之外,设备可以通过使用 [`PartyManager::SetOption()`](/services/playfab/multiplayer/networking/reference/classes/PartyManager/methods/partymanager_setoption) 设置 [`PartyOption::LocalDeviceDirectPeerConnectivityOptionsMask`](/services/playfab/multiplayer/networking/reference/enums/partyoption),为其身份验证进入的所有网络进一步约束直接对等连接。所有标志都使用按位 AND 运算进行求值。也就是说,只有在网络配置以及两个设备各自的本地掩码选项这三个位置都启用了某个标志,该标志才对给定网络的一对设备生效。即使网络配置允许相关形式的直接对等连接,任一设备也可通过在其本地设备掩码选项中不启用该标志,独立地选择退出彼此之间的 IP 地址披露和直接连接尝试。在支持直接对等连接的库版本中,`PartyOption::LocalDeviceDirectPeerConnectivityOptionsMask` 值默认允许由网络启用的所有直接对等连接。因此,只有在你有特定于设备的要求以防止涉及本地设备的部分或全部直接对等连接时,才需要对其进行配置。

以下示例展示了如何为本地设备约束直接对等连接,以便它仅尝试与同一平台上的设备建立直接对等连接。

```cpp theme={null}
PartyDirectPeerConnectivityOptions localDeviceMask = PartyDirectPeerConnectivityOptions::SamePlatformType | PartyDirectPeerConnectivityOptions::AnyEntityLoginProvider;
PartyError error = PartyManager::GetSingleton().SetOption(nullptr, PartyOption::LocalDeviceDirectPeerConnectivityOptionsMask, &localDeviceMask);
if (PARTY_FAILED(error))
{
    printf("Failed to set local device direct peer connectivity options mask! error = 0x%08x\n", error);
}
```

### 评估连接类型和延迟

你可以通过调用 [`PartyNetwork::GetDeviceConnectionType()`](/services/playfab/multiplayer/networking/reference/classes/PartyNetwork/methods/partynetwork_getdeviceconnectiontype) 来确定本地设备是否与特定远程设备建立了直接点对点连接。

我们建议你不要主动强制要求任何给定的一对设备之间可用直接点对点连接(即,如果 PartyNetwork::GetDeviceConnectionType() 报告的值不是 [`PartyDeviceConnectionType::DirectPeerConnection`](/services/playfab/multiplayer/networking/reference/enums/partydeviceconnectiontype),不要调用 [`PartyNetwork::LeaveNetwork()`](/services/playfab/multiplayer/networking/reference/classes/PartyNetwork/methods/partynetwork_leavenetwork)),因为具体使用的底层传输方法不会改变总体的通信逻辑能力。如果你的游戏设计对最大消息延迟有严格要求以鼓励直接对等连接,最好基于 [`PartyEndpointStatistic::AverageDeviceRoundTripLatencyInMilliseconds`](/services/playfab/multiplayer/networking/reference/enums/partyendpointstatistic) 统计信息所报告的该延迟的当前具体观察值采取行动,而不是基于传输机制做出抽象假设。否则,由于超出用户控制范围的环境因素,你可能会持续妨碍那些始终需要使用附近透明云中继服务器与同一群好友一起游戏的用户。

以下示例展示了如何检查 `network` 中从 `localEndpoint` 到 `remoteEndpoint` 的往返延迟。

```cpp theme={null}
// A helper for inspecting the connection type and average round trip latency between a local endpoint and a remote
// endpoint in a network.
void PrintConnectionTypeAndLatency(
    PartyNetwork* network,
    PartyLocalEndpoint* localEndpoint,
    PartyEndpoint* remoteEndpoint
    )
{
    // Retrieve the device associated with the remote endpoint.
    PartyDevice* remoteDevice;
    PartyError error = remoteEndpoint->GetDevice(&remoteDevice);
    if (PARTY_FAILED(error))
    {
        printf("Failed to get the remote device! error = 0x%08x\n", error);
        return;
    }

    // Get the device connection type.
    PartyDeviceConnectionType connectionType;
    PartyError error = network->GetDeviceConnectionType(remoteDevice, &connectionType);
    if (PARTY_FAILED(error))
    {
        printf("Failed to get device connection type! error = 0x%08x\n", error);
        return;
    }

    // Retrieve the latency statistic.
    PartyEndpointStatistic latencyStatistic = PartyEndpointStatistic::AverageDeviceRoundTripLatencyInMilliseconds;
    uint64_t latencyStatisticValue;
    error = localEndpoint->GetEndpointStatistics(
        1,                       // targetEndpointCount
        &remoteEndpoint,         // targetEndpoints
        1,                       // statisticCount
        &latencyStatistic,       // statisticTypes
        &latencyStatisticValue); // statisticValues
    if (PARTY_FAILED(error))
    {
        printf("Failed to get latency statistic! error = 0x%08x\n", error);
        return;
    }

    // Print the results.
    printf("Local endpoint 0x%p and remote endpoint 0x%p in network 0x%p have average round trip latency %llu ms and device connection type %i\n",
        localEndpoint,
        remoteEndpoint,
        network,
        latencyStatisticValue,
        static_cast<int32_t>(connectionType));
}
```

### 连接类型的变化

不断变化的环境条件可能会中断直接对等连接,使其对 PlayFab Party 变得不可用。
如果发生这种情况,设备会尝试回退到通过云中继服务器进行通信。如果中继通信仍然可行,则 [PartyNetwork::GetDeviceConnectionType()](/services/playfab/multiplayer/networking/reference/classes/PartyNetwork/methods/partynetwork_getdeviceconnectiontype) 函数将开始报告 [PartyDeviceConnectionType::RelayServer](/services/playfab/multiplayer/networking/reference/enums/partydeviceconnectiontype),并且设备将继续使用新的连接类型留在 Party 网络中。否则,通信中断的设备将离开网络。

<Warning>
  在直接对等连接中断时仍在传输或接收过程中的聊天和游戏消息可能永远不会到达,即使它们是使用 [PartySendMessageOptions::GuaranteedDelivery](/services/playfab/multiplayer/networking/reference/enums/partysendmessageoptions) 发送的,并且即使它们在 [PartyLocalEndpoint::GetEndpointStatistics()](/services/playfab/multiplayer/networking/reference/classes/PartyLocalEndpoint/methods/partylocalendpoint_getendpointstatistics) 返回的 [PartyEndpointStatistic::CurrentlyQueuedSendMessages](/services/playfab/multiplayer/networking/reference/enums/partyendpointstatistic) 或 [PartyEndpointStatistic::CurrentlyActiveSendMessages](/services/playfab/multiplayer/networking/reference/enums/partyendpointstatistic) 值中不再计入。
</Warning>

此外,即使使用 [PartySendMessageOptions::SequentialDelivery](/services/playfab/multiplayer/networking/reference/enums/partysendmessageoptions) 发送,在连接类型过渡期间传输的消息也可能不按顺序到达。
在使用直接对等连接和这些 PartySendMessageOptions 时,你的游戏应为这种数据丢失和乱序的可能性做好准备。

PartyDeviceConnectionType::RelayServer 的连接类型永远不会更改为其他类型,无论该值是在设备首次加入网络时分配的,还是在之前中断的直接对等连接之后分配的。

## 计费计量

在使用直接点对点连接的网络中,应用了与使用云中继服务的网络相同的[计费计量](/services/playfab/pricing/meters/meters#party)。然而,只有通过云中继服务的游戏或语音数据才计入 **Network egress** 和 **Party voice** 计量。


## Related topics

- [PlayFab Party 发行说明](/zh-CN/services/playfab/multiplayer/networking/release-notes.md)
- [DirectPeerConnectivityOptions](/zh-CN/services/playfab/multiplayer/networking/unity-party-api-reference/enums/partyunitydirectpeerconnectivityoptions.md)
- [PlayFab Party 端口使用情况和防火墙要求](/zh-CN/services/playfab/multiplayer/networking/concepts-port-usage.md)
- [PlayFab Party Unity SDK](/zh-CN/services/playfab/multiplayer/networking/unity-party-api-reference/index.md)
- [Party C/C++ API 概述](/zh-CN/services/playfab/multiplayer/networking/reference/party_members.md)
