为什么在Python中,%en0后缀无法连接链路本地IPv6 TCP套接字?
大约一周前,有人在StackOverflow上提问,问他们的Python代码为什么无法连接到IPv6的链路本地地址。我回复说,因为这是一个链路本地地址,所以他们需要在目标IP地址后面加上一个%en0(或者其他想要的本地接口名称)。我以为我知道自己在说什么,所以在回答之前并没有实际测试我的建议(真是太糟糕了!)。
今天我想用同样的方法,结果发现这似乎并不奏效。:^( 也就是说,这段代码无法正常工作:
>>> from socket import *
>>> s = socket(AF_INET6, SOCK_STREAM)
>>> s.connect(('fe80::21f:5bff:fe3f:1b36%en0', 2001))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 1, in connect
socket.error: [Errno 65] No route to host
而下面这段代码,反而可以正常工作(无论有没有%en0后缀):
>>> from socket import *
>>> s = socket(AF_INET6, SOCK_STREAM)
>>> s.connect(('fe80::21f:5bff:fe3f:1b36%en0', 2001, 0, 6))
>>>
...不过我不喜欢这样做,因为为了找出最后一个参数应该提供哪个范围ID整数,我得执行一堆不太方便的代码,去遍历本地接口列表,找到名为'en0'的接口,并提取它的范围ID,这样的复杂度让我觉得不太舒服。
既然connect()函数可以接受IP地址后面的%en0后缀,为什么它没有像预期那样使用这个后缀来确定范围ID呢?
顺便说一下,我是在MacOS/X 10.6.4下测试Python 2.6.1。
1 个回答
14
这是建立IPv6连接的正确方法:
>>> addrinfo = getaddrinfo('fe80::225:ff:fecd:5aa0%en0', 2001, AF_INET6, SOCK_STREAM)
>>> addrinfo
[(30, 1, 6, '', ('fe80::225:ff:fecd:5aa0%en0', 2001, 0, 4))]
>>> (family, socktype, proto, canonname, sockaddr) = addrinfo[0]
>>> s = socket(family, socktype, proto)
>>> s.connect(sockaddr)
getaddrinfo()
这个函数会为你返回正确的数字范围和流量信息。