用于 IPv6 Winsock 应用程序的 Dual-Stack 套接字

为了在 Windows XP Service Pack 1 (SP1) 和 Windows Server 2003 上同时支持 IPv4 和 IPv6,应用程序必须创建两个套接字,一个套接字用于 IPv4,一个套接字用于 IPv6。 这两个套接字必须由应用程序单独处理。

Windows Vista 及更高版本提供了创建单个 IPv6 套接字的功能,该套接字可以同时处理 IPv6 和 IPv4 流量。 例如,创建 IPv6 的 TCP 侦听套接字,将其置于双堆栈模式,并绑定到端口 5001。 此双栈套接字可以接受来自连接到端口 5001 的 IPv6 TCP 客户端和连接到端口 5001 的 IPv4 TCP 客户端的连接。 此功能允许大大简化应用程序设计,并减少在两个单独的套接字上发布作所需的资源开销。

创建 Dual-Stack Socket

默认情况下,在 Windows Vista 及更高版本上创建的 IPv6 套接字仅通过 IPv6 协议运行。 为了将 IPv6 套接字转换为双堆栈套接字,必须在套接字绑定到 IP 地址之前,使用 IPV6_V6ONLY 套接字选项调用 setsockopt 函数,以将此值设置为零。 当 IPV6_V6ONLY 套接字选项设置为零时,为 AF_INET6 地址系列创建的套接字可用于向 IPv6 地址或 IPv4 映射地址发送和接收数据包。

具有 Dual-Stack 套接字的 IP 地址

双栈套接字始终需要 IPv6 地址。 与 IPv4 地址交互的能力需要使用 IPv4 映射的 IPv6 地址格式。 任何 IPv4 地址必须以 IPv4 映射的 IPv6 地址格式表示,以支持仅使用 IPv6 的应用程序与 IPv4 节点通信。 IPv4 映射的 IPv6 地址格式支持将 IPv4 节点的 IPv4 地址表示为 IPv6 地址。 IPv4 地址可编码为低序 32 位 IPv6 地址,并且高序 96 位将保留固定前缀 0:0:0:0:0:FFFF。 IPv4 映射的 IPv6 地址格式是在 RFC 4291 中指定的。 有关详细信息,请参阅 www.ietf.org/rfc/rfc4291.txt。 “Mstcpip.h”中的 IN6ADDR_SETV4MAPPED 宏可用于将 IPv4 地址转换为所需的 IPv4 映射的 IPv6 地址格式。

如果底层协议实际上是 IPv4,则 IPv4 地址将映射到 IPv4 映射的 IPv6 地址格式。 也就是说, SOCKADDR 结构中的 family 字段表示AF_INET6,但 IPv4 映射的 IPv6 地址在 IPv6 地址结构中编码。 对于处于侦听模式的双堆栈套接字,这意味着任何接受的 IPv4 连接都将返回 IPv4 映射的 IPv6 地址。 对于连接到 IPv4 目标的双堆栈套接字,传递给 connect 的 SOCKADDR 结构必须是 IPv4 映射的 IPv6 地址。 应用程序必须注意适当地处理这些 IPv4 映射的 IPv6 地址,并且仅将它们与双堆栈套接字一起使用。 如果要将 IP 地址传递到常规 IPv4 套接字,则该地址必须是常规 IPv4 地址,而不是 IPv4 映射的 IPv6 地址。

使用 Dual-Stack 套接字的潜在问题

应用程序的一个潜在陷阱是在双栈套接字上获取 IPv4 映射的 IPv6 地址,然后尝试在不同的仅限 IPv6 的套接字上使用返回的 IP 地址。 例如,在双堆栈套接字上使用时, getsocknamegetpeername 函数可以返回 IPv4 映射的 IPv6 地址。 如果返回的 IPv4 映射的 IPv6 地址随后在未设置为双堆栈的其他套接字上使用(仅 IPv6 套接字,这是创建套接字时的默认行为),则将此仅 IPv6 套接字与 IPv4 映射的 IPv6 地址一起使用将失败。 IPv4 映射的 IPv6 地址格式只能在双栈套接字上使用。

在双堆栈数据报套接字上,如果应用程序要求 LPFN_WSARECVMSG (WSARecvMsg) 函数在 WSAMSG 结构中为通过 IPv4 接收的数据报返回数据包信息,则必须在套接字上 IP_PKTINFO 套接字选项设置为 true。 如果在套接字上仅将 IPV6_PKTINFO 选项设置为 true,则将为通过 IPv6 接收的数据报提供包信息,但可能不会为通过 IPv4 接收的数据报提供包信息。

如果应用程序尝试在双堆栈数据报套接字上设置 IP_PKTINFO 套接字选项,并且在系统上禁用了 IPv4,则 setsockopt 函数将失败, 并且 WSAGetLastError 将返回 WSAEINVAL 错误。 由于其他错误, setsockopt 函数也会返回相同的错误。 如果应用程序尝试在双堆栈套接字上设置 IPPROTO_IP 级别套接字选项,并且失败并显示 WSAEINVAL,则应用程序应确定是否在本地计算机上禁用了 IPv4。 可用于检测 IPv4 是否启用或禁用的一种方法是调用 socket 函数, 并将 af 参数设置为 AF_INET 以尝试创建 IPv4 套接字。 如果 套接字 函数失败并且 WSAGetLastError 返回 WSAEAFNOSUPPORT 错误,则表示未启用 IPv4。 在这种情况下,应用程序可以忽略尝试设置 IP_PKTINFO 套接字选项时的 setsockopt 函数失败。 否则,尝试设置 IP_PKTINFO 套接字选项时失败应被视为意外错误。

对于使用 WSASendMsg 函数发送数据报时的双堆栈套接字,并且应用程序想要指定要使用的特定本地 IP 源地址,处理此问题的方法取决于目标 IP 地址。 发送到 IPv4 目标地址或 IPv4 映射的 IPv6 目标地址时,在 lpMsg 参数指向的 WSAMSG 结构中传递的控制数据对象之一应包含一个 in_pktinfo 结构,其中包含用于发送的本地 IPv4 源地址。 发送到不是 IPv4 映射的 IPv6 地址的 IPv6 目标地址时,在 lpMsg 参数指向的 WSAMSG 结构中传递的控制数据对象之一应包含一个 in6_pktinfo 结构,其中包含用于发送的本地 IPv6 源地址。

适用于 Windows 套接字应用程序的 IPv6 指南

更改 IPv6 Winsock 应用程序的数据结构

IPv6 Winsock 应用程序的函数调用

使用硬编码的 IPv4 地址

IPv6 Winsock 应用程序的用户界面问题

IPv6 Winsock 应用程序的基础协议

getpeername

getsockname

in_pktinfo

in6_pktinfo

IP_PKTINFO

IPV6_PKTINFO

setsockopt

LPFN_WSARECVMSG (WSARecvMsg)

WSASendMsg