Browse Source

add a module for doing multicast work..

irr_shared
John-Mark Gurney 3 years ago
parent
commit
6c383ce883
2 changed files with 93 additions and 2 deletions
  1. +2
    -2
      Makefile
  2. +91
    -0
      multicast.py

+ 2
- 2
Makefile View File

@@ -168,8 +168,8 @@ runbuild: $(SRCS)
for i in $(.MAKEFILE_LIST) $(.ALLSRC) $$(cat $(DEPENDS) | gsed ':x; /\\$$/ { N; s/\\\n//; tx }' | sed -e 's/^[^:]*://'); do if [ "$$i" != ".." ]; then echo $$i; fi; done | entr -d sh -c 'echo starting...; cd $(.CURDIR) && $(MAKE) $(.MAKEFLAGS) depend && $(MAKE) $(.MAKEFLAGS) all'

.PHONY: runtests
runtests: Makefile lora_comms.py lora.py $(LIBLORA_TEST) $(LIBLORA_TEST_SRCS)
ls $(.ALLSRC) | entr sh -c '(cd $(.CURDIR) && $(MAKE) $(.MAKEFLAGS) $(LIBLORA_TEST)) && ((PYTHONPATH="$(.CURDIR)" python -m coverage run -m unittest lora && coverage report --omit=$(.CURDIR)/p/\* -m -i) 2>&1 | head -n 30)'
runtests: Makefile lora_comms.py lora.py multicast.py $(LIBLORA_TEST) $(LIBLORA_TEST_SRCS)
ls $(.ALLSRC) | entr sh -c '(cd $(.CURDIR) && $(MAKE) $(.MAKEFLAGS) $(LIBLORA_TEST)) && ((PYTHONPATH="$(.CURDIR)" python -m coverage run -m unittest lora multicast && coverage report --omit=$(.CURDIR)/p/\* -m -i) 2>&1 | head -n 30)'

# native objects
.SUFFIXES: .no


+ 91
- 0
multicast.py View File

@@ -0,0 +1,91 @@
import asyncio
import random
import socket
import struct
import unittest

from lora import timeout

# This function based upon code from:
# https://gist.github.com/petrdvor/e802bec72e78ace061ab9d4469418fae#file-async-multicast-receiver-server-py-L54-L72
def make_multisock(maddr):
# family, type, proto, ??, addr)
addrinfo = socket.getaddrinfo(*maddr, type=socket.SOCK_DGRAM)[0]

sock = socket.socket(*addrinfo[:2])
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)

sock.bind(maddr)

group_bin = socket.inet_pton(addrinfo[0], addrinfo[4][0])
mreq = group_bin + struct.pack('=I', socket.INADDR_ANY)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)

return sock

class StupidProtocol(object):
def __init__(self):
self.transport = None

def connection_made(self, transport):
# Note: the connection_made call seems to be sync. This
# isn't documented, and I don't know how to force a test
# if it isn't.
self.transport = transport

class ReceiverProtocol(StupidProtocol):
def __init__(self):
super().__init__()

self._q = asyncio.Queue()

def datagram_received(self, data, addr):
self._q.put_nowait((data, addr))

async def recv(self):
return await self._q.get()

async def create_multicast_receiver(maddr):
sock = make_multisock(maddr)

loop = asyncio.get_running_loop()
transport, protocol = await loop.create_datagram_endpoint(
lambda: ReceiverProtocol(),
sock=sock)

return protocol

class TransmitterProtocol(StupidProtocol):
async def send(self, msg):
self.transport.sendto(msg)

async def create_multicast_transmitter(maddr):
loop = asyncio.get_running_loop()
transport, protocol = await loop.create_datagram_endpoint(
lambda: TransmitterProtocol(),
remote_addr=maddr)

return protocol

class TestMulticast(unittest.IsolatedAsyncioTestCase):
@timeout(2)
async def test_multicast(self):
# see: https://www.iana.org/assignments/multicast-addresses/multicast-addresses.xhtml#multicast-addresses-1
maddr = ('224.0.0.%d' % random.randint(151, 250), 3485)

l1 = await create_multicast_receiver(maddr)
l2 = await create_multicast_receiver(maddr)

t1 = await create_multicast_transmitter(maddr)
print('tm:', repr(t1))

msg = b'test message'

await t1.send(msg)
await t1.send(msg)

self.assertEqual((await l1.recv())[0], msg)
self.assertEqual((await l2.recv())[0], msg)

self.assertEqual((await l1.recv())[0], msg)
self.assertEqual((await l2.recv())[0], msg)

Loading…
Cancel
Save