9.12. UDP utičnice¶
UDP promet u Pythonu šalje se i prima pomoću dviju metoda na datagramskoj utičnici: sendto() za odašiljanje datagrama na odabrano odredište i recvfrom() za primanje datagrama i otkrivanje odakle je stigao. Svaki poziv premješta jednu samostalnu poruku; nema stanja veze.
9.12.1. Slanje datagrama¶
Najjednostavnije UDP slanje jedna je linija Pythona povrh konstruktora utičnice:
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.sendto(b"hello", ("192.168.1.20", 9000))
s.close()
To šalje b"hello" na port 9000 na 192.168.1.20 i ostavlja to. MicroPython bira efemerni izvorni port; skripta ne mora ništa povezivati.
Slanje istog sadržaja na više odredišta samo je petlja – utičnica je iskoristiva između slanja, a nema veze koju treba postaviti:
targets = [
("192.168.1.20", 9000),
("192.168.1.21", 9000),
("192.168.1.22", 9000),
]
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
for addr in targets:
s.sendto(b"hello", addr)
9.12.2. Primanje datagrama¶
Za primanje datagrama, utičnica mora zauzeti poznati port koji će pošiljatelji koristiti kao svoje odredište. To je poziv bind()
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(("0.0.0.0", 9000))
while True:
data, src = s.recvfrom(1024)
print("from", src, "got", data)
Adresa "0.0.0.0" znači „svako IPv4 sučelje na kameri” – koje god Wi-Fi ili Ethernet sučelje dovodi pakete, port 9000 pripada ovoj utičnici.
Argument 1024 u recvfrom() najveći je broj bajtova koji se učitavaju u vraćeni međuspremnik. UDP datagrami veći od te veličine bit će odsječeni; odaberite vrijednost koja odgovara najvećem datagramu koji aplikacija očekuje.
recvfrom() vraća (data, src): primljene bajtove i adresu pošiljatelja. Adresa pošiljatelja je ono na što treba odgovoriti, što olakšava pisanje malog protokola zahtjeva/odgovora:
while True:
request, src = s.recvfrom(1024)
if request == b"ping":
s.sendto(b"pong", src)
Prema zadanim postavkama recvfrom() blokira dok ne stigne datagram. Obrasci za to da ne blokira – istek vremena, neblokirajuće utičnice, asyncio – nalaze se na Utičnice s asyncio.
9.12.3. Zahtjev i odgovor¶
Dvije kratke skripte: jedna šalje zahtjev, druga prima i odgovara.
Primatelj:
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(("0.0.0.0", 9000))
while True:
req, src = s.recvfrom(64)
print("got", req, "from", src)
s.sendto(b"ack: " + req, src)
Pošiljatelj:
import socket
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
s.settimeout(2.0) # 2 s reply window
s.sendto(b"ping", ("192.168.1.20", 9000))
try:
reply, _ = s.recvfrom(64)
print("reply:", reply)
except OSError:
print("no reply in 2 s -- packet lost?")
Nekoliko stvari vrijedi primijetiti kod pošiljatelja:
settimeout()postavlja rok za poziv primanja. Ako odgovor ne stigne unutar dvije sekunde, poziv izazivaOSErrorumjesto da blokira zauvijek – prirodan način otkrivanja izgubljenog paketa.Blok
withautomatski zatvara utičnicu.
9.12.4. Ograničenja veličine datagrama¶
UDP datagrami teoretski mogu biti veliki do oko 64 KB, ali praktično ograničenje mnogo je manje. Svaka poveznica na putu između pošiljatelja i primatelja ima Maximum Transmission Unit (MTU) – najveći pojedinačni blok bajtova koji ta poveznica može prenijeti u jednoj sličici. Ethernet i Wi-Fi to oboje ograničavaju na oko 1500 bajtova, a gotovo svaki internetski put negdje se vraća na to ograničenje.
Kada datagram premaši MTU poveznice koju mora prijeći, mrežni sloj dijeli ga na manje fragmente i ponovno ih sastavlja na odredištu. Sam UDP nikada ne vidi podjelu, ali fragmenti imaju nekoliko nezgodnih svojstava:
Ako se izgubi bilo koji jedan fragment, cijeli se datagram odbacuje na primatelju – nema ponovnog slanja po fragmentu. Vjerojatnost gubitka raste s brojem fragmenata.
Neke mreže i vatrozidi u potpunosti odbacuju fragmentirane pakete, tretirajući ih kao sumnjive.
Ponovno sastavljanje troši memoriju na primatelju, koje na mikrokontroleru ima malo.
Praktično pravilo na kameri: držite UDP poruke znatno ispod 1500 bajtova. Otprilike 1400 bajtova ostavlja prostora za IP i UDP zaglavlja, sve režijske troškove tuneliranja koje put dodaje te male varijacije MTU-a između Ethernet, Wi-Fi i VPN poveznica. Aplikacije koje trebaju slati više od toga trebale bi ili podijeliti podatke na aplikacijskom sloju ili prijeći na TCP, koji automatski rješava dijeljenje i ponovno sastavljanje.
9.12.5. Česte zamke¶
Zaboravljanje da UDP može izgubiti pakete. Kod koji savršeno radi na mirnoj lokalnoj mreži ponekad zakaže na suptilne načine na prometnijoj ili široj mreži. Uvijek projektirajte za mogućnost da poruka nije stigla.
Primatelj nije povezan prije nego pošiljatelj pošalje. Datagram poslan na port na kojem nitko ne osluškuje tiho se odbacuje. Prvo pokrenite primatelja.
Slanje datagrama većeg od MTU-a puta. Pogledajte prethodni odjeljak – držite poruke ispod ~1400 bajtova.
Gornji obrasci pokrivaju gotovo svaku UDP upotrebu za kojom kamera poseže. Sljedeća stranica radi ekvivalent za TCP.