9.15. Nazwy i DNS¶
Każda dotychczasowa strona używała numerycznych adresów IP – jak 192.168.1.20 i podobne. Rzeczywiste aplikacje prawie nigdy tego nie robią. Serwery noszą nazwy takie jak example.com lub api.example.com, a aplikacja w czasie działania wyszukuje nazwę, aby znaleźć adres IP, do którego ma wysyłać pakiety. To wyszukiwanie to Domain Name System, czyli DNS.
9.15.1. Na co tłumaczy się nazwa¶
Nazwa to tylko etykieta. example.com samo w sobie nie niesie żadnej informacji o IP – musi zostać wyszukane, tak samo jak numer telefonu wyszukuje się w książce telefonicznej. Infrastruktura DNS to rozproszona książka telefoniczna internetu, a wynikiem wyszukiwania jest jeden lub więcej adresów IP, z którymi kamera może się połączyć.
example.com -> 93.184.216.34
Pojedyncza nazwa często tłumaczy się na kilka adresów (dla równoważenia obciążenia, redundancji geograficznej, wersji IPv4 oraz IPv6 tej samej usługi). Każdy z nich działa; aplikacja wybiera jeden i próbuje go użyć, przechodząc do następnego, jeśli ten zawiedzie.
9.15.2. Jak przebiega wyszukiwanie¶
Gdy kamera pyta o example.com:
Kamera wysyła mały datagram UDP (tak, UDP – zobacz UDP – wyślij pakiet i miej nadzieję na najlepsze) do skonfigurowanego serwera DNS. Adres serwera DNS pochodził z tej samej wymiany DHCP, która przydzieliła kamerze jej własny adres IP.
Serwer DNS może już mieć odpowiedź w pamięci podręcznej (był o nią ostatnio pytany). Jeśli tak, odpowiada natychmiast.
Jeśli nie, serwer DNS przechodzi globalną hierarchię DNS: pyta serwery główne (root) o
.com, pyta te serwery oexample.com, a następnie pyta tamte serwery o samą nazwę. Cały ten obchód drzewa jest ukryty przed kamerą; kamera widzi jedno zapytanie i jedną odpowiedź.Serwer DNS zapisuje wynik w pamięci podręcznej na następny raz i odsyła odpowiedź do kamery jako kolejny datagram UDP.
Cała wymiana trwa zwykle kilka milisekund przy ciepłej pamięci podręcznej, a do około stu przy zimnej.
9.15.3. Interfejs w Pythonie¶
Funkcja getaddrinfo() wykonuje wyszukiwanie i zwraca adres gotowy do przekazania konstruktorowi gniazda:
import socket
addr = socket.getaddrinfo("example.com", 80)[0][-1]
print(addr)
# ('93.184.216.34', 80)
Sygnatura to getaddrinfo(host, port). Wartością zwracaną jest lista 5-elementowych krotek (po jednej na każdy rozwiązany adres); [0] wybiera pierwszą z nich, a [-1] wybiera ostatnie pole, którym jest krotka (ip, port), jaką można bezpośrednio przekazać gniazdu:
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect(socket.getaddrinfo("example.com", 80)[0][-1])
s.send(b"GET / HTTP/1.0\r\nHost: example.com\r\n\r\n")
...
Większość kodu kamery komunikującego się ze zdalnym serwerem zaczyna się od tej jednej linii. Pomocniki asyncio (asyncio.open_connection()) wykonują wyszukiwanie wewnętrznie, jeśli zamiast numerycznego IP przekazana zostanie nazwa, więc kod asynchroniczny zwykle nie wywołuje getaddrinfo() bezpośrednio.
9.15.4. Co może pójść nie tak¶
Brak dostępnego serwera DNS. Jeśli Wi-Fi właśnie się podniosło, a łącze jest niestabilne, pierwsze wywołanie
getaddrinfo()może przekroczyć limit czasu. Zgłaszany jestOSError; ponów próbę, gdy łącze będzie stabilne.Nazwa nie istnieje. Literówka lub nieaktualna nazwa zgłasza
OSErrorpo tym, jak serwer DNS zwróci „nie ma takiej nazwy”. Kod błędu odróżnia to od „serwer DNS niedostępny”, ale dla większości aplikacji z kamerą polityka ponawiania/rezygnacji jest taka sama.Zwrócony adres nie działa. DNS może zwrócić adres, pod którym usługa już nie jest hostowana. Rozwiązaniem jest przejście do następnego wpisu na liście wyników
getaddrinfo()lub ponowne wyszukanie nazwy później (pamięć podręczna DNS prawdopodobnie zdąży się do tego czasu zaktualizować).Portale przechwytujące (captive portals). Niektóre sieci Wi-Fi przechwytują DNS i zwracają adres IP strony portalu przechwytującego dla wszystkiego. Kamera będzie sprawiać wrażenie połączonej, ale otrzymywane dane nie będą odpowiadać temu, co wysłałaby rzeczywista usługa. Niespotykane w środowiskach produkcyjnych, ale zdarza się w sieci Wi-Fi na konferencjach i podobnych sieciach.
9.15.5. Własna nazwa kamery¶
Dotychczasowe strony wyszukiwały nazwy innych urządzeń. Kamera ma także własną nazwę, którą rozgłasza w sieci lokalnej, gdy prosi o adres. Domyślnie jest to ogólny identyfikator różniący się w zależności od płytki; network.hostname() nadpisuje go czymś, co rozpozna reszta sieci:
import network
network.hostname("kitchen-cam")
# ... then bring the link up as usual ...
Ustaw nazwę hosta przed podniesieniem interfejsu, aby nazwa została wysłana jako część początkowego żądania adresu kamery, a nie po nim.
Po dołączeniu kamery do sieci dzieją się teraz dwie rzeczy. Po pierwsze, większość domowych routerów rejestruje nazwy hostów, którym przydziela adresy, we własnym lokalnym katalogu wyszukiwania, dzięki czemu inne urządzenia mogą dotrzeć do kamery jako kitchen-cam – bez konieczności znajomości numerycznego adresu, który router akurat przydzielił. (Sieci korporacyjne mogą, ale nie muszą tego respektować; zachowanie zależy od routera.) Po drugie, sama kamera od razu uruchamia responder mDNS, więc ta sama nazwa jest również osiągalna jako kitchen-cam.local w każdej sieci, której klienci rozumieją mDNS – a robi to większość nowoczesnych systemów operacyjnych na komputery stacjonarne.
Informacja
Przekaż do network.hostname() samą nazwę hosta – po prostu "kitchen-cam", bez sufiksu .local. Forma .local to coś, co mDNS dodaje podczas wyszukiwania; wbudowanie jej w nazwę hosta sprawia, że kamera rozgłasza kitchen-cam.local jako zwykłą nazwę hosta, czego żadna ze stron wyszukiwania nie oczekuje.
9.15.6. Gdy nazwy nie wystarczają¶
Jest kilka sytuacji, w których DNS nie pomaga:
Wykrywanie w sieci lokalnej. Standardowy DNS odpowiada na pytania o nazwy zarejestrowane w globalnym katalogu; nie wie nic o urządzeniach w lokalnym segmencie. Multicast DNS (mDNS) to system, który wypełnia tę lukę. Każde uczestniczące urządzenie dołącza do specjalnej grupy multicast w sieci lokalnej i nasłuchuje zapytań; gdy urządzenie pyta o nazwę kończącą się na
.local, odpowiada bezpośrednio to urządzenie, które jest właścicielem tej nazwy. Brak centralnego serwera, brak konfiguracji DNS. Bonjour na urządzeniach Apple, Avahi na Linuksie oraz Windows 10+ – wszystkie mówią tym samym protokołem – dlatego nazwakitchen-cam.localskonfigurowana w poprzedniej sekcji rozwiązuje się w sieci domowej bez żadnej dodatkowej konfiguracji.Strona tej wymiany po stronie kamery to responder i już on działa. Czego kamera nie ma, to resolver – druga połowa, która pozwoliłaby skryptowi zapytać sieć „gdzie jest
printer.local?” i otrzymać odpowiedź. Dołączony kod mDNS obsługuje tylko responder (urządzenia wbudowane są zwykle tym, co jest znajdowane, a nie tym, co znajduje). Gdy wykrywanie musi przebiegać w drugą stronę, rozgłoszenie UDP (zobacz UDP – wyślij pakiet i miej nadzieję na najlepsze) jest prostszą odpowiedzią w przypadku lokalnego segmentu, lub czysto pythonowy moduł taki jak cbrand/micropython-mdns dodaje pełny resolver.Nazwy IPv6.
getaddrinfo()zwraca zarówno wyniki IPv4, jak i IPv6, jeśli oba są dostępne. Wybierz rodzinę, której może użyć gniazdo aplikacji.
W większości kodu po stronie kamery getaddrinfo to jednolinijka na początku dowolnej funkcji otwierającej połączenie sieciowe. Przykłady w innych miejscach tej sekcji, które działały na "192.168.1.20", działałyby tak samo z nazwą publiczną jak "api.example.com" – wystarczy najpierw ją rozwiązać.