9.11. Obiekty gniazd¶
Interfejsem Python do warstwy transportowej jest klasa socket.socket. Gniazdo reprezentuje jeden punkt końcowy konwersacji sieciowej – adres, port oraz protokół (UDP lub TCP), na którym odbywa się konwersacja. Rozdziały o sterowaniu sprzętem otwierały instancje UART, aby komunikować się po przewodzie; ta sekcja otwiera instancje socket, aby komunikować się w sieci. Model jest taki sam; bazowa usługa jest tylko znacznie bardziej rozbudowana.
9.11.1. Tworzenie gniazda¶
Gniazdo opisują trzy argumenty: jaką rodzinę adresów obsługuje, jaki typ gniazda oferuje i jakiego protokołu używa. Wartości domyślne pokrywają przypadki używane w pozostałej części tej sekcji:
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # IPv4 TCP
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # IPv4 UDP
Dwie stałe, między którymi aplikacja wybiera niemal w każdym przypadku:
AF_INET– adresy IPv4 (numeryczny IP plus port). Najczęstszy wybór.AF_INET6to odpowiednik dla IPv6.SOCK_STREAM– połączenie TCP, niezawodny strumień bajtów.SOCK_DGRAMto odpowiednik dla UDP.
Trzeci argument (protokół) pozostaje równy zero, co wybiera właściwą wartość domyślną na podstawie dwóch pierwszych. Pełny konstruktor jest udokumentowany w socket — moduł socket.
9.11.2. Adresy w gnieździe¶
Adres gniazda to krotka (host, port)
("192.168.1.50", 80)
("0.0.0.0", 8000)
Host to adres IP w postaci łańcucha znaków. Port to 16-bitowa liczba całkowita omówiona w Porty.
Warto znać kilka specjalnych łańcuchów host:
"0.0.0.0"oznacza „każdy interfejs IPv4 na tym urządzeniu”. Serwer powiązany z tym adresem akceptuje połączenia na dowolnym adresie posiadanym przez kamerę."127.0.0.1"to localhost – ruch do niego nigdy nie opuszcza urządzenia. Przydatny do testowania."255.255.255.255"to lokalny adres rozgłoszeniowy. Datagram UDP wysłany na ten adres trafia do każdego urządzenia w lokalnym segmencie.
Nazwy domenowe takie jak "example.com" nie są prawidłowymi łańcuchami host w adresie gniazda. Trzeba je najpierw rozwiązać do adresu IP; Nazwy i DNS omawia wywołanie getaddrinfo(), które to robi.
9.11.3. Dwie role¶
Cykl życia gniazda zależy od tego, po której stronie konwersacji się ono znajduje. Gniazdo klienta wywołuje connect() (lub, w przypadku UDP, po prostu sendto()), aby komunikować się ze znanym serwerem. Gniazdo serwera wywołuje bind(), aby zarezerwować port, a następnie albo listen() i accept() (dla TCP), albo recvfrom() (dla UDP), aby odbierać przychodzący ruch.
W obu przypadkach używany jest ten sam konstruktor socket; różnią się tylko metody wywoływane później. Następne trzy strony przeprowadzają przez praktyczne wzorce:
Gniazda UDP – wysyłanie i odbieranie datagramów.
Gniazda TCP – klient i serwer TCP.
Gniazda z asyncio – wszystko powyższe, ale wewnątrz pętli zdarzeń
asyncio.
9.11.4. Zamykanie gniazda¶
Każde gniazdo przechowuje niewielki fragment stanu systemu operacyjnego (rezerwację portu, bufory, stan TCP połączenia). Gdy aplikacja skończy z niego korzystać, close() zwalnia ten stan. Zapomniane gniazdo to powolny wyciek, który się kumuluje; w pętli otwierającej połączenia pominięcie close w końcu wyczerpie pulę dostępnych gniazd kamery.
Najczystszym wzorcem jest instrukcja with
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect(addr)
s.send(b"GET / HTTP/1.0\r\n\r\n")
...
# socket is closed automatically here, even on error
Gniazda implementują protokół menedżera kontekstu omówiony w przeglądzie języka Python, więc blok with gwarantuje wywołanie close() niezależnie od tego, czy blok zakończył się normalnie, czy przez zgłoszenie wyjątku.
9.11.5. Dokumentacja modułu socket¶
Ta i kolejne strony przeprowadzają przez API w formie opisowej. Pełny opis na poziomie argumentów każdej metody, każdej flagi i każdej stałej udostępnianej przez moduł znajdziesz w socket — moduł socket. Dokumentacja jest też miejscem, w którym należy szukać rzadziej używanych operacji (opcji gniazda, członkostwa w grupach multicast, identyfikatorów zakresu IPv6), których ta sekcja nie omawia.