在基于 BSD 和 BSD 的系统上(例如包括 MacOS X),作用域 ID 嵌入到地址本身中,以作为第二个 16 位字的链接本地地址。请参考FreeBSD 手册并搜索“8.1.1.3 Scope Index”(不带引号)。
因此,假设 intf1 的范围 ID 为 1,而 intf2 的范围 ID 为 2,inet_pton()
将在这些平台上按如下方式转换字符串:
"fe80::1234%intf1" -> fe80:1::1234
"fe80::1234%intf2" -> fe80:2::1234
"fe80::1234" -> fe80::1234
最后一个地址只是无范围的,因此不能真正用于发送数据。
请注意,这是非标准的;inet_pton()
在基于 Linux 或 Windows 的系统上不起作用。但是,我认为即使在基于 Linux 和 Windows 的系统上,inet_pton()
最后也允许使用范围 ID,但它会简单地忽略它。
对于非链接本地地址,这个技巧当然不起作用,但这些地址通常没有作用域。它们可以限定范围,但通常每个接口都有一个自己的、唯一的接口 IPv6 地址,基于其接口标识符(即使您使用 DHCPv6,在这种情况下,它具有由 DHCP 服务器分配的 DHCP 地址,以及自动生成的IPv6 接口地址,除非此自动生成已被禁止)。
该struct sockaddr_in6
结构有一个用于范围 ID 的字段,但定义该字段的 RFC(RFC 2553 - 第 3.3 节)并没有真正详细说明如何解释该字段。它只说:
sin6_scope_id 到一个接口或一组接口的映射留给关于站点标识符主题的实现和未来规范。
所以这个字段完全是特定于实现的。
如果您希望正确填写此字段,并且您的代码应尽可能跨平台,则应使用getaddrinfo()
:
struct addrinfo hints;
struct addrinfo * result;
memset(&hints, 0, sizeof(hints));
// AI_NUMERICHOST prevents usage of DNS servers,
// it tells getaddrinfo that the input string is a numeric IP address.
hints.flags = AI_NUMERICHOST;
if (getaddrinfo("fe80::1234%intf1", NULL, &hints, &result) == 0) {
// result->ai_addr claims to be a pointer to struct sockaddr,
// in fact it will be a pointer to a struct sockaddr_in6 in our case.
struct sockaddr_in6 * so = (struct sockaddr_in6 *)result->ai_addr;
// It will be prefilled like this:
//
// so->sin6_family ==> AF_INET6;
// so->sin6_port ==> 0
// so->sin6_flowinfo ==> 0
// so->sin6_addr ==> fe80::1234
// so->sin6_scope_id ==> "intf1" as scope ID
// Do something with that sockaddr,
// e.g. set a port number and connect a socket to that address.
freeaddrinfo(result);
}
一个额外的提示:如果你想使用返回getaddrinfo()
的服务器套接字(你想在本地绑定然后调用accept()
它的套接字),你还应该设置被动标志:
hints.flags = AI_NUMERICHOST | AI_PASSIVE;
并不是说它在大多数情况下都会起作用,但这是正确的使用方式getaddrinfo()
。