> ## 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.

# 检测网络初始化状态

> 检测网络初始化状态

## 网络初始化

本文帮助你了解如何在 Microsoft 游戏开发工具包 (GDK) 游戏中获取网络连接性与初始化信息。GDK 游戏通常在核心 OS 组件和网络服务运行之前就已启动。因此，在游戏启动后过早调用大多数网络与安全 API（包括 `WinSock`、`WinHTTP`、`BCrypt`、`WinCrypt`、`schannel` 和 `IPHLPAPI`）会导致不确定的行为。这些行为可能包括意外失败、未初始化的返回值、任意丢包，以及潜在的内存损坏和崩溃。

为避免这种不确定行为，Microsoft 游戏开发工具包 (GDK) 游戏应使用 [XNetworkingGetConnectivityHint](/reference/networking/xnetworking/functions/xnetworkinggetconnectivityhint) 和 [XNetworkingRegisterConnectivityHintChanged](/reference/networking/xnetworking/functions/xnetworkingregisterconnectivityhintchanged) 函数。特别是，`XNetworkingConnectivityHint::networkInitialized` 字段指示网络是否已初始化。游戏应等待 `networkInitialized` 字段变为 `true` 后再调用网络与安全 API。

许多中间件库内部也会使用网络和安全 API——即使是非网络类型的中间件也可能为遥测或调试使用网络堆栈。请咨询各中间件供应商以了解具体做法。如果中间件本身不会等待网络初始化，你可能需要延迟加载该中间件，直到网络完成初始化。Microsoft 游戏开发工具包 (GDK) 中的若干库，如 [XSAPI](https://learn.microsoft.com/windows/uwp/xbox-live/xsapi-flat-c) 和 [Azure PlayFab Party](/build/console-features/networking/game-mesh/playfab-party-intro-networking)，都要求在使用前等待网络初始化。

## HTTP 堆栈时序

`xCurl` 和 `libHttpClient` 是本文档集中唯一自动管理网络初始化的 HTTP 库。如果你的游戏使用其他任何 HTTP 堆栈，请显式安排启动顺序。一种安全的模式是：

1. 查询当前连接性提示 (connectivity hint) 或注册连接性变更通知。
2. 等待 `XNetworkingConnectivityHint::networkInitialized` 为 `true`。
3. 初始化 `WinSock` 及其他网络依赖项。
4. 访问证书状态并配置信任。
5. 创建 HTTP 堆栈并开始发出请求。

网络就绪应优先。过早启动 `WinSock`、证书状态或 HTTP 状态可能引发难以诊断的故障，因为可见错误往往晚于根因出现。

以下示例展示了游戏自有 HTTP 堆栈启动序列的开头几步。它在启动 `WinSock` 之前直接查询 `XNetworkingConnectivityHint`。

```cpp theme={null}
HRESULT InitializeNetworkingDependencies()
{
    XNetworkingConnectivityHint connectivityHint{};
    HRESULT hr = XNetworkingGetConnectivityHint(&connectivityHint);
    if (FAILED(hr))
    {
        return hr;
    }

    if (!connectivityHint.networkInitialized)
    {
        // Try again after your title receives a connectivity change notification.
        return E_PENDING;
    }

    WSADATA wsaData{};
    int winsockResult = WSAStartup(MAKEWORD(2, 2), &wsaData);
    if (winsockResult != 0)
    {
        return HRESULT_FROM_WIN32(winsockResult);
    }

    return S_OK;
}
```

以上步骤成功后，创建你的 HTTP 堆栈对象，然后在发出请求之前应用堆栈特定的信任配置。对于非 Schannel 堆栈，可能包括加载调试工具所需的代理证书。

## 挂起与恢复

此外，游戏的挂起/恢复周期会将 `networkInitialized` 字段重置为 `false`。挂起时，游戏应清理所有网络和安全组件的所有句柄，并停止所有网络操作。每个网络 API 的概述页面提供了该 API 在挂起时的具体要求，请参阅。在恢复时，游戏应再次等待 `networkInitialized` 字段变为 `true`，然后再尝试重新建立连接并使用任何网络或安全 API。我们建议恢复时的网络初始化路径与初始游戏启动路径一致——无论恢复还是启动，都应在网络初始化后再启动网络相关代码。GameChat2 和 Azure PlayFab Party 等不感知挂起/恢复的中间件库，必须在挂起时清理，在恢复时等待网络初始化后再重新初始化。

请假定除 xCurl 之外的每个 HTTP 堆栈都需要显式的生命周期管理。挂起或关闭时，停止将新请求入队并取消或排空正在进行中的工作。销毁不应跨挂起存活的请求句柄、会话和套接字。

恢复时，将网络启动视作一次全新的初始化路径。在重新创建 HTTP 状态或重新注册监听器之前，再次等待 `networkInitialized`。基于可取消、非阻塞工作的设计比长时间阻塞调用更容易在挂起时干净地收尾。

## 测试网络初始化

在恢复和游戏启动时，网络初始化通常需要几秒钟，具体时长取决于主机类型和用户的网络环境。在开发过程中，网络初始化几乎是瞬时的。这可能会掩盖游戏各部分未正确等待网络初始化的问题。为了测试网络初始化场景，可使用 `xbconfig NetworkInitDelayInSeconds=30` 为网络初始化过程添加一段任意延迟。使用此设置时，请务必在每次测试之间使用 `xbapp terminate /full` 完整重启游戏。测试完成后请将 `NetworkInitDelayInSeconds` 设回 `0`。

对于创建自有网络状态的 HTTP 堆栈，测试覆盖至少应包括以下场景：

* 冷启动
* 请求活动时挂起
* 干净挂起后的恢复
* Quick Resume 或等效恢复流程
* 挂起与恢复边界附近的网络断开

## 网络初始化代码示例

以下代码示例展示如何以实时、安全的方式轮询网络是否已初始化。

```cpp theme={null}

bool IsNetworkInitialized()
{
    XNetworkingConnectivityHint connectivityHint;
    if (SUCCEEDED(XNetworkingGetConnectivityHint(&connectivityHint)))
    {
        return connectivityHint.networkInitialized;
    }
    return false;
}

```

以下代码示例展示游戏如何阻塞等待，直到网络初始化完成。

```cpp theme={null}
static
void
NetworkConnectivityHintChangedCallback(
    _In_ void* context,
    _In_ const XNetworkingConnectivityHint* connectivityHint
    )
{
    HANDLE networkInitializedEvent = static_cast<HANDLE>(context);
    if (connectivityHint->networkInitialized)
    {
        (void)SetEvent(networkInitializedEvent);
    }
}

HRESULT EnsureNetworkInitialized()
{
    HRESULT hr = S_OK;
    XNetworkingConnectivityHint connectivityHint;
    XTaskQueueHandle queue;

    hr = XTaskQueueCreate(XTaskQueueDispatchMode::Immediate, XTaskQueueDispatchMode::Immediate, &queue);
    if (SUCCEEDED(hr))
    {
        // Use the new XNetworking APIs to check if the network is initialized.
        hr = XNetworkingGetConnectivityHint(&connectivityHint);
        if (SUCCEEDED(hr))
        {
            if (!connectivityHint.networkInitialized)
            {
                // The network isn't initialized. Wait until the network becomes initialized.
                HANDLE networkInitializedEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
                if (networkInitializedEvent != nullptr)
                {
                    XTaskQueueRegistrationToken token;
                    hr = XNetworkingRegisterConnectivityHintChanged(queue, networkInitializedEvent, NetworkConnectivityHintChangedCallback, &token);
                    if (SUCCEEDED(hr))
                    {
                        DWORD result = WaitForSingleObjectEx(networkInitializedEvent, INFINITE, FALSE);
                        if (result != WAIT_OBJECT_0)
                        {
                            hr = HRESULT_FROM_WIN32(GetLastError());
                        }

                        XNetworkingUnregisterConnectivityHintChanged(token, true);
                    }

                    CloseHandle(networkInitializedEvent);
                }
                else
                {
                    hr = HRESULT_FROM_WIN32(GetLastError());
                }
            }
        }

        XTaskQueueCloseHandle(queue);
    }

    return hr;
}

```

## 网络信息

在 Microsoft 游戏开发工具包 (GDK) 游戏中，可以使用 [XNetworkingGetConnectivityHint](/reference/networking/xnetworking/functions/xnetworkinggetconnectivityhint) API 获取网络信息。

[XNetworkingGetConnectivityHint](/reference/networking/xnetworking/functions/xnetworkinggetconnectivityhint) API 返回设备级的信息，包括网络连接级别、数据限制、有线还是无线连接类型，以及网络是否已初始化。它是一个实时、安全的 API，会立即返回当前信息。你可以使用 [XNetworkingRegisterConnectivityHintChanged](/reference/networking/xnetworking/functions/xnetworkingregisterconnectivityhintchanged) 和 [XNetworkingUnregisterConnectivityHintChanged](/reference/networking/xnetworking/functions/xnetworkingunregisterconnectivityhintchanged) 函数监听变化。

以下代码示例展示如何使用 [XNetworkingGetConnectivityHint](/reference/networking/xnetworking/functions/xnetworkinggetconnectivityhint) 函数查询当前网络状态的信息。

```cpp theme={null}

XNetworkingConnectivityHint connectivityHint;
if (SUCCEEDED(XNetworkingGetConnectivityHint(&connectivityHint)))
{
    printf(L"network initialized %u\n", connectivityHint.networkInitialized);
    printf(L"network connectivity level hint %u\n", connectivityHint.connectivityLevel);
    printf(L"network connectivity cost hint %u\n", connectivityHint.connectivityCost);
    printf(L"network approaching data limit %u\n", connectivityHint.approachingDataLimit);
    printf(L"network over data limit %u\n", connectivityHint.overDataLimit);
    printf(L"device is roaming %u\n", connectivityHint.roaming);
    switch (connectivityHint.ianaInterfaceType) {
    case IF_TYPE_ETHERNET_CSMACD:
        printf(L"network type is wired\n");
            break;
    case IF_TYPE_IEEE80211:
        printf(L"network type is wireless\n");
        break;
    case IF_TYPE_WWANPP:
    case IF_TYPE_WWANPP2:
        printf(L"network type is broadband\n");
        break;
    default:
        printf(L"network type is unusually esoteric %u\n", connectivityHint.connectivityLevel);
        break;
    }
}

```

## 网络连接最佳实践

除 `XNetworkingConnectivityHint::networkInitialized` 字段外，返回的 [XNetworkingConnectivityHint](/reference/networking/xnetworking/structs/xnetworkingconnectivityhint) 结构中的字段都是提示。它们是设备基于自身观察到的网络流量启发式得出的对当前网络状态的最佳猜测。

`XNetworkingConnectivityLevelHint` 的状态表示网络级别的总体近似，以简化游戏的连接性逻辑。游戏可以预期 `XNetworkingConnectivityLevelHint::None` 反映的是网络介质断开以及在网络环境几分钟未变化的稳态下总体缺乏连接性。其他状态并不能表明是否可以连接到你游戏的特定端点。

因此，我们建议在等待网络初始化完成后，无论 `XNetworkingConnectivityHint::connectivityLevelHint` 字段处于何种状态，都直接使用 `WinSock` 和/或 `WinHTTP` 尝试与端点建立连接。如果这些 API 后续失败，我们建议再使用 [XNetworkingGetConnectivityHint](/reference/networking/xnetworking/functions/xnetworkinggetconnectivityhint) API 用于更多 UI 与诊断报告目的。然后应等待网络连接级别发生变化后再重试。

## 获取高级网络信息

大多数 Microsoft 游戏开发工具包 (GDK) 游戏应使用 [XNetworkingGetConnectivityHint](/reference/networking/xnetworking/functions/xnetworkinggetconnectivityhint) API 搭配 `WinSock` API 来获取网络状态和基本网络信息（如 IP 地址）。如果需要更多信息，GDK 中提供了底层的 [IP Helper API](https://learn.microsoft.com/windows/desktop/IpHlp/ip-helper-start-page)。

总的来说，在 Microsoft 游戏开发工具包 (GDK) 中使用 `IP Helper` API 与在 Win32 程序中使用该 API 的方式相同。

1. 在源文件中，在 `#include <winsock2.h>` 之后 `#include <iphlpapi.h>`。

2. 链接 `XGamePlatform.lib`，而不是直接链接 `Ws2_32.lib` 和 `Iphlpapi.lib`。

在 Microsoft 游戏开发工具包 (GDK) 游戏中，只有 `WINAPI\_PARTITION\_GAMES` API 系列下的 API 可用。

在 XBOX 主机上，由于底层平台抽象，使用 `IP Helper` API 获取某些信息并不准确。这包括但不限于：

* MAC 地址始终为 `AA-AA-AA-AA-AA-AA`。
* 所有接口都会报告为有线接口，无论底层网络连接类型如何。真实的接口类型仅可通过 [XNetworkingGetConnectivityHint](/reference/networking/xnetworking/functions/xnetworkinggetconnectivityhint) 获取。

## 不支持的网络连接性 API

Microsoft 游戏开发工具包 (GDK) 游戏不支持以下网络连接性 API，应改用 [XNetworkingGetConnectivityHint](/reference/networking/xnetworking/functions/xnetworkinggetconnectivityhint) API 确定网络连接性。

* [Windows.Networking.Connectivity 命名空间](https://learn.microsoft.com/uwp/api/windows.networking.connectivity)

* [Network List Manager](https://learn.microsoft.com/windows/desktop/nla/portal)

## 另请参阅

[XNetworkingGetConnectivityHint](/reference/networking/xnetworking/functions/xnetworkinggetconnectivityhint)

[XNetworkingRegisterConnectivityHintChanged](/reference/networking/xnetworking/functions/xnetworkingregisterconnectivityhintchanged)

[XNetworkingUnregisterConnectivityHintChanged](/reference/networking/xnetworking/functions/xnetworkingunregisterconnectivityhintchanged)

[Windows Sockets 2 (Winsock)](https://learn.microsoft.com/windows/desktop/WinSock/windows-sockets-start-page-2)

[Windows HTTP Services (WinHTTP)](https://learn.microsoft.com/windows/desktop/winhttp/winhttp-start-page)

[IP Helper API](https://learn.microsoft.com/windows/desktop/IpHlp/ip-helper-start-page)


## Related topics

- [首选本地 UDP 多人游戏端口网络 API](/zh-CN/build/console-features/networking/game-mesh/preferred-local-udp-multiplayer-port-networking.md)
- [XNetworkingGetConnectivityHint](/zh-CN/reference/networking/xnetworking/functions/xnetworkinggetconnectivityhint.md)
- [使用 GDK 在 XBOX 主机上进行网络开发](/zh-CN/build/console-features/networking/index.md)
- [XNetworkingConnectivityHint](/zh-CN/reference/networking/xnetworking/structs/xnetworkingconnectivityhint.md)
- [XNetworkingRegisterConnectivityHintChanged](/zh-CN/reference/networking/xnetworking/functions/xnetworkingregisterconnectivityhintchanged.md)
