如何在Python中创建IPv6套接字?为什么会出现socket.error: (22, '无效参数')?
我想在Python中创建一个IPv6的套接字,我是这样做的:
#!/usr/bin/env python
import sys
import struct
import socket
host = 'fe80::225:b3ff:fe26:576'
sa = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
sa.bind((host , 50000))
但是失败了:
socket.error: (22, 'Invalid argument') ?
有没有人能帮我一下?谢谢!
我又尝试了这样做,但还是不行:
>>>host = 'fe80::225:b3ff:fe26:576'
>>>sa = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
>>>res = socket.getaddrinfo(host, port, socket.AF_UNSPEC, socket.SOCK_DGRAM, 0, socket.AI_PASSIVE)
>>>family, socktype, proto, canonname, sockaddr = res[0]
>>>print sockaddr
('fe80::225:b3ff:fe26:576', 50001, 0, 0)
>>>sa.bind(sockaddr)
Traceback (most recent call last):
File "<stdin>", line 1, in ?
File "<string>", line 1, in bind
socket.error: (22, 'Invalid argument')
2 个回答
上面提到的还有一个缺失的点,就是fe80::*是链接本地地址。简单来说,这种地址只能在特定的网络连接上使用,所以你在使用的时候必须加上一个范围标识符(Scope ID)。
如果你不太明白,“链接本地
”的意思就是这个地址只能在某个特定的网络上用,而因为这个地址是特定于网络的,理论上你可以在多个网络上使用同样的地址。
通常情况下,链接本地地址是通过把一个独特的硬件地址(EUI-48)转换成EUI-64格式,然后打开一个特定的位(U/L位)来生成的(查看这里)。不过,这并不是生成链接本地地址的唯一方法,其他方式也可能导致地址重复使用(我觉得没有什么规定禁止这样做)。所以你现在知道为什么它不工作了。
接下来的问题是,如何解决这个问题,或者说说你该从哪里获取范围标识符(Scope ID)。 不幸的是,这个并不是很明显。如果你查阅讨论“范围
”的IPv6标准文档,你会发现它的定义比较笼统。实际上,如果你在Linux上运行这段代码(可能在Windows或Mac上也可以),你可以使用接口索引来获取。
需要注意的是,目前在nss-mdns中有一个bug,导致.local的名称返回的范围标识符总是为零,所以在Linux上使用.local名称基本上是行不通的,除非你的代码已经找到了范围标识符并自己设置了它。
这个问题可以分成两个部分
第一个问题
你应该使用 sa.bind(sockaddr),其中 sockaddr 是通过 getaddrinfo 获取的
>>> HOST = 'localhost'
>>> PORT = 50007
>>> res = socket.getaddrinfo(HOST, PORT, socket.AF_UNSPEC, socket.SOCK_DGRAM, 0, socket.AI_PASSIVE)
>>> family, socktype, proto, canonname, sockaddr = res[1]
>>> proto
17
>>> sockaddr
('fe80::1%lo0', 50007, 0, 1)
第二个问题
如果你查看 socket 文档中的示例,地址是
Socket 接受三个参数
socket( [family[, type[, proto]]])
根据文档的说明
Create a new socket using the given address family,
socket type and protocol number. The address family
should be AF_INET (the default), AF_INET6 or AF_UNIX.
The socket type should be SOCK_STREAM (the default),
SOCK_DGRAM or perhaps one of the other "SOCK_" constants.
The protocol number is usually zero and may be omitted in that case.
如果你使用 getaddressinfo 来获取 proto 的值,那么这个值会和默认的 0 不一样
但是当我执行以下代码时,我得到的协议值是 17。你可能也想调查一下这个问题。
当然,对我来说,socket.has_ipv6 是 True。