多年来,我使用 WinSock(路由器、Web/邮件/FTP 服务器等...等)为 Windows 开发了少量 C++ 服务器/客户端应用程序。
我开始越来越多地考虑创建这些应用程序的 IPv6 版本(当然,同时也要保持原始 IPv4 版本)。
问题:
- 我可能会遇到什么陷阱?
- 移植/转换困难吗?
- 转换值得吗?
作为参考(或为了好玩),您可以在我的应用程序核心中窥探IPv4 代码的峰值。
getaddrinfo和getnameinfo是您的朋友。在您寻求在现有应用程序中提供 IPv4 和 IPv6 支持的过程中,我建议他们尽可能成为您最好的朋友。
如果通过添加 IPv6 支持做得好,您最终也会将系统抽象到一个未知的未来 IP 协议可以在不修改代码的情况下运行的地步。
通常在连接时,您会填写套接字结构、端口、地址族、IP 地址、将地址/端口转换为网络字节顺序等。
随着getaddrinfo
您发送 IP 地址或主机名和端口或端口名称,它会返回一个链接列表,其中包含可以直接传递到socket()
and的结构和所有内容connect()
。
getaddrinfo
对于使用这两种 IP 协议至关重要,因为它知道主机是否具有 IPv6 或 IPv4 连接,并且它通过查看 DNSAAAA
与A
记录并动态确定哪些协议可用于为特定连接提供服务来知道对等方是否也这样做要求。
我强烈建议不要使用特定于 IP 版本的inet_pton()
或inet_addr()
类似设备。在 Windows 平台上,特别inet_pton()
是与早期版本的 MS Windows(XP、2003 等)不兼容,除非您自己推出。还建议不要使用 IPv4 和 IPv6 的单独版本......这作为技术解决方案是不可行的,因为在不久的将来这两种协议将需要同时使用,人们可能不知道提前使用哪个协议。套接字接口是抽象的,通过尝试创建 IPv6 套接字或尝试为侦听器设置 IPv6 双堆栈套接字选项,很容易检测到双堆栈或 IPv6 支持。没有理由生成的应用程序不会在不支持或不了解 IPv6 的系统上运行。
对于传出连接,请使用PF_UNSPEC
ingetaddrinfo
以便在进行传出连接时为您选择地址系列。恕我直言,这比双栈方法更好,因为它允许不支持双栈的平台工作。
对于传入的连接,如果设计合理,您可以单独绑定 IPv4/IPv6 套接字,或者如果您不能做单独的侦听器,则可以使用双栈。当使用双栈getnameinfo
返回 IPv4 地址的 IPv6 地址时,恕我直言,这最终毫无用处。一个小的实用程序可以将字符串转换为普通的 IPv4 地址。
根据我的经验,如果做得好,您已经删除了对特定 IP 版本的依赖,并且最终得到的套接字管理代码比您开始时要少。
大约一年前,我 在我以前只支持 IPv4 的网络库中添加了 IPv6 支持,我并没有发现这样做非常困难或痛苦。
唯一的大区别是您如何存储 IP 地址:
在IPv4中,您将它们存储为sockaddr_in
's(或者如果您像我一样顽皮,则存储为 uint32_t's)。
对于IPv6,您需要将它们存储为sockaddr_in6
's(或一些等效的 128 位结构)。
一个好的转换前步骤是检查您的代码并找到当前存储IPv4地址的所有位置,并将它们抽象为通用 IP 地址类,该类稍后可以在内部重新实现为IPv4 地址或IPv6 地址。
然后重新测试以确保在IPv4模式下没有任何问题......一旦检查出来,您应该只需进行一些更改(主要更改为、to等......)即可切换到IPv6 。PF_INET
PF_INET6
inet_aton()
inet_pton()
默认情况下,我的库仍然仅作为 IPv4 提供,但可以选择定义预处理器宏 ( ) 以在IPv6 感知模式下-DMUSCLE_USE_IPV6
重新编译它。
这样它仍然可以在不支持 IPv6 的系统上编译。我在此过程中发现的一个非常有用的特性是IPv4 映射的 IPv6 地址:通过指定其中一个(本质上是一个带有前缀的 IPv4 地址),您将获得一个可以同时与 IPv4 和 IPv6 通信的套接字,因此服务器可以同时与 IPv4 和 IPv6 客户端通信,无需为所有内容编写单独的 IPv4 和 IPv6 代码路径。0xFFFF
至于是否值得付出努力,这实际上取决于您打算对代码做什么。如果没有别的,我会说这是一次很好的教育体验,它确实允许您的软件在 IPv6 环境中使用,随着时间的推移,这将变得更加普遍。
glibc 的维护者 Ulrich Drepper 有一篇关于该主题的好文章,
http://people.redhat.com/drepper/userapi-ipv6.html
但不要忘记 Richard Steven 的书Unix Network Programming, Volume 1: The Sockets Networking API的良好实践。
查看一些已实现 IPv6 的开源项目的更改日志。其中大部分是 Unix 代码,但 Winsock 与 BSD 套接字非常相似。
Exim、Courier、Squid、Apache、BIND DNS 是一些开始寻找的地方。