具有 IPv6 地址的 Python TCP 套接字失败

标签 python linux sockets tcp ipv6

我尝试使用 IPv6 地址绑定(bind) python tcp 套接字。

self.__addr = ('fe80::224:d7ff:fe9d:9800', 5050)
self.__type = socket.AF_INTE6
self.__sock = socket.socket(self.__type, socket.SOCK_STREAM)
for family, _, _, _, sockaddr in socket.getaddrinfo( self.__addr[0], self.__addr[1], 0, 0, socket.SOL_TCP ):
    if family == self.__type:
        self.__addr = sockaddr
            break

self.__sock.bind( self.__addr )
self.__sock.listen(1)

我使用了其他解决方案中提到的 socket.getaddrinfo() 的结果,但总是出现此错误:

self.__sock.bind( self.__addr )
  File "/usr/lib/python2.7/socket.py", line 224, in meth
    return getattr(self._sock,name)(*args)
socket.error: [Errno 22] Invalid argument

这是我对网络接口(interface)的 ifconfig 结果

wlan0     Link encap:Ethernet  Hardware Adresse 00:24:d7:9d:98:00  
          inet Adresse:192.168.0.103  Bcast:192.168.0.255  Maske:255.255.255.0
          inet6-Adresse: fe80::224:d7ff:fe9d:9800/64

知道为什么会出现这个错误吗?

最佳答案

通常网络前缀不会重叠,每个 IP 地址仅在一个接口(interface)上使用。然而,对于本地链路,(相同的)前缀 (fe80::/10) 用于每个启用 IPv6 的网络接口(interface)。这意味着仅基于链路本地地址,系统无法确定要在哪个网络接口(interface)上监听。您必须自己指定接口(interface)。

当用字符串表示法编写地址时,您可以通过将 % 后跟接口(interface) ID 附加到 IPv6 地址来实现。正如 glglgl 所示,这可能是例如fe80::224:d7ff:fe9d:9800%wlan0。接口(interface)名称取决于您所在的系统。我的 OS X 机器有接口(interface) en0en1,我的 Linux 机器有 eth0 而在 Windows 机器上,接口(interface)名称将是一个整数。

在 socket 函数中,接口(interface)是使用 scope-id 指定的。这是地址元组的一部分。在您的示例中,您使用 ('fe80::224:d7ff:fe9d:9800', 5050)。这没问题,因为对于 IPv6,最后两个元素可能会被省略。完整的地址元组是例如('fe80::224:d7ff:fe9d:9800', 5050, 0, 4) 其中 4 是范围 ID。

你可以用一个小脚本来测试这个:

import socket

addrs = [('fe80::cafe:1%en0', 5050),            
         ('fe80::cafe:1%en1', 5050)]    
for addr in addrs:
  print('Original addr: {}'.format(addr))
  for res in socket.getaddrinfo(addr[0], addr[1], socket.AF_INET6,
                                socket.SOCK_STREAM, socket.SOL_TCP):
      af, socktype, proto, canonname, sa = res
      print('Full addr:     {}'.format(sa))
  print('')

在我的系统上运行这个脚本显示:

Original addr: ('fe80::cafe:1%en0', 5050)
Full addr:     ('fe80::cafe:1%en0', 5050, 0, 4)

Original addr: ('fe80::cafe:1%en1', 5050)
Full addr:     ('fe80::cafe:1%en1', 5050, 0, 5)

套接字绑定(bind)不再使用 % 之后的部分,它依赖于 scope-id。


这确实意味着以下 bind 调用会产生相同的结果:

s = socket.socket(af, socktype, proto)
s.bind(('fe80::cafe:1', 5050, 0, 4))
s.bind(('fe80::cafe:1%en0', 5050, 0, 4))
s.bind(('fe80::cafe:1%bla', 5050, 0, 4))

不过,最好使用从 getaddrinfo 返回的原始值。依赖于这种未定义的行为不是一个好主意。像这样的简单循环应该效果最好:

addr = ('fe80::224:d7ff:fe9d:9800%wlan0', 5050)
s = None
for res in socket.getaddrinfo(addr[0], addr[1], socket.AF_INET6,
                              socket.SOCK_STREAM, socket.SOL_TCP):
    af, socktype, proto, canonname, sa = res
    try:
        s = socket.socket(af, socktype, proto)
        s.bind(sa)
        s.listen(1)
    except socket.error:
        if s is not None:
            s.close()
        s = None

if s is None:
    print('Socket opening/binding failed')

etc.

关于具有 IPv6 地址的 Python TCP 套接字失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24780404/

相关文章:

python - 将元组转换为字符串时出现问题

用于创建名称为日期戳的新目录的 Linux 脚本

java - 为什么socketRead0使线程处于RUNNABLE状态

python - 如何更改不同语言中单词的最后一个字母?

python - Matplotlib:是否有 rcParams 方法来调整图例边框宽度?

c++ - 我应该忽略 __builtin_prefetch() 函数还是替换它?

linux - 为旧的 Linux 内核设置 Buildroot

c++ - 套接字监听不会在 linux 下的 C++ 中解除绑定(bind)

Java - 套接字编程/阻塞端口

java - Scala:相当于 np.digitize 对数据进行分桶