9.12. UDP sokety¶
UDP provoz se v Pythonu odesílá a přijímá dvěma metodami na datagramovém soketu: sendto() pro vyslání datagramu na zvolený cíl a recvfrom() pro přijetí datagramu a zjištění, odkud přišel. Každé volání přenáší jednu samostatnou zprávu; neexistuje žádný stav spojení.
9.12.1. Odeslání datagramu¶
Nejjednodušší UDP odeslání je jeden řádek Pythonu nad konstruktorem soketu:
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.sendto(b"hello", ("192.168.1.20", 9000))
s.close()
Tím se odešle b"hello" na port 9000 na 192.168.1.20 a dál se nic neděje. MicroPython zvolí dočasný zdrojový port; skript nemusí nic navazovat.
Odeslání stejných dat na mnoho cílů je jen smyčka – soket je mezi odesláními opakovaně použitelný a není třeba navazovat žádné spojení:
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. Přijetí datagramu¶
Aby mohl soket přijímat datagramy, musí obsadit známý port, který odesílatelé použijí jako svůj cíl. To je volání 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" znamená „každé IPv4 rozhraní na kameře“ – ať už pakety přivádí jakékoli Wi-Fi nebo Ethernetové rozhraní, port 9000 patří tomuto soketu.
Argument 1024 u recvfrom() je maximální počet bajtů, které se mají přečíst do vráceného bufferu. UDP datagramy větší než tato hodnota budou oříznuty; zvolte hodnotu odpovídající největšímu datagramu, který aplikace očekává.
recvfrom() vrací (data, src): přijaté bajty a adresu odesílatele. Adresa odesílatele je to, na co odpovědět, což usnadňuje napsání malého protokolu typu požadavek/odpověď:
while True:
request, src = s.recvfrom(1024)
if request == b"ping":
s.sendto(b"pong", src)
Ve výchozím nastavení recvfrom() blokuje, dokud nedorazí datagram. Vzory, jak zařídit, aby neblokoval – časové limity, neblokující sokety, asyncio – najdete na stránce Sockety s asyncio.
9.12.3. Požadavek a odpověď¶
Dva krátké skripty: jeden odešle požadavek, druhý přijme a odpoví.
Příjemce:
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)
Odesílatel:
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?")
Pár věcí, které stojí za zmínku u odesílatele:
settimeout()nastaví na volání příjmu časový limit. Pokud do dvou sekund nedorazí žádná odpověď, volání vyvoláOSErrormísto nekonečného blokování – přirozený způsob, jak zjistit ztracený paket.Blok
withsoket automaticky zavře.
9.12.4. Omezení velikosti datagramu¶
UDP datagramy mohou být teoreticky velké až přibližně 64 KB, ale praktický limit je mnohem menší. Každá linka na cestě mezi odesílatelem a příjemcem má svou Maximum Transmission Unit (MTU) – největší jednotlivý blok bajtů, který daná linka dokáže přenést v jednom rámci. Ethernet i Wi-Fi tuto hodnotu omezují na zhruba 1500 bajtů a téměř každá internetová cesta se k tomuto limitu někde vrací.
Když datagram překročí MTU linky, kterou musí překročit, síťová vrstva ho rozdělí na menší fragmenty a v cíli je opět složí. Samotné UDP rozdělení nikdy nevidí, ale fragmenty mají několik nepříjemných vlastností:
Pokud se ztratí byť jen jeden fragment, celý datagram se na příjemci zahodí – neexistuje žádné opětovné vysílání jednotlivých fragmentů. Pravděpodobnost ztráty roste s počtem fragmentů.
Některé sítě a firewally zahazují fragmentované pakety úplně a považují je za podezřelé.
Opětovné složení stojí paměť na straně příjemce, které je na mikrokontroléru nedostatek.
Praktické pravidlo na kameře: udržujte UDP zprávy výrazně pod 1500 bajty. Zhruba 1400 bajtů ponechává prostor pro IP a UDP hlavičky, případnou režii tunelování, kterou cesta přidá, a drobné odchylky MTU mezi Ethernetem, Wi-Fi a VPN linkami. Aplikace, které potřebují odeslat více, by měly buď rozdělit data na úrovni aplikace, nebo přejít na TCP, které rozdělování a opětovné skládání zvládá automaticky.
9.12.5. Časté chyby¶
Zapomenutí, že UDP může ztrácet pakety. Kód, který funguje dokonale na klidné lokální síti, někdy selhává nenápadnými způsoby na rušnější nebo rozsáhlejší síti. Vždy počítejte s možností, že zpráva nedorazila.
Příjemce není navázán dříve, než odesílatel odešle. Datagram odeslaný na port, na kterém nikdo nenaslouchá, je tiše zahozen. Spusťte nejprve příjemce.
Odeslání datagramu většího, než je MTU cesty. Viz předchozí část – udržujte zprávy pod ~1400 bajty.
Výše uvedené vzory pokrývají téměř každé použití UDP, po kterém kamera sáhne. Následující stránka popisuje totéž pro TCP.