检测网络初始化状态
使用此主题了解如何检索 Microsoft 游戏开发工具包(GDK)游戏中的网络连接和初始化信息。 它们通常在核心操作系统组件和网络服务运行之前启动。 因此,在游戏启动后过快地尝试调用大多数网络和安全 API(包括 WinSock
、WinHTTP
、 BCrypt
、 WinCrypt
、schannel
和IPHLPAPI
)将导致不确定的行为。 这些行为可能包括意外失败、未初始化的返回值、任意数据包丢失以及潜在的内存损坏和崩溃。
为了避免此不确定的行为,Microsoft 游戏开发工具包(GDK)游戏应使用 XNetworkingGetConnectivityHint 和 XNetworkingRegisterConnectivityHintChanged 函数。
XNetworkingConnectivityHint::networkInitialized
字段尤其指示网络是否已初始化。 游戏应等到 networkInitialized
字段为 true
,才能调用网络和安全 API。
许多中间件库还在内部使用网络和安全 API,即使是非网络中间件也可能使用网络堆栈来进行遥测或调试。 要了解每种情况下该如何操作,请向中间件提供商咨询。 如果中间件本身并不等待网络初始化,可能需要延迟加载中间件,直到网络完成初始化。 Microsoft Game Development Kit (GDK) 中的多个库(例如 XSAPI 和 Azure PlayFab Party)都需要等到网络初始化完成后才能使用。
暂停和继续
此外,游戏挂起/恢复周期会将 networkInitialized
字段重置回 false
。 在挂起时,游戏应该清理指向所有网络和安全组件的所有句柄,并且停止所有网络操作。 有关该 API 暂停时的要求的更多信息,请参阅每个网络 API 的概述页。 恢复后,游戏应再次等到 networkInitialized
字段变为 true
,然后再尝试重新建立连接,并使用任何网络或安全 API。 建议恢复网络初始化路径与游戏的初始启动路径相同,即在恢复时或在游戏启动时,等待网络初始化完成,然后再启动您的网络代码。 不知道暂停/恢复的中间件库(如 XSAPI、GameChat2 和 Azure PlayFab Party)必须在暂停时进行清理,并在等待网络初始化后在恢复时重新初始化。
测试网络初始化
在恢复和游戏启动期间进行网络初始化通常都需要几秒钟时间,具体取决于主机类型和用户的网络环境。 在开发过程中,网络初始化几乎是瞬时完成的。 这可能会隐藏游戏各个部分中导致无法正确等待网络初始化的问题。 要测试网络初始化方案,可以使用 xbconfig NetworkInitDelayInSeconds=30
在网络初始化过程中添加任意延迟。 使用此设置时,请确保在两次测试之间使用 xbapp terminate /full
充分重新启动您的游戏。 完成测试后,将 NetworkInitDelayInSeconds
设置回 0
。
网络初始化代码示例
下列代码示例说明如何以实时安全的方式进行轮询,以了解网络是否已初始化。
bool IsNetworkInitialized()
{
XNetworkingConnectivityHint connectivityHint;
if (SUCCEEDED(XNetworkingGetConnectivityHint(&connectivityHint)))
{
return connectivityHint.networkInitialized;
}
return false;
}
以下代码示例说明在网络初始化完成之前,如何阻止游戏。
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;
}
网络信息
可以使用 XNetworkingGetConnectivityHint API 来检索 Microsoft 游戏开发工具包(GDK)游戏中的网络信息。
XNetworkingGetConnectivityHint API 返回设备范围的有关网络连接级别、数据限制、有线和无线连接类型以及网络是否初始化等信息。 它是一种可立即返回最新信息的实时安全 API。 您可以使用 XNetworkingRegisterConnectivityHintChanged 和 XNetworkingUnregisterConnectivityHintChanged 函数来侦听更改。
以下代码示例演示如何使用 XNetworkingGetConnectivityHint 函数查询有关当前网络状态的信息。
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 结构中的字段(而不是 XNetworkingConnectivityHint::networkInitialized
字段)是提示内容。 它们是设备对网络当前状态的尽可能猜测。 这是基于在设备上观察到的网络流量的启发。
XNetworkingConnectivityLevelHint
的状态表示常规的网络级别近似值,用于简化游戏的连接逻辑。 游戏可预期 XNetworkingConnectivityLevelHint::None
反映网络媒体断开连接,以及在稳定状态下(网络环境在几分钟内没有变化的情况下)普遍缺乏连接性的情况。 其他状态不表示是否存在到特定游戏终结点的连接。
因此,建议在等到网络初始化完成后,无论 XNetworkingConnectivityHint::connectivityLevelHint
字段的状态如何,都使用 WinSock
和/或 WinHTTP
尝试建立到终结点的连接。 如果这些 API 后来失败了,我们建议您将 XNetworkingGetConnectivityHint API 用于其他 UI 和诊断报告目的。 然后,您应等到网络连接级别发生变化之后,再重试。
检索高级网络信息
大多数 Microsoft 游戏开发工具包(GDK)游戏都应将 XNetworkingGetConnectivityHint API 与 WinSock
API 结合使用,来检索网络状态和基本网络信息(例如 IP 地址)。 如果您需要更多信息,GDK 中提供了低级别 IP 帮助程序 API。
一般情况下,在 Microsoft 游戏开发工具包(GDK)中使用 IP Helper
API 的方式与在 Win32 程序中使用此 API 的方式相同。
在您的源文件中,
#include <iphlpapi.h>
位于#include <winsock2.h>
的后面。代替直接链接
Ws2_32.lib
和Iphlpapi.lib
,链接XGamePlatform.lib
。
只有 WINAPI\_PARTITION\_GAMES
API 系列下的 API 才能用于 Microsoft 游戏开发工具包(GDK)游戏。
在 Xbox 主机上,由于基础平台抽象,使用 IP Helper
API时某些信息不准确。 这包括但不限于以下各项:
- MAC 地址将始终为
AA-AA-AA-AA-AA-AA
。 - 无论基本网络连接类型是什么,所有接口都将始终报告有线接口。 只能通过 XNetworkingGetConnectivityHint 检索真实接口类型。
不支持的网络连接 API
Microsoft 游戏开发工具包(GDK)游戏不支持以下网络连接 API,而是应改用 XNetworkingGetConnectivityHint API 来确定网络连接。
另请参阅
XNetworkingGetConnectivityHint
XNetworkingRegisterConnectivityHintChanged