简体   繁体   中英

Python TCP socket with IPv6 address failed

I tried to bind a python tcp socket using an IPv6 address.

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)

I used the result from socket.getaddrinfo() as mentioned in other solutions but always get this error:

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

Here's my ifconfig result for the network interface

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

Any idea why this error occurs?

Usually network prefixes don't overlap and every IP address is used only on a single interface. However for link-local the (same) prefix ( fe80::/10 ) is used on every network interface with IPv6 enabled. That means that based on just the link-local address the system cannot determine on which network interface to listen. You will have to specify the interface yourself.

When writing an address in string notation you can do that by appending % followed by the id of the interface to the IPv6 address. As glglgl has shown, this could be eg fe80::224:d7ff:fe9d:9800%wlan0 . The interface name depends on the system that you are on. My OS X machine has interfaces en0 and en1 , my Linux box has eth0 and on a Windows box the interface name would be an integer.

In the socket function the interface is specified using the scope-id. This is part of the address tuple. In your example you use ('fe80::224:d7ff:fe9d:9800', 5050) . This ok because for IPv6 the last two elements may be omitted. The full address tuple is eg ('fe80::224:d7ff:fe9d:9800', 5050, 0, 4) where 4 is the scope-id.

You can test this with a small script like:

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('')

On my system running this script shows:

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)

The part after the % isn't used anymore by the socket binding, it relies on the scope-id.


This does mean that the following bind calls produce identical results:

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))

Best to use the raw values that you get back fron getaddrinfo though. Depending on such undefined behaviour is not a good idea. A simple loop like this should work best:

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.

An fe80: address is link-local and thus always needs the specification of the link to be used.

('fe80::224:d7ff:fe9d:9800%wlan0', 5050) might be what you need.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM