我目前正在研究IPv6类,并使用inet_pton从字符串中检索IP的实际二进制表示,即:
AdressV6::AdressV6(const String & _ip)
{
int result = inet_pton(AF_INET6, _ip.c_str(), &(m_nativeAdress));
if(result <= 0)
//throw...
//How can I retrieve the sope ID from that?
}
有没有通用的方法呢?您是否只是手动解析字符串并查找听起来非常防弹的“%”:(
谢谢!
我现在尝试手动解析,这似乎有效。不过,如果有更好的方法,请告诉我:
//retrieve scope ID
uint32 scopeId = 0;
size_t pos = _ip.find("%");
if(pos != String::npos)
{
String theId = _ip.substr(pos+1);
scopeId = atoi(theId.c_str());
}
m_scopeId = scopeId;
答案 0 :(得分:4)
在基于BSD和BSD的系统(例如,包括MacOS X)上,范围ID作为第二个16位字嵌入到链路本地地址的地址本身中。请参考FreeBSD Handbook并搜索“8.1.1.3范围索引”(不带引号)。
因此假设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 - Section 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()
的正确方法。
答案 1 :(得分:1)
inet_pton()
不支持范围ID。我不了解其他平台,但在Windows上,您可以使用RtlIpv6StringToAddressEx()
代替。
答案 2 :(得分:0)
inet_pton()
半支持范围标识符,范围是在使用一个地址解析地址时不会引发错误。主要限制是调用的参数是struct in6_addr
,它不包含范围标识符的字段,超级结构struct sockaddr_in6
是必需的。
简单的方法是将getnameinfo()
和getaddrinfo()
包含struct sockaddr
个参数以方便使用。例如,
socklen_t
sockaddr_len (
const struct sockaddr* sa
)
{
socklen_t sa_len;
switch (sa->sa_family) {
case AF_INET: sa_len = sizeof(struct sockaddr_in); break;
case AF_INET6: sa_len = sizeof(struct sockaddr_in6); break;
default: sa_len = 0; break;
}
return sa_len;
}
int
sockaddr_ntop (
const struct sockaddr* restrict sa,
char* restrict host,
size_t hostlen
)
{
return getnameinfo (sa, sockaddr_len (sa),
host, hostlen,
NULL, 0,
NI_NUMERICHOST);
}
int
sockaddr_pton (
const char* restrict src,
struct sockaddr* restrict dst /* will error on wrong size */
)
{
struct addrinfo hints = {
.ai_family = AF_UNSPEC,
.ai_socktype = SOCK_STREAM, /* not really */
.ai_protocol = IPPROTO_TCP, /* not really */
.ai_flags = AI_NUMERICHOST
}, *result = NULL;
const int status = getaddrinfo (src, NULL, &hints, &result);
if (0 == status) {
memcpy (dst, result->ai_addr, result->ai_addrlen);
freeaddrinfo (result);
return 1;
}
return 0;
}
要回答原始前提但给出struct sockaddr
,可能需要额外的API,例如:
uint32_t
sockaddr_scope_id (
const struct sockaddr* sa
)
{
uint32_t scope_id;
if (AF_INET6 == sa->sa_family) {
struct sockaddr_in6 s6;
memcpy (&s6, sa, sizeof(s6));
scope_id = s6.sin6_scope_id;
} else
scope_id = 0;
return scope_id;
}