9.12. UDP soketleri¶
Python’da UDP trafiği, bir datagram soketi üzerindeki iki yöntemle gönderilip alınır: seçilen bir hedefe datagram fırlatmak için sendto() ve bir datagram alıp nereden geldiğini öğrenmek için recvfrom(). Her çağrı kendi kendine yeterli tek bir mesaj taşır; bağlantı durumu yoktur.
9.12.1. Bir datagram gönderme¶
En basit UDP gönderimi, bir soket yapıcısının üzerinde tek satırlık Python’dur:
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.sendto(b"hello", ("192.168.1.20", 9000))
s.close()
Bu, 192.168.1.20 üzerindeki 9000 portuna b"hello" gönderir ve devam eder. MicroPython geçici bir kaynak portu seçer; betiğin hiçbir şeyi bağlaması gerekmez.
Aynı yükü birçok hedefe göndermek yalnızca bir döngüdür – soket gönderimler arasında yeniden kullanılabilir ve kurulacak bir bağlantı yoktur:
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. Bir datagram alma¶
Datagram almak için soketin, göndericilerin hedef olarak kullanacağı bilinen bir port talep etmesi gerekir. Bu, bind() çağrısıdır:
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)
"0.0.0.0" adresi “kameradaki her IPv4 arabirimi” anlamına gelir – paketleri hangi Wi-Fi veya Ethernet arabirimi getirirse getirsin, 9000 portu bu sokete aittir.
recvfrom() için 1024 argümanı, döndürülen arabelleğe okunacak maksimum bayt sayısıdır. Bu boyuttan büyük UDP datagramları kesilir; değeri uygulamanın beklediği en büyük datagrama uyacak şekilde seçin.
recvfrom() (data, src) döndürür: alınan baytlar ve göndericinin adresi. Göndericinin adresi yanıt verilecek yerdir, bu da küçük bir istek/yanıt protokolü yazmayı kolaylaştırır:
while True:
request, src = s.recvfrom(1024)
if request == b"ping":
s.sendto(b"pong", src)
Varsayılan olarak recvfrom() bir datagram gelene kadar engeller. Engellememesini sağlamaya yönelik kalıplar – zaman aşımları, engellemesiz soketler, asyncio – asyncio ile soketler sayfasındadır.
9.12.3. Bir istek ve bir yanıt¶
İki kısa betik: biri istek gönderir, biri alır ve yanıtlar.
Alıcı:
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)
Gönderici:
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?")
Göndericide dikkat çekmeye değer birkaç şey:
bind()yok veconnect()yok. UDP istemcileri yalnızca gönderir.settimeout()alma çağrısına bir son tarih koyar. İki saniye içinde yanıt gelmezse, çağrı sonsuza kadar engellemek yerineOSErrorfırlatır – kaybolan bir paketi tespit etmenin doğal bir yolu.withbloğu soketi otomatik olarak kapatır.
9.12.4. Datagram boyut sınırları¶
UDP datagramları teoride yaklaşık 64 KB’a kadar olabilir, ancak pratik sınır çok daha küçüktür. Gönderici ile alıcı arasındaki yoldaki her bağlantının bir Maksimum İletim Birimi (MTU) vardır – o bağlantının tek bir çerçevede taşıyabileceği en büyük tek bayt bloğu. Hem Ethernet hem de Wi-Fi bunu yaklaşık 1500 baytla sınırlar ve neredeyse her internet yolu bir yerde bu sınıra dayanır.
Bir datagram, geçmesi gereken bir bağlantının MTU’sunu aştığında, ağ katmanı onu daha küçük parçalara böler ve hedefte yeniden birleştirir. UDP’nin kendisi bölünmeyi hiç görmez, ancak parçaların birkaç sakıncalı özelliği vardır:
Herhangi bir parça kaybolursa, tüm datagram alıcıda atılır – parça başına yeniden iletim yoktur. Kayıp olasılığı parça sayısıyla birlikte artar.
Bazı ağlar ve güvenlik duvarları, parçalanmış paketleri şüpheli sayarak tamamen atar.
Yeniden birleştirme alıcıda bellek tüketir; bu da bir mikrodenetleyicide kıt bir kaynaktır.
Kamerada pratik kural: UDP mesajlarını 1500 baytın oldukça altında tutun. Yaklaşık 1400 bayt; IP ve UDP başlıkları, yolun eklediği herhangi bir tünelleme yükü ve Ethernet, Wi-Fi ve VPN bağlantıları arasındaki küçük MTU farklılıkları için yer bırakır. Bundan fazlasını göndermesi gereken uygulamalar, ya veriyi uygulama katmanında parçalara ayırmalı ya da bölme ve yeniden birleştirmeyi otomatik olarak ele alan TCP’ye geçmelidir.
9.12.5. Sık karşılaşılan tuzaklar¶
UDP’nin paket kaybedebileceğini unutmak. Sessiz bir yerel ağda kusursuz çalışan kod, bazen daha yoğun veya daha geniş bir ağda göze çarpmayan şekillerde başarısız olur. Mesajın varmamış olma olasılığını her zaman göz önünde bulundurarak tasarlayın.
Gönderici göndermeden önce alıcının bağlanmamış olması. Kimsenin dinlemediği bir porta gönderilen datagram sessizce atılır. Önce alıcıyı başlatın.
Yolun MTU’sundan daha büyük bir datagram göndermek. Önceki bölüme bakın – mesajları ~1400 baytın altında tutun.
Yukarıdaki kalıplar, kameranın başvurduğu neredeyse her UDP kullanımını kapsar. Bir sonraki sayfa TCP için eşdeğerini yapar.