2010-09-27 9 views
9

Chcę ceate gniazdo zgodny z protokołem IPv6 w Pythonie, robię to tak:Jak utworzyć gniazdo IPv6 w pythonie? Dlaczego otrzymałem socket.error: (22, 'Invalid argument')?

#!/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)) 

Ale to nie powiodło się:

socket.error: (22, 'Invalid argument') ? 

Czy ktoś może mi pomóc? dzięki!

mi przerobić to tak, ale wciąż nie może pracować

>>>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') 
+0

Czy weryfikacja socket.has_ipv6 zwróciła true? –

+0

Dlaczego są wiążące dla dowolnego adresu lokalnego łącza? W 99% przypadków chcesz powiązać :: (tzn. Wszystkie interfejsy). –

Odpowiedz

5

Istnieją dwie części tego problemu

pierwszym numerze

Należy użyć sa.bind (sockaddr) gdzie sockaddr jest uzyskiwany z 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) 

drugim numerze

Jeśli spojrzeć na przykład przewidzianego w dokumentacji gniazda na

Gniazdo przyjmuje trzy argumenty

socket([family[, type[, proto]]]) 

Zgodnie z dokumentacją

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. 

A jeśli użyto getaddressinfo celu uzyskania wartości dla proto to wartość różni się od domyślnego 0

Ale kiedy wykonywane następujące czynności, mam inną wartość Protocol - 17. można chcieć żeby to zbadać.

I oczywiście socket.has_ipv6 jest dla mnie Prawdziwy.

+0

nie, nadal nie działa – Jianzhong

+0

Edytowałem moje pytanie za pomocą metody, która zawiera błąd. Thanks >>> socket.has_ipv6 True – Jianzhong

3

Inną rzeczą, której nie ma w powyższym opisie, jest to, że fe80 :: * są adresami link-local. Nie można tego technicznie używać bez identyfikatora zakresu.
Na wypadek, gdyby się nie zatopiło, "link-local" oznacza, że ​​adres może być użyty tylko w określonym łączu, ale z następstwa jest to, że ponieważ adres jest specyficzny dla łącza, teoretycznie możesz mieć ten sam adres na więcej niż jednym łączu połączyć.
Teraz przyznaje się, że w większości przypadków adres lokalny dla łącza jest generowany z adresu unikatowego sprzętu (EUI-48) o adresie, przekształcając go w EUI-64, a następnie rzutując bit U/L (see), ale jest to nie jest to jedyny sposób, w jaki można wygenerować adres lokalny dla łącza, a inne mechanizmy mogą spowodować ponowne użycie adresu (nie sądzę, że jest coś, co zabrania tego). Teraz już wiesz, dlaczego to nie zadziałało.
Kolejne pytanie brzmi: jak to poprawić, lub raczej, skąd otrzymasz identyfikator zakresu. Jest to niestety nieoczywiste. Jeśli czytasz dokumenty RFC IPV6, które omawiają "Scope", stwierdzasz, że jest to zdefiniowane w sposób ogólny. W praktyce, jeśli używasz tego kodu w Linuksie (może również Windows/Mac?), Możesz użyć indeksu interfejsu.

Zauważ, że obecnie występuje błąd w nss-mdns taki, że nazwy w domenie .local są zwracane z id zakresu zawsze o wartości zero, więc w Linuksie używanie nazw w domenie .local zasadniczo nie działa, chyba że twój kod już określił identyfikator zakresu i zestawy to samo.

Powiązane problemy