【问题标题】:Python TCP socket with IPv6 address failed具有 IPv6 地址的 Python TCP 套接字失败
【发布时间】:2014-09-06 23:01:08
【问题描述】:

我尝试使用 IPv6 地址绑定 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

这是我的网络接口的 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

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

【问题讨论】:

    标签: python linux sockets tcp ipv6


    【解决方案1】:

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

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

    在 socket 函数中,接口是使用 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)
    

    % 之后的部分不再被套接字绑定使用,它依赖于 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.
    

    【讨论】:

    • 感谢您的回答。这就是问题所在。添加接口解决了它。
    【解决方案2】:

    fe80: 地址是本地链接地址,因此始终需要指定要使用的链接。

    ('fe80::224:d7ff:fe9d:9800%wlan0', 5050) 可能是您需要的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-06-15
      • 2012-04-11
      • 2011-10-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-25
      • 2015-03-05
      相关资源
      最近更新 更多