简体   繁体   English

如何同时支持 IPv4 和 IPv6 连接

[英]How to support both IPv4 and IPv6 connections

I'm currently working on a UDP socket application and I need to build in support so that IPV4 and IPV6 connections can send packets to a server.我目前正在开发 UDP 套接字应用程序,我需要构建支持,以便 IPV4 和 IPV6 连接可以将数据包发送到服务器。

I was hoping that someone could help me out and point me in the right direction;我希望有人可以帮助我并指出正确的方向; the majority of the documentation that I found was not complete.我发现的大部分文档都不完整。 It'd also be helpful if you could point out any differences between Winsock and BSD sockets.如果您能指出 Winsock 和 BSD sockets 之间的任何差异,那也会很有帮助。

Thanks in advance!提前致谢!

The best approach is to create an IPv6 server socket that can also accept IPv4 connections. 最好的方法是创建一个也可以接受IPv4连接的IPv6服务器套接字。 To do so, create a regular IPv6 socket, turn off the socket option IPV6_V6ONLY , bind it to the "any" address, and start receiving. 为此,请创建常规IPv6套接字, 关闭套接字选项IPV6_V6ONLY ,将其绑定到“任何”地址,然后开始接收。 IPv4 addresses will be presented as IPv6 addresses, in the IPv4-mapped format. IPv4地址将以IPv4映射格式显示为IPv6地址。

The major difference across systems is whether IPV6_V6ONLY is a) available, and b) turned on or off by default. 跨系统的主要区别在于IPV6_V6ONLY是否a)可用,b)默认情况下是打开还是关闭。 It is turned off by default on Linux (ie allowing dual-stack sockets without setsockopt), and is turned on on most other systems. 它默认在Linux上关闭(即允许没有setsockopt的双栈套接字),并在大多数其他系统上打开。

In addition, the IPv6 stack on Windows XP doesn't support that option. 此外,Windows XP上的IPv6堆栈不支持该选项。 In these cases, you will need to create two separate server sockets, and place them into select or into multiple threads. 在这些情况下,您需要创建两个单独的服务器套接字,并将它们放入select或多个线程中。

The socket API is governed by IETF RFCs and should be the same on all platforms including windows WRT IPv6. 套接字API由IETF RFC管理,在所有平台上都应该相同,包括Windows WRT IPv6。

For IPv4/IPv6 applications it's ALL about getaddrinfo() and getnameinfo() . 对于IPv4 / IPv6应用程序,它是关于getaddrinfo()getnameinfo()全部内容。 getaddrinfo is a genius - looks at DNS, port names and capabilities of the client to resolve the eternal question of “can I use IPv4, IPv6 or both to reach a particular destination?” Or if you're going the dual-stack route and want it to return IPv4-mapped IPv6 addresses, it will do that too. getaddrinfo是一个天才 - 查看客户端的DNS,端口名称和功能,以解决“我可以使用IPv4,IPv6或两者到达特定目的地吗?”的永恒问题。或者,如果您要使用双栈路由,希望它返回IPv4映射的IPv6地址,它也会这样做。

It provides a direct sockaddr * structure that can be plugged into bind() , recvfrom() , sendto() and the address family for socket() … In many cases this means no messy sockaddr_in(6) structures to fill out and deal with. 它提供了一个直接的sockaddr *结构,可以插入bind()recvfrom()sendto()socket()的地址族......在很多情况下,这意味着没有凌乱的sockaddr_in(6)结构来填写和处理。

For UDP implementations I would be careful about setting dual-stack sockets or, more generally, binding to all interfaces ( INADDR_ANY ). 对于UDP实现,我会小心设置双栈套接字,或者更一般地说,绑定到所有接口( INADDR_ANY )。 The classic issue is that, when addresses are not locked down (see bind() ) to specific interfaces and the system has multiple interfaces requests, responses may transit from different addresses for computers with multiple addresses based on the whims of the OS routing table, confusing application protocols—especially any systems with authentication requirements. 经典的问题是,当地址未被锁定(请参阅bind() )到特定接口并且系统有多个接口请求时,响应可能会根据OS路由表的异想天开从具有多个地址的计算机的不同地址传输,令人困惑的应用程序协议 - 尤其是任何具有身份验证要

For UDP implementations where this is not a problem, or TCP, dual stack sockets can save a lot of time when IPv*-enabling your system. 对于没有问题的UDP实现或TCP,双堆栈套接字可以在使用IPv *启用系统时节省大量时间。 One must be careful to not rely entirely on dual-stack where it`s not absolutely necessary as there are no shortage of reasonable platforms (Old Linux, BSD, Windows 2003) deployed with IPv6 stacks not capable of dual stack sockets. 必须小心不要完全依赖双栈,因为它并不是绝对必要的,因为没有合理的平台(旧Linux,BSD,Windows 2003)部署IPv6堆栈不能兼容双栈。

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

The RFCs don't really specify the existence of the IPV6_V6ONLY socket option, but, if it is absent, the RFCs are pretty clear that the implementation should be as though that option is FALSE. RFC并没有真正指定IPV6_V6ONLY套接字选项的存在,但是,如果它不存在,则RFC非常清楚,实现应该就像该选项是FALSE一样。

Where the option is present, I would argue that it should default FALSE, but, for reasons passing understanding, BSD and Windows implementations default to TRUE. 如果选项存在,我认为它应该默认为FALSE,但是,出于理解的原因,BSD和Windows实现默认为TRUE。 There is a bizarre claim that this is a security concern because an unknowing IPv6 programmer could bind thinking they were binding only to IN6ADDR_ANY for only IPv6 and accidentally accept an IPv4 connection causing a security problem. 有一个奇怪的说法是这是一个安全问题,因为一个不知情的IPv6程序员可以认为他们只绑定到IN6ADDR_ANY只有IPv6并且意外接受IPv4连接导致安全问题。 I think this is both far-fetched and absurd in addition to a surprise to anyone expecting an RFC-compliant implementation. 我认为这对于任何期望符合RFC的实现的人来说都是一个既牵强附会也是荒谬的。

In the case of Windows, non-compiance won't usually be a surprise. 在Windows的情况下,非竞争通常不会是一个惊喜。 In the case of BSD, this is unfortunate at best. 就BSD而言,这是不幸的。

As Craig M. Brandenburg observes, getaddrinfo does all the heavy lifting to make dual IPv4/IPv6 possible.正如 Craig M. Brandenburg 所观察到的,getaddrinfo 完成了所有繁重的工作以使双 IPv4/IPv6 成为可能。 I have an experimental server and client on my localhost.我的本地主机上有一个实验服务器和客户端。 I use this in the server:我在服务器中使用它:

hints.ai_family = AF_INET6;
hints.ai_socktype = SOCK_DGRAM;
hints.ai_flags = AI_PASSIVE;
...

The client can then connect to the server using any kind of address:然后客户端可以使用任何类型的地址连接到服务器:

hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM;

host_port = "4950"; // whatever

// All of these work.
host_ip = "127.0.0.1";        // Pure IPv4 address
host_ip = "::ffff:127.0.0.1"; // IPv4 address expressed as IPv6
host_ip = "::1";              // Pure IPv6 address
host_ip = "localhost";        // Domain name

int rv = getaddrinfo(host_ip, host_port, &hints, &result);
...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM