C++ Socket编程中,
如果客户端要向服务端进行IPv6通信,
如果使用的是global 地址(2409::开头的),直接设置sin6_addr就可以,
如果使用的是链路本地地址(fe80::开头的),就需要设置sin6_scope_id才能通信。
sin6_scope_id实际上是网卡的索引号码。
命令行查看网口索引
Linux
可以用ip addr来查看网口索引
Windows
可以用netsh interface ipv6 show route来查看网口索引
通过代码获取网口索引
使用getaddrinfo
首先用gethostname() 获取本地hostname,然后用getaddrinfo() 获取hostname的IP地址信息,这里面的IPv6信息会包括网口索引。
//get local host name char errmsg[256] = { 0x00 }; struct in_addr addr; char local_host_name[256] = { 0x00 }; if (gethostname(local_host_name, 256)) { std::cout << "Get local host name failed, err:" << strerror_s(errmsg, 256, WSAGetLastError()) << std::endl; return false; } std::cout << "local_host_name:" << local_host_name << std::endl; std::cout << std::endl; struct addrinfo hints, * res = NULL, * p = NULL; memset(&hints, 0, sizeof(hints)); int e; hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; e = getaddrinfo(local_host_name, NULL, &hints, &res); if (e != 0) { std::cout << "get local addrinfo error:" << WSAGetLastError() << std::endl; return false; } p = res; char bufff[512] = { 0x00 }; while (p != NULL) { if (p->ai_family == AF_INET) { std::cout << "support IPv4" << std::endl; memset(bufff, 0x00, sizeof(bufff)); inet_ntop(AF_INET, &((sockaddr_in*)p->ai_addr)->sin_addr, bufff, sizeof(bufff)); std::cout << "local support IPv4 addr:" << bufff << std::endl; } else if (p->ai_family == AF_INET6) { std::cout << "support IPv6" << std::endl; memset(bufff, 0x00, sizeof(bufff)); inet_ntop(AF_INET6, &((sockaddr_in6*)p->ai_addr)->sin6_addr, bufff, sizeof(bufff)); sockaddr_in6* pv6 = (sockaddr_in6*)p->ai_addr; ULONG scopeid = pv6->sin6_scope_id; std::cout << "local support IPv6 addr:" << bufff << std::endl; std::cout << "ipv6 socopeid:" << ((sockaddr_in6*)p->ai_addr)->sin6_scope_id << std::endl; this->local_ipv6_scope_id = ((sockaddr_in6*)p->ai_addr)->sin6_scope_id; ret = true; } p = p->ai_next; } freeaddrinfo(res);
输出结果:
是可以返回非0的网口索引的。(sin6_scope_id 设置为0 ,是无法进行链路本地地址通信的)
这里有一点要注意,如果getaddrinfo设置了hints.ai_flags = AI_PASSIVE;,那获取的是用于bind的任意地址,这个地址信息中是没有网口索引的。
struct addrinfo hints, * res = NULL, * p = NULL; memset(&hints, 0, sizeof(hints)); int e; hints.ai_flags = AI_PASSIVE; e = getaddrinfo(NULL, "8080", &hints, &res); if (e != 0) { std::cout << "get local addrinfo error:" << WSAGetLastError() << std::endl; return false; } p = res; char bufff[512] = { 0x00 }; while (p != NULL) { if (p->ai_family == AF_INET) { std::cout << "support IPv4" << std::endl; memset(bufff, 0x00, sizeof(bufff)); inet_ntop(AF_INET, &((sockaddr_in*)p->ai_addr)->sin_addr, bufff, sizeof(bufff)); std::cout << "local support IPv4 addr:" << bufff << std::endl; } else if (p->ai_family == AF_INET6) { std::cout << "support IPv6" << std::endl; memset(bufff, 0x00, sizeof(bufff)); inet_ntop(AF_INET6, &((sockaddr_in6*)p->ai_addr)->sin6_addr, bufff, sizeof(bufff)); sockaddr_in6* pv6 = (sockaddr_in6*)p->ai_addr; ULONG scopeid = pv6->sin6_scope_id; std::cout << "local support IPv6 addr:" << bufff << std::endl; std::cout << "ipv6 socopeid:" << ((sockaddr_in6*)p->ai_addr)->sin6_scope_id << std::endl; this->local_ipv6_scope_id = ((sockaddr_in6*)p->ai_addr)->sin6_scope_id; ret = true; } p = p->ai_next; } freeaddrinfo(res); return ret;
结果:网口索引是0,不能用于链路本地地址通信
其余可以达成目的的API
Linux
使用if_nameindex()或者getifaddrs() 遍历网口,获取网口名字。-> C语言getifaddrs()通过网口IP获取网口名
然后使用if_nametoindex() 根据网口名,获取网口名的索引。->[IPv6] socket IPv6编程 connect()连接不上的问题
windows
可能是我菜鸡,没找到。