为了在 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 地址。 例如,在双堆栈套接字上使用时, getsockname 或 getpeername 函数可以返回 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 指南