9.12. UDP-socketit¶
UDP-liikennettä lähetetään ja vastaanotetaan Pythonissa kahdella datagram-socketin metodilla: sendto() lähettää datagrammin valittuun kohteeseen, ja recvfrom() vastaanottaa datagrammin ja selvittää, mistä se tuli. Jokainen kutsu siirtää yhden itsenäisen viestin; yhteystilaa ei ole.
9.12.1. Datagrammin lähettäminen¶
Yksinkertaisin UDP-lähetys on yksi rivi Pythonia socket-konstruktorin päällä:
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.sendto(b"hello", ("192.168.1.20", 9000))
s.close()
Tuo lähettää b"hello" -viestin porttiin 9000 osoitteessa 192.168.1.20 ja jatkaa eteenpäin. MicroPython valitsee tilapäisen lähdeportin; skriptin ei tarvitse sitoa mitään.
Saman hyötykuorman lähettäminen moneen kohteeseen on vain silmukka – socket on uudelleenkäytettävissä lähetysten välillä, eikä yhteyttä tarvitse muodostaa:
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. Datagrammin vastaanottaminen¶
Datagrammien vastaanottamiseksi socketin on varattava tunnettu portti, jota lähettäjät käyttävät kohteenaan. Se tehdään bind() -kutsulla:
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)
Osoite "0.0.0.0" tarkoittaa ”jokaista kameran IPv4-rajapintaa” – riippumatta siitä, mikä Wi-Fi- tai Ethernet-rajapinta tuo paketit sisään, portti 9000 kuuluu tälle socketille.
Argumentti 1024 recvfrom() -metodille on enimmäismäärä tavuja, jotka luetaan palautettuun puskuriin. Tätä suuremmat UDP-datagrammit katkaistaan; valitse arvo vastaamaan suurinta datagrammia, jota sovellus odottaa.
recvfrom() palauttaa (data, src): vastaanotetut tavut sekä lähettäjän osoitteen. Lähettäjän osoite on se, johon vastataan, mikä tekee pienen pyyntö/vastaus-protokollan kirjoittamisesta helppoa:
while True:
request, src = s.recvfrom(1024)
if request == b"ping":
s.sendto(b"pong", src)
Oletusarvoisesti recvfrom() estyy, kunnes datagrammi saapuu. Mallit sen estymättömäksi tekemiseen – aikakatkaisut, estymättömät socketit, asyncio – ovat sivulla Socketit asyncion kanssa.
9.12.3. Pyyntö ja vastaus¶
Kaksi lyhyttä skriptiä: toinen lähettää pyynnön, toinen vastaanottaa ja vastaa.
Vastaanottaja:
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)
Lähettäjä:
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?")
Muutama huomionarvoinen seikka lähettäjässä:
Ei
bind()-kutsua eikäconnect()-kutsua. UDP-asiakkaat vain lähettävät.settimeout()asettaa määräajan vastaanottokutsulle. Jos vastausta ei saavu kahdessa sekunnissa, kutsu nostaaOSError-poikkeuksen sen sijaan että estyisi ikuisesti – luonteva tapa havaita kadonnut paketti.with-lohko sulkee socketin automaattisesti.
9.12.4. Datagrammin kokorajoitukset¶
UDP-datagrammit voivat teoriassa olla jopa noin 64 KB, mutta käytännön raja on paljon pienempi. Jokaisella lähettäjän ja vastaanottajan välisen polun linkillä on Maximum Transmission Unit (MTU) – suurin yksittäinen tavulohko, jonka linkki voi kuljettaa yhdessä kehyksessä. Sekä Ethernet että Wi-Fi rajaavat tämän noin 1500 tavuun, ja lähes jokainen internet-polku palautuu jossakin kohtaa tuohon rajaan.
Kun datagrammi ylittää ylittämänsä linkin MTU:n, verkkokerros pilkkoo sen pienemmiksi fragmenteiksi ja kokoaa ne uudelleen kohteessa. UDP itse ei koskaan näe pilkkomista, mutta fragmenteilla on useita hankalia ominaisuuksia:
Jos yksikin fragmentti katoaa, koko datagrammi hylätään vastaanottajalla – fragmenttikohtaista uudelleenlähetystä ei ole. Häviötodennäköisyys kasvaa fragmenttimäärän myötä.
Jotkin verkot ja palomuurit hylkäävät pilkotut paketit kokonaan ja pitävät niitä epäilyttävinä.
Uudelleenkokoaminen kuluttaa muistia vastaanottajalla, mistä mikrokontrollerissa on pulaa.
Käytännön sääntö kameralla: pidä UDP-viestit selvästi alle 1500 tavussa. Noin 1400 tavua jättää tilaa IP- ja UDP-otsikoille, polun mahdollisesti lisäämälle tunnelointiylätyölle sekä pienille MTU:n vaihteluille Ethernet-, Wi-Fi- ja VPN-linkkien välillä. Sovellusten, joiden on lähetettävä tätä enemmän, tulisi joko paloitella data sovelluskerroksessa tai vaihtaa TCP:hen, joka hoitaa pilkkomisen ja uudelleenkokoamisen automaattisesti.
9.12.5. Yleisiä sudenkuoppia¶
Sen unohtaminen, että UDP voi kadottaa paketteja. Koodi, joka toimii täydellisesti hiljaisessa paikallisverkossa, epäonnistuu joskus hienovaraisilla tavoilla vilkkaammassa tai laajemmassa verkossa. Suunnittele aina sen varalta, että viesti ei saapunut perille.
Vastaanottajaa ei ole sidottu ennen kuin lähettäjä lähettää. Porttiin, jota kukaan ei kuuntele, lähetetty datagrammi hylätään hiljaisesti. Käynnistä vastaanottaja ensin.
MTU:ta suuremman datagrammin lähettäminen polulla. Katso edellinen osio – pidä viestit alle ~1400 tavussa.
Yllä olevat mallit kattavat lähes kaikki UDP-käytöt, joihin kameralla tarvitaan. Seuraava sivu tekee vastaavan TCP:lle.