Python网络编程 4.2 IPv6简介与现代地址解析--getaddrinfo()方法的使用

来源:互联网 发布:易众网络 编辑:程序博客网 时间:2024/06/14 10:22

IPv6是一个地址族,与IPv4的AF_INET类似的,其地址族为AF_INET6。这将是未来的主流地址族,可以防止IP地址被耗尽的情况。同时IPv6协议对链路层安全等很多特性提供了更加完整的支持。IPv6对Python代码的影响:

  • 必须使用AF_INET6来创建套接字,且套接字名不再仅仅由地址和端口号组成,还包括提供了“流”信息和“范围”标识的额外坐标。
  • IPv4用字节表示地址的优美形式被含有大量冒号、十六进制数字等的丑陋IPv6形式代替。
为了令程序简单强大,并免于处理IPv4到~6的迁移带来的复杂性,Python提供了套接字用户工具集中最强大的工具之一——getaddrinfo().

getaddrinfo()是socket模块中涉及地址的众多操作之一,它可能是我们用来将用户指定的主机名和端口号转换为可供套接字方法使用的地址时所需的唯一方法。在下面一段代码中,分别用之前的socket方法和getaddrinfo方法来实现对百度的连接:

from pprint import pprintimport  socketsc = socket.socket(socket.AF_INET,socket.SOCK_STREAM)sc.connect(('www.baidu.com',80))  #之前的方法。参数中的www.可以去掉,因为80就代表www服务器的默认端口号,可以从后面的连接看出来。infolist = socket.getaddrinfo('baidu.com','www')  #getaddrinfo方法可以直接用www作为端口号,也可以用整形80来作为端口号。而上面的方法只能用整形。pprint(infolist)info = infolist[1]s = socket.socket(*info[0:3])   #注意 * 号的使用,是将info的前三个元素作为三个参数传入socket方法中。s.connect(info[4])print('\n两种方法的对比\n')print(s.getpeername())print(s.getsockname())print(sc.getpeername())print(sc.getsockname())
该程序的运行结果如图:

可以看出:1.getaddrinfo()的返回值将会是一个元组的列表,其中每一个元组中的信息都可以使得我们到达想要连接到的域名。我对书上的源代码做了修改,主要在于

info = infolist[1]
也就是并没有取列表中的第一个地址信息,而是取了第二个,是为了方便两种方法的对比。在输出的结果中也可以看到,两个方法中得到的getpeername是不一样的,是两个不同的IP地址,是因为sc.connect('baidu.com','80')将会默认地选择该套接字上一次连接的ip地址,或者选择列表中的第一个地址。这些不同的IP地址都指向了同一个域名。

  1. 使用getaddrinfo()为服务器绑定端口:有时候我们会想要得到一个地址,并将其作为参数提供给bind()这样做的原因可能是我们正在创建一个服务器套接字,也可能是出于某些原因希望客户端从一个可预计的地址连接至其他主机,此时可调用getaddrinfo()将主机名设置为None,但提供端口号与套接字类型。需要注意的是,在此处以及后面的get调用中,如果某个字段为数字,则可以使用0来表示数字通配符。      本例中有两个查询,第一个使用了字符串作为端口标识符,第二个使用了原始的数字端口号。第一个查询的目的是想知道,如果使用TCP来支持SMTP数据传输的话,应该通过bind()把套接字绑定到哪个地址。该查询返回的答案是合适的通配符地址,表示可以绑定到本机上的任何IPv4和IPv6接口,还提供了套接字地址族、套接字类型以及协议。需要注意的是,如果getaddrinfo()中的地址是“localhost”,那么其返回的将包含IPv4和IPv6两种形式的套接字参数,而如果用'127.0.0.1'作为传入的参数的话,那么其只会返回IPv4形式的套接字参数。
  2. 使用getaddrinfo()连接服务:查询服务时,可以使用一个空字符串来表示要通过自环接口来连接回本机,也可以提供一个包含IPv4地址、IPv6地址或是主机名的字符串来指定目标地址。    准备调用connect()或者sendto()连接服务或向服务发送数据时,调用getaddrinfo(),并设置AI_ADDRCONFIG标记,可以将计算机无法连接的所有地址都过滤掉。例如,有一个机构,可能既有IPv4的地址段,也有IPv6的地址段。如果特定的主机只支持IPv4,那么我们会希望将结果中的非IPv4地址过滤掉。有些情况下,本机只有IPv6网络接口,但连接的服务只支持IPv4。为了支持这种情况,我们也需要指定的AI_V4MAPPED,指定该标记之后,会将IPv4地址重新编码为可实际使用的IPv6地址。综合起来,就得到了在套接字连接前使用getaddrinfo()的常用方法。

在代码中使用getaddrinfo()

代码段放在了www_ping.py链接中。运行效果如下:


除了代码可以正常工作之外,还发现了一个有趣的事情,就是网址为www.baidu.com的时候,下面返回的host居然是www.a.shifen.com。上网查了查,这个还是有些渊源的。

百度与十分系统

关于这个脚本,有三点值得注意:

  • 该脚本的通用性。代码中并没有提到IP协议,也没用提到使用TCP作为传输方式。如果用户刚好输入了一个主机名,而系统认为该主机是通过某种方式连接的,那么getaddrinfo()将会返回该方式的套接字族、类型和协议,从而我们最终创建并连接的套接字就是这个类型的。
  • getaddrinfo()调用的失败会引起一个特定的名称服务错误,就是代码中的gaierror。
  • 代码中,并没有为socket()的构造函数传入三个单独的参数,而是使用星号引入了参数列表,表示socket_args列表中的三个元素会被当做三个单独的参数传入到构造参数中。

阅读全文
0 0
原创粉丝点击