簡體   English   中英

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

[英]Python TCP socket with IPv6 address failed

我試圖使用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

知道為什么會出現這個錯誤嗎?

通常,網絡前綴不會重疊,並且每個IP地址僅在單個接口上使用。 但是對於鏈路本地,在啟用IPv6的每個網絡接口上使用(相同)前綴( fe80::/10 )。 這意味着,僅基於鏈路本地地址,系統無法確定要偵聽的網絡接口。 您必須自己指定界面。

以字符串表示法編寫地址時,可以通過將%后跟接口的ID附加到IPv6地址來實現。 正如glglgl所示,這可能是例如fe80::224:d7ff:fe9d:9800%wlan0 接口名稱取決於您所在的系統。 我的OS X機器有接口en0en1 ,我的Linux機箱有eth0 ,在Windows機器上接口名稱是整數。

在套接字函數中,使用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.

fe80:地址是鏈路本地的,因此總是需要使用鏈路的規范。

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM