11. IPv6客户端程序设计 IPv6客户端通过指定地址或域名来访问IPv4以及IPv6的服务端,如果服务提供方是IPv4,那么需要用IPv4-mapped IPv6地址的形式去访问。针对域名访问,需要采用DNS实现名称来解析,在IPv4环境下的解析库函数调用有两个:gethostbyname(正向解析)、gethostbyaddr(反向解析),此类解析用于IPv4环境。IPv6环境下则采用getaddrinfo和getnameinfo来解析IPv4和IPv6地址信息,getaddrinfo根据域名/服务名称以及相关hints等信息返回一组A和AAAA记录,每条返回的记录附加有用于创建socket所需的AF_xxx以及SOCK_xxx等信息,客户端程序通常利用循环语句针对getaddrinfo返回地址列表的每一项尝试创建socket以及connect对端,直到发现针对一条地址记录的socket和connect调用都返回成功为止。 客户端只需要知道服务端的域名以及服务端口,采用getaddrinfo调用返回的地址列表信息,就可以创建合适的地址族的socket和服务端连接。 n Python的getaddrinfo使用示例: 解析www.hpe.com的地址信息,返回一个包含IPv4以及IPv6地址的列表 #python >>> from socket import * >>>from os import * >>> from pprint import pprint >>> list=getaddrinfo("www.hpe.com","www") >>> pprint(list) [(2, 1, 6, '', ('23.7.213.221', 80)), (2, 2, 17, '', ('23.7.213.221', 80)), (10, 1, 6, '', ('2600:1417:a000:195::1463', 80, 0, 0)), (10, 2, 17, '', ('2600:1417:a000:195::1463', 80, 0, 0)), (10, 1, 6, '', ('2600:1417:a000:1b4::1463', 80, 0, 0)), >>> list[0][0:3] (2, 1, 6) >>> list[0][4] ('23.7.213.221', 80) 连接DNS解析返回列表中的第一项的IPv4地址,成功后在系统层面输出TCP连接的四元组信息: >>> s=socket(*list[0][0:3]) >>> s.connect(list[0][4]) >>> system("ss -tn|grep 80") ESTAB 0 0 192.168.100.165:39838 23.7.213.221:80 0 尝试用DNS解析返回列表中的第三项IPv6的地址信息来连接www.hpe.com,由于测试系统centos无法通达服务端IPv6地址,连接失败: >>> s1=socket(*list[2][0:3]) >>> s1.connect(list[2][4]) Traceback (most recent call last): File "<stdin>", line 1, in <module> File "/usr/lib64/python2.7/socket.py", line 224, in meth return getattr(self._sock,name)(*args) socket.error: [Errno 101] Network is unreachable >>> n C语言的getaddrinfo使用示例: main(int argc, char **argv) { struct addrinfo *res, *ainfo; struct addrinfo hints; int error; struct sockaddr_in6 peeraddr6; struct sockaddr_in6 addr6; char connect_addr[INET6_ADDRSTRLEN]; if (argc != 2) { fprintf(stderr, "Usage: %s <remote host>\n", argv[0]); exit(1); } memset ((char *)&hints, 0, sizeof(hints)); hints.ai_socktype = SOCK_STREAM; error = getaddrinfo(argv[1], "www", &hints, &res); if (error != 0) exit(1); for (ainfo = res; ainfo != NULL; ainfo = ainfo->ai_next) { s = socket (ainfo->ai_family,ainfo->ai_socktype,ainfo->ai_protocol); if (s == -1) continue; if (connect(s, ainfo->ai_addr, ainfo->ai_addrlen) == -1) continue; else break; } ……………………….. } java,Python等语言对这些细节都做了很好的封装,如python中的socket.create_connection()就可以实现以上C代码的所有功能。 12. 结束语 本文对基于不同IP地址族的应用的访问方式以及具体实现的方法进行了简要说明, 同时也介绍了如何改造现有IPv4应用以适应新的IPv6环境,介绍了同时适用于IPv4以及IPv6环境的应用的方法。IPv6应用迁移是未来应用迁移发展的方向,希望本文内容对IPv6应用迁移感兴趣的读者有所帮助。 |