Skip to main content
Microsoft 游戏开发工具包 (GDK) 支持使用 Windows Sockets 2 (Winsock) API。 Windows Sockets 2 (Winsock) 使你能够构建高级的 Internet、内网及其他可联网的应用程序,从而在网络上传输应用数据,且独立于所使用的网络协议。 Microsoft 游戏开发工具包 (GDK) 游戏与 Winsock 的交互方式总体上与 Win32 程序与 Winsock 的交互方式相同。 本主题介绍在 Microsoft 游戏开发工具包 (GDK) 游戏中使用 Winsock 时的一些细微差异和最佳实践。

设置

本节描述在使用 Winsock API 时需要包含哪些 .h 和 .lib 文件。
  • 在源文件中添加 #include <winhttp.h>
  • 对于 PC 游戏,继续链接 Winhttp.lib
  • 对于 XBOX 主机游戏,必须链接 XGamePlatform.lib,而不是直接链接 Winhttp.lib
在 Microsoft 游戏开发工具包 (GDK) 游戏中,只有 WINAPI_PARTITION_GAMES API 系列下的 API 可用。

网络初始化

在首次调用 WSAStartup 之前,Microsoft 游戏开发工具包 (GDK) 游戏必须确保网络堆栈已就绪。如果在游戏启动过程中过早调用 WSAStartup,则 WSAStartup 或后续的 Winsock 调用可能会失败。有关如何判断网络堆栈就绪的详细信息,请参阅 网络初始化与连接性

挂起与恢复

你应通过 RegisterAppStateChangeNotification 注册挂起与恢复事件。挂起时,应关闭所有套接字句柄并调用 WSACleanup。恢复时,应再次等待 网络初始化 完成,然后调用 WSAStartup 并创建新的套接字。

Microsoft 游戏开发工具包 (GDK) 首选本地用户数据报协议 (UDP) 多人游戏端口 API

Microsoft 游戏开发工具包 (GDK) 的 首选本地 UDP 多人游戏端口 API 返回一个最佳的、动态选择的端口,游戏应使用 Winsock 在此端口上 bind 以便通过 UDP 进行游戏内通信。Microsoft 游戏开发工具包 (GDK) 平台确保这一特定端口最有可能适用于每个用户的具体网络环境。使用该端口能最大程度地利用平台的客户支持与诊断流程,提升标准化 NAT (网络地址转换) 兼容性,提供标准化的 UPnP™ 认证设备功能,并让 QoS 路由器与 ISP 算法将数据包识别为实时敏感数据。 UDP 端口对依赖点对点网络拓扑的游戏特别相关。它是唯一允许入站 UDP 数据包在不进行防火墙打洞的情况下穿过防火墙的端口。依赖点对点网络拓扑的 Microsoft 游戏开发工具包 (GDK) 游戏仍需自行提供公共 IP 地址和端口发现能力,并为具有中等或严格 NAT 类型的客户端提供 NAT 打洞方案。首选端口本身可提升这些技术的成功率,但不能替代它们。 使用客户端/服务器网络拓扑的游戏也能受益于该 UDP 端口。故障排查、UPnP™ 和数据包识别对于医院、酒店和大学宿舍中常见的强制门户及其他基于源的过滤方式仍然适用。 我们强烈建议 Microsoft 游戏开发工具包 (GDK) 游戏在其主要多人游戏和聊天网络流量上使用首选本地 UDP 多人游戏端口。

套接字的安全性

Microsoft 游戏开发工具包 (GDK) 游戏负责为通过 Winsock API 在网络上传输的所有数据确保适当的安全性和加密。 BCryptWinCryptschannel 以及其他标准 Windows API 提供了推荐的加密原语。Microsoft 游戏开发工具包 (GDK) 游戏应使用这些 API 为所有套接字流实现 DTLS (Datagram Transport Layer Security) 及其他标准化的安全协议。 对于不希望实现自有安全模型的游戏,Microsoft 游戏开发工具包 (GDK) 的 PlayFab Party 库提供了完整、集成的套接字安全方案以及其他多项功能。

套接字内存注意事项

Microsoft 游戏开发工具包 (GDK) 游戏当前的所有 WinSock 与 WinHTTP 用途大约只有 16MB 内核可用内存,一旦超出该值系统可能变得不稳定。最主要的内存消耗来源之一是 WinSock 内核态发送与接收内存池,进入或发出的数据包会存储在其中,直到它们能被送上网络线路或通过众多 recv 变体之一被传送到游戏的用户态内存。 尤其在高带宽场景下,游戏必须以至少与远端对等方或服务器推送速率相当的速度、低延迟地把数据传送到自己的用户态缓冲区中。要实现这一点有几种复杂度和保障程度不同的方法,但下面所有建议的核心目标都是:始终保持有一个用户态缓冲区处于挂起等待状态,以便一旦数据到达就能立即被转移,这样内核就无需持续分配更多内存。最终效果是把待处理数据的内存使用从极为有限的内核池转移到由你自己在游戏更大的内存池中直接管理。 使用 WinSock 的高层 API 通常都有各自控制套接字内核内存使用的机制。WinHTTPXCurl 都允许通过各自的读取通知机制控制内核内存使用;而 XSAPIPlayFab Party 等其他 API 则通过下文所述技术尽量减少其内核内存使用。

Berkeley (BSD) 套接字

如果你想继续使用阻塞式 Berkeley 套接字 API,需要提高 recv 调用的频率。建议使用一个专用线程配合紧凑的循环,将收到的数据入队交由别处处理。理想情况下该线程始终阻塞在 recv 调用中;任何在 recv 之外花费的时间都可能是内核内存增长的时间,因为它正等着你再次调用 recv。经常出现的情况是:如果在再次调用 recv 之前就试图处理数据,游戏就可能跟不上,只要数据继续以高速率到来,内核内存使用量就会增长。我们还建议指定至少 8k 的缓冲区大小,以与内核缓冲区边界对齐,并有助于抵御抖动和突发发送模式。

WinSock 重叠 I/O

WinSock 的重叠 I/O 允许你异步保持一个用户态接收缓冲区处于挂起状态。它还允许内核直接使用你的内存缓冲区,避免了额外的内存拷贝(与 Berkeley 套接字范式不同),并且只要总有缓冲区处于挂起状态,内核就完全不需要为接收数据额外分配内存。此外,使用重叠 I/O 时你可以把 SO_RCVBUF 和 SO_SNDBUF 设为特殊值 0,这样几乎完全不会分配发送/接收相关的内核内存。 在几乎所有场景中,此方法都是管理套接字内存使用的最佳方式。

Registered I/O

Registered I/O 是一种复杂的网络 API,它可为你带来最低的延迟并保证发送与接收操作不使用任何内核内存。它允许你设置多个直接被内核使用的接收/发送缓冲区,以确保始终有缓冲区可用来接收数据。

最大 UDP 传输单元大小

尽管 Microsoft 游戏开发工具包 (GDK) 中存在一个理论最大有效负载大小,但在实际中,特定连接的最大值取决于网络连接类型,并且可能在游戏运行时发生变化。与其试图在游戏运行时确定实际 MTU (Maximum Transmission Unit) 并对其变化做出反应,不如在网络代码中一律按每个数据包 1,384 字节的最大 UDP 有效负载来设计。此值在所有网络配置下均可安全使用,可避免传输中的分片。无论套接字类型为 IPv4 还是 IPv6,都建议这么做。 传输超过 1,384 字节的有效负载通常需要 IP 层数据包分片。ISP 以及用户家庭路由器和设备对 IP 数据包分片的支持并不好。在这些网络配置下,IP 数据包分片不会导致 Winsock API 报错,而是会在游戏侧表现为丢包。为避免 IP 层数据包分片,请以 1,384 字节作为数据包有效负载的安全最大值。 为确保游戏避免分片,并帮助游戏满足与多人游戏最低网络要求相关的 XBOX 要求,应对游戏打开的每个套接字应用套接字选项 IP_DONTFRAGMENTIP_USER_MTU。这些标志仅在 XGameRuntimeIsFeatureAvailable(XGameRuntimeFeature::XNetworking) API 返回 true 时可用于 Microsoft 游戏开发工具包 (GDK) 游戏。如果 XNetworking 功能不可用,则 setsockopt 调用会失败。以下示例展示了如何设置这两个套接字选项。

另请参阅

最后修改于 2026年8月24日