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

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

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

创建Dual-Stack套接字

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

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

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

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

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

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

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

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

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

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