9.12. UDP sockets¶
Lalu lintas UDP dalam Python dikirim dan diterima dengan dua metode pada datagram socket: sendto() untuk mengirimkan datagram ke tujuan yang dipilih, dan recvfrom() untuk menerima datagram dan mengetahui asalnya. Setiap panggilan memindahkan satu pesan mandiri; tidak ada status koneksi.
9.12.1. Mengirim datagram¶
Pengiriman UDP paling sederhana adalah satu baris Python di atas konstruktor socket:
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.sendto(b"hello", ("192.168.1.20", 9000))
s.close()
Itu mengirimkan b"hello" ke port 9000 pada 192.168.1.20 dan selesai. MicroPython memilih port sumber sementara; skrip tidak harus mengikat apa pun.
Mengirimkan payload yang sama ke banyak tujuan hanyalah sebuah loop -- socket dapat digunakan kembali antar pengiriman, dan tidak ada koneksi yang perlu disiapkan:
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. Menerima datagram¶
Untuk menerima datagram, socket harus mengklaim port yang dikenal yang akan digunakan pengirim sebagai tujuan mereka. Itulah panggilan 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)
Alamat "0.0.0.0" berarti "setiap antarmuka IPv4 pada kamera" -- antarmuka Wi-Fi atau Ethernet apa pun yang membawa paket masuk, port 9000 milik socket ini.
Argumen 1024 untuk recvfrom() adalah jumlah byte maksimum yang akan dibaca ke dalam buffer yang dikembalikan. Datagram UDP di atas ukuran ini akan dipotong; pilih nilai sesuai datagram terbesar yang diharapkan oleh aplikasi.
recvfrom() mengembalikan (data, src): byte yang diterima, dan alamat pengirim. Alamat pengirim adalah tempat untuk membalas, sehingga mudah menulis protokol permintaan/respons kecil:
while True:
request, src = s.recvfrom(1024)
if request == b"ping":
s.sendto(b"pong", src)
Secara default recvfrom() memblokir hingga datagram tiba. Pola untuk membuatnya tidak memblokir -- timeout, socket non-blocking, asyncio -- ada di Socket dengan asyncio.
9.12.3. Permintaan dan balasan¶
Dua skrip pendek: satu mengirim permintaan, satu menerima dan membalas.
Penerima:
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)
Pengirim:
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?")
Beberapa hal yang perlu diperhatikan pada pengirim:
Tidak ada
bind()dan tidak adaconnect(). Klien UDP hanya mengirim.settimeout()menetapkan batas waktu pada panggilan terima. Jika tidak ada balasan yang datang dalam dua detik, panggilan memunculkanOSErrordaripada memblokir selamanya -- cara alami untuk mendeteksi paket yang hilang.Blok
withmenutup socket secara otomatis.
9.12.4. Batas ukuran datagram¶
Datagram UDP secara teori dapat mencapai sekitar 64 KB, tetapi batas praktisnya jauh lebih kecil. Setiap tautan dalam jalur antara pengirim dan penerima memiliki Maximum Transmission Unit (MTU) -- blok byte tunggal terbesar yang dapat dibawa tautan dalam satu bingkai. Ethernet dan Wi-Fi keduanya membatasi ini sekitar 1500 byte, dan hampir setiap jalur internet menelusuri batas itu di suatu tempat.
Ketika datagram melebihi MTU dari tautan yang harus dilintasi, lapisan jaringan membaginya menjadi fragmen yang lebih kecil dan merakitnya kembali di tujuan. UDP sendiri tidak pernah melihat pemisahan itu, tetapi fragmen memiliki beberapa sifat yang tidak nyaman:
Jika satu fragmen hilang, seluruh datagram dibuang di penerima -- tidak ada retransmisi per fragmen. Probabilitas kehilangan bertambah seiring jumlah fragmen.
Beberapa jaringan dan firewall membuang paket yang terfragmentasi sepenuhnya, memperlakukannya sebagai mencurigakan.
Perakitan ulang menghabiskan memori di penerima, yang pada mikrokontroler sangat terbatas.
Aturan praktis pada kamera: jaga pesan UDP jauh di bawah 1500 byte. Sekitar 1400 byte menyisakan ruang untuk header IP dan UDP, overhead tunneling apa pun yang ditambahkan jalur, dan variasi kecil dalam MTU antara Ethernet, Wi-Fi, dan tautan VPN. Aplikasi yang perlu mengirim lebih dari itu harus membagi data di lapisan aplikasi atau beralih ke TCP, yang menangani pemisahan dan perakitan ulang secara otomatis.
9.12.5. Jebakan umum¶
Lupa bahwa UDP dapat kehilangan paket. Kode yang bekerja sempurna di jaringan lokal yang tenang kadang-kadang gagal dengan cara halus di jaringan yang lebih sibuk atau lebih luas. Selalu rancang untuk kemungkinan bahwa pesan tidak tiba.
Penerima tidak terikat sebelum pengirim mengirim. Datagram yang dikirim ke port yang tidak ada yang mendengarkan dibuang tanpa pemberitahuan. Mulai penerima terlebih dahulu.
Mengirim datagram yang lebih besar dari MTU jalur. Lihat bagian sebelumnya -- jaga pesan di bawah ~1400 byte.
Pola di atas mencakup hampir setiap penggunaan UDP yang dijangkau kamera. Halaman berikutnya melakukan hal yang setara untuk TCP.