如何同时支持 IPv4 和 IPv6 连接

发布于 2024-08-08 18:07:50 字数 160 浏览 9 评论 0原文

我目前正在开发 UDP 套接字应用程序,需要构建支持,以便 IPV4 和 IPV6 连接可以将数据包发送到服务器。

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

提前致谢!

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.

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.

Thanks in advance!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

靑春怀旧 2024-08-15 18:07:50

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

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

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

The best approach is to create an IPv6 server socket that can also accept IPv4 connections. To do so, create a regular IPv6 socket, turn off the socket option IPV6_V6ONLY, bind it to the "any" address, and start receiving. IPv4 addresses will be presented as IPv6 addresses, in the IPv4-mapped format.

The major difference across systems is whether IPV6_V6ONLY is a) available, and b) turned on or off by default. It is turned off by default on Linux (i.e. allowing dual-stack sockets without setsockopt), and is turned on on most other systems.

In addition, the IPv6 stack on Windows XP doesn't support that option. In these cases, you will need to create two separate server sockets, and place them into select or into multiple threads.

寄意 2024-08-15 18:07:50

套接字 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())到特定接口并且系统有多个接口请求时,对于具有多个地址的计算机,响应可能会根据不同的地址从不同的地址传输。操作系统路由表的反复无常,使应用程序协议变得混乱——尤其是任何具有身份验证要求的系统。

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

The socket API is governed by IETF RFCs and should be the same on all platforms including windows WRT IPv6.

For IPv4/IPv6 applications it's ALL about getaddrinfo() and 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.

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.

For UDP implementations I would be careful about setting dual-stack sockets or, more generally, binding to all interfaces (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.

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. 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.

み格子的夏天 2024-08-15 18:07:50

我一直在 Windows 下玩这个,它实际上似乎是一个安全问题,如果您绑定到环回地址,则 IPv6 套接字正确绑定到 [::1] 但映射的 IPv4 套接字绑定到 INADDR_ANY ,因此您的(据称)安全的仅限本地的应用程序实际上已暴露给全世界。

I've been playing with this under Windows and it actually does appear to be a security issue there, if you bind to the loopback address then the IPv6 socket is correctly bound to [::1] but the mapped IPv4 socket is bound to INADDR_ANY, so your (supposedly) safely local-only app is actually exposed to the world.

忘东忘西忘不掉你 2024-08-15 18:07:50

RFC 并没有真正指定 IPV6_V6ONLY 套接字选项的存在,但是,如果不存在该选项,RFC 非常清楚,实现应该就像该选项为 FALSE 一样。

如果存在该选项,我认为它应该默认为 FALSE,但是,由于无法理解的原因,BSD 和 Windows 实现默认为 TRUE。有一种奇怪的说法认为这是一个安全问题,因为不知情的 IPv6 程序员可能会认为他们只绑定到 IPv6 的 IN6ADDR_ANY 并意外接受 IPv4 连接,从而导致安全问题。我认为这既牵强又荒谬,而且会让任何期待符合 RFC 实现的人感到惊讶。

就 Windows 而言,不合规通常不会令人意外。对于 BSD 来说,这充其量是不幸的。

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.

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. 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. I think this is both far-fetched and absurd in addition to a surprise to anyone expecting an RFC-compliant implementation.

In the case of Windows, non-compiance won't usually be a surprise. In the case of BSD, this is unfortunate at best.

妳是的陽光 2024-08-15 18:07:50

正如 Craig M. Brandenburg 所观察到的那样,getaddrinfo 完成了所有繁重的工作,使双 IPv4/IPv6 成为可能。我的本地主机上有一个实验服务器和客户端。我在服务器中使用它:

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

然后客户端可以使用任何类型的地址连接到服务器:

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);
...

As Craig M. Brandenburg observes, getaddrinfo does all the heavy lifting to make dual IPv4/IPv6 possible. 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);
...
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文