2009-10-24 120 views
46

我目前正在研究一个UDP套接字应用程序,并且我需要构建支持以便IPV4和IPV6连接可以将数据包发送到服务器。如何同时支持IPv4和IPv6连接

我希望有人能帮助我,并指出我正确的方向;我发现的大部分文档都不完整。如果你能指出Winsock和BSD套接字之间的任何区别,这也会很有帮助。

在此先感谢!

回答

72

最好的方法是创建一个也可以接受IPv4连接的IPv6服务器套接字。为此,请创建一个常规IPv6套接字,将关闭套接字选项IPV6_V6ONLY,将其绑定到“任何”地址并开始接收。 IPv4地址将以IPv4-mapped格式显示为IPv6地址。

不同系统的主要区别在于IPV6_V6ONLY是否可用,以及b)默认情况下是打开还是关闭。它在Linux上默认关闭(即允许没有setsockopt的双栈套接字),并在大多数其他系统上打开。

另外,Windows XP上的IPv6堆栈不支持该选项。在这些情况下,您将需要创建两个单独的服务器套接字,并将它们放入select或多个线程中。

+1

感谢您的这些信息,正是我一直在寻找的。 – Charles

+7

说在Linux上默认关闭IPV6_V6ONLY是错误的:它取决于操作系统,而不仅仅是内核。例如,在Debian GNU/Linux上,它最近默认开启。 – bortzmeyer

+1

默认情况下,OS X也将其关闭,但最好的办法是始终明确设置它。毕竟,本地系统管理员可能已经改变了它。 –

6

套接字API由IETF RFC控制,并且在包括windows WRT IPv6在内的所有平台上都应该相同。

对于IPv4/IPv6应用程序,它是全部getaddrinfo()getnameinfo()getaddrinfo是一个天才 - 查看客户端的DNS,端口名称和功能以解决永久性问题“我可以使用IPv4,IPv6还是两者都可以到达特定目的地?”或者如果您要进入双栈路由并且希望它返回IPv4映射的IPv6地址,它也会这样做。

它提供了一个直接sockaddr *结构可以插入bind()recvfrom()sendto()和地址族socket() ......在许多情况下,这意味着没有凌乱sockaddr_in(6)结构填写和处理。

对于UDP实现,我会小心设置双栈套接字,或者更一般地说,绑定到所有接口(INADDR_ANY)。一个典型的问题是,当地址没有被锁定(见bind())到特定的接口,并且系统有多个接口请求时,根据OS路由表的意思,响应可能会从具有多个地址的计算机的不同地址传输,从而混淆了应用程序协议 - 尤其是任何具有认证要求的系统。

对于不存在问题的UDP实现(或TCP),双栈套接字可以节省大量的时间,这是在启用系统时启用IPv *。必须小心谨慎,不要完全依赖双堆栈,因为不缺乏合理的平台(旧Linux,BSD,Windows 2003),而这些平台部署的IPv6堆栈不支持双堆栈套接字。

2

RFC没有真正指定IPV6_V6ONLY套接字选项的存在,但是,如果它不存在,RFC很清楚实现应该如同该选项为FALSE。

如果选项存在,我会争辩说它应该默认为FALSE,但由于理解的理由,BSD和Windows实现默认为TRUE。有一种奇怪的说法认为这是一个安全问题,因为一个不知情的IPv6程序员可能会认为他们仅仅绑定到IN6ADDR_ANY上,并且意外地接受导致安全问题的IPv4连接。我认为这是一个既牵强又荒谬的事情,除了令任何期望达到RFC标准的人感到意外之外。

在Windows的情况下,non-compiance通常不会是一个惊喜。在BSD的情况下,这是不幸的。

+3

IPv6 API的标准RFC 3493在其5.3节描述了IPV6_V6ONLY,如果你想读取所有的细节。 – bortzmeyer

3

我一直在Windows下玩这个,它实际上似乎是一个安全问题,如果你绑定到环回地址,那么IPv6套接字被正确绑定到[:: 1],但映射的IPv4套接字被绑定到INADDR_ANY,所以你的(应该是)安全的本地应用程序实际上暴露于世界。