Bagaimana Anda mengirim dan menerima multicast UDP dengan Python? Apakah ada perpustakaan standar untuk melakukannya?
Jawaban:
Ini bekerja untuk saya:
Menerima
import socket
import struct
MCAST_GRP = '224.1.1.1'
MCAST_PORT = 5007
IS_ALL_GROUPS = True
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
if IS_ALL_GROUPS:
# on this port, receives ALL multicast groups
sock.bind(('', MCAST_PORT))
else:
# on this port, listen ONLY to MCAST_GRP
sock.bind((MCAST_GRP, MCAST_PORT))
mreq = struct.pack("4sl", socket.inet_aton(MCAST_GRP), socket.INADDR_ANY)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
while True:
# For Python 3, change next line to "print(sock.recv(10240))"
print sock.recv(10240)
Kirim
import socket
MCAST_GRP = '224.1.1.1'
MCAST_PORT = 5007
# regarding socket.IP_MULTICAST_TTL
# ---------------------------------
# for all packets sent, after two hops on the network the packet will not
# be re-sent/broadcast (see https://www.tldp.org/HOWTO/Multicast-HOWTO-6.html)
MULTICAST_TTL = 2
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, MULTICAST_TTL)
# For Python 3, change next line to 'sock.sendto(b"robot", ...' to avoid the
# "bytes-like object is required" msg (https://stackoverflow.com/a/42612820)
sock.sendto("robot", (MCAST_GRP, MCAST_PORT))
Ini didasarkan pada contoh dari http://wiki.python.org/moin/UdpCommunication yang tidak berfungsi.
Sistem saya adalah ... Linux 2.6.31-15-generik # 50-Ubuntu SMP Sel 10 Nov 14:54:29 UTC 2009 i686 GNU / Linux Python 2.6.4
sock.bind((MCAST_GRP, MCAST_PORT))
, kode Anda mungkin dan mungkin tidak berfungsi, mungkin tidak berfungsi ketika Anda memiliki banyak
Pengirim multicast yang menyiarkan ke grup multicast:
#!/usr/bin/env python
import socket
import struct
def main():
MCAST_GRP = '224.1.1.1'
MCAST_PORT = 5007
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 32)
sock.sendto('Hello World!', (MCAST_GRP, MCAST_PORT))
if __name__ == '__main__':
main()
Penerima multicast yang membaca dari grup multicast dan mencetak data hex ke konsol:
#!/usr/bin/env python
import socket
import binascii
def main():
MCAST_GRP = '224.1.1.1'
MCAST_PORT = 5007
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
try:
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
except AttributeError:
pass
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 32)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_LOOP, 1)
sock.bind((MCAST_GRP, MCAST_PORT))
host = socket.gethostbyname(socket.gethostname())
sock.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_IF, socket.inet_aton(host))
sock.setsockopt(socket.SOL_IP, socket.IP_ADD_MEMBERSHIP,
socket.inet_aton(MCAST_GRP) + socket.inet_aton(host))
while 1:
try:
data, addr = sock.recvfrom(1024)
except socket.error, e:
print 'Expection'
hexdata = binascii.hexlify(data)
print 'Data = %s' % hexdata
if __name__ == '__main__':
main()
sock.bind((MCAST_GRP, MCAST_PORT))
Untuk Bergabung dengan grup multicast, Python menggunakan antarmuka soket OS asli. Karena portabilitas dan stabilitas lingkungan Python, banyak opsi soket secara langsung diteruskan ke panggilan setockopt soket asli. Mode operasi multicast seperti bergabung dan membatalkan keanggotaan grup dapat dilakukan dengansetsockopt
.
Program dasar untuk menerima paket IP multicast dapat berupa:
from socket import *
multicast_port = 55555
multicast_group = "224.1.1.1"
interface_ip = "10.11.1.43"
s = socket(AF_INET, SOCK_DGRAM )
s.bind(("", multicast_port ))
mreq = inet_aton(multicast_group) + inet_aton(interface_ip)
s.setsockopt(IPPROTO_IP, IP_ADD_MEMBERSHIP, str(mreq))
while 1:
print s.recv(1500)
Pertama, ia membuat soket, mengikatnya dan memicu grup multicast bergabung dengan mengeluarkan setsockopt
. Pada akhirnya, ia menerima paket selamanya.
Mengirim frame IP multicast sangatlah mudah. Jika Anda memiliki NIC tunggal dalam sistem Anda, pengiriman paket tersebut tidak berbeda dari pengiriman frame UDP biasa. Yang perlu Anda perhatikan hanyalah mengatur alamat IP tujuan yang benarsendto()
metode.
Saya perhatikan bahwa banyak contoh seputar Internet bekerja secara tidak sengaja. Bahkan pada dokumentasi resmi python. Masalah untuk mereka semua adalah menggunakan struct.pack secara tidak benar. Harap diperhatikan bahwa penggunaan contoh biasa4sl
sebagai format dan tidak selaras dengan struktur antarmuka soket OS yang sebenarnya.
Saya akan mencoba menjelaskan apa yang terjadi di bawah kap saat menjalankan panggilan setsockopt untuk objek soket python.
Python meneruskan panggilan metode setsockopt ke antarmuka soket C asli. Dokumentasi soket Linux (lihat man 7 ip
) memperkenalkan dua bentuk ip_mreqn
struktur untuk opsi IP_ADD_MEMBERSHIP. Bentuk terpendek adalah 8 byte dan yang lebih panjang adalah 12 byte. Contoh di atas menghasilkan setsockopt
panggilan 8 byte di mana empat byte pertama mendefinisikan multicast_group
dan empat byte kedua mendefinisikan interface_ip
.
Lihat py-multicast . Modul jaringan dapat memeriksa apakah suatu antarmuka mendukung multicast (setidaknya di Linux).
import multicast
from multicast import network
receiver = multicast.MulticastUDPReceiver ("eth0", "238.0.0.1", 1234 )
data = receiver.read()
receiver.close()
config = network.ifconfig()
print config['eth0'].addresses
# ['10.0.0.1']
print config['eth0'].multicast
#True - eth0 supports multicast
print config['eth0'].up
#True - eth0 is up
Mungkin masalah tidak melihat IGMP, disebabkan oleh antarmuka yang tidak mendukung multicast?
Hanya jawaban lain untuk menjelaskan beberapa poin halus dalam kode jawaban lain:
socket.INADDR_ANY
- (Diedit) Dalam konteks IP_ADD_MEMBERSHIP
, ini tidak benar-benar mengikat soket ke semua antarmuka tetapi hanya memilih antarmuka default di mana multicast sudah aktif (menurut tabel perutean)lihat Apa artinya mengikat soket multicast (UDP)?untuk mengetahui lebih lanjut tentang cara kerja multicast
Penerima multicast:
import socket
import struct
import argparse
def run(groups, port, iface=None, bind_group=None):
# generally speaking you want to bind to one of the groups you joined in
# this script,
# but it is also possible to bind to group which is added by some other
# programs (like another python program instance of this)
# assert bind_group in groups + [None], \
# 'bind group not in groups to join'
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
# allow reuse of socket (to allow another instance of python running this
# script binding to the same ip/port)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('' if bind_group is None else bind_group, port))
for group in groups:
mreq = struct.pack(
'4sl' if iface is None else '4s4s',
socket.inet_aton(group),
socket.INADDR_ANY if iface is None else socket.inet_aton(iface))
sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
while True:
print(sock.recv(10240))
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--port', type=int, default=19900)
parser.add_argument('--join-mcast-groups', default=[], nargs='*',
help='multicast groups (ip addrs) to listen to join')
parser.add_argument(
'--iface', default=None,
help='local interface to use for listening to multicast data; '
'if unspecified, any interface would be chosen')
parser.add_argument(
'--bind-group', default=None,
help='multicast groups (ip addrs) to bind to for the udp socket; '
'should be one of the multicast groups joined globally '
'(not necessarily joined in this python program) '
'in the interface specified by --iface. '
'If unspecified, bind to 0.0.0.0 '
'(all addresses (all multicast addresses) of that interface)')
args = parser.parse_args()
run(args.join_mcast_groups, args.port, args.iface, args.bind_group)
contoh penggunaan: (jalankan di bawah ini dalam dua konsol dan pilih sendiri --iface (harus sama dengan antarmuka yang menerima data multicast))
python3 multicast_recv.py --iface='192.168.56.102' --join-mcast-groups '224.1.1.1' '224.1.1.2' '224.1.1.3' --bind-group '224.1.1.2'
python3 multicast_recv.py --iface='192.168.56.102' --join-mcast-groups '224.1.1.4'
Pengirim multicast:
import socket
import argparse
def run(group, port):
MULTICAST_TTL = 20
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, MULTICAST_TTL)
sock.sendto(b'from multicast_send.py: ' +
f'group: {group}, port: {port}'.encode(), (group, port))
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--mcast-group', default='224.1.1.1')
parser.add_argument('--port', default=19900)
args = parser.parse_args()
run(args.mcast_group, args.port)
contoh penggunaan: # anggap penerima mengikat ke alamat grup multicast di bawah ini dan bahwa beberapa program meminta untuk bergabung dengan grup itu. Dan untuk menyederhanakan kasus, asumsikan penerima dan pengirim berada di bawah subnet yang sama
python3 multicast_send.py --mcast-group '224.1.1.2'
python3 multicast_send.py --mcast-group '224.1.1.4'
Untuk membuat kode klien (dari tolomea) berfungsi di Solaris, Anda harus meneruskan nilai ttl untuk IP_MULTICAST_TTL
opsi socket sebagai unsigned char. Jika tidak, Anda akan mendapatkan error. Ini berhasil untuk saya di Solaris 10 dan 11:
import socket
import struct
MCAST_GRP = '224.1.1.1'
MCAST_PORT = 5007
ttl = struct.pack('B', 2)
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, ttl)
sock.sendto("robot", (MCAST_GRP, MCAST_PORT))
Contoh ini tidak berhasil untuk saya, karena alasan yang tidak jelas.
Tidak jelas, ini perutean sederhana.
Di OpenBSD
route add -inet 224.0.0.0/4 224.0.0.1
Anda dapat mengatur rute ke dev di Linux
route add -net 224.0.0.0 netmask 240.0.0.0 dev wlp2s0
memaksa semua lalu lintas multicast ke satu antarmuka di Linux
ifconfig wlp2s0 allmulti
tcpdump sangat sederhana
tcpdump -n multicast
Dalam kode Anda, Anda memiliki:
while True:
# For Python 3, change next line to "print(sock.recv(10240))"
Mengapa 10240 ?
ukuran paket multicast harus 1316 byte
jawaban tolomea berhasil untukku. Saya meretasnya ke socketserver.UDPServer juga:
class ThreadedMulticastServer(socketserver.ThreadingMixIn, socketserver.UDPServer):
def __init__(self, *args):
super().__init__(*args)
self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.socket.bind((MCAST_GRP, MCAST_PORT))
mreq = struct.pack('4sl', socket.inet_aton(MCAST_GRP), socket.INADDR_ANY)
self.socket.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)