diff --git a/examples/multicast_server.py b/examples/multicast_server.py new file mode 100644 index 0000000..e67989f --- /dev/null +++ b/examples/multicast_server.py @@ -0,0 +1,14 @@ +from pythonosc import dispatcher +from pythonosc import osc_server + +dispatch = dispatcher.Dispatcher() +dispatch.map("/*", print) + +address = ("239.255.0.76", 10076) +interface_addr = "192.168.0.120" + +server = osc_server.ThreadingOSCUDPServer(address, dispatch, interface_addr) +#server = osc_server.ThreadingOSCUDPServer(("239.255.0.76", 10076), dispatch) + +print("Listening to {} through {}".format(address, interface_addr)) +server.serve_forever() diff --git a/examples/simple_client.py b/examples/simple_client.py index 3471a10..f9ef43a 100644 --- a/examples/simple_client.py +++ b/examples/simple_client.py @@ -17,11 +17,15 @@ help="The ip of the OSC server") parser.add_argument("--port", type=int, default=5005, help="The port the OSC server is listening on") + parser.add_argument("--ifaceip", default="127.0.0.1", + help="The ip of the interface to send on if multicast") + parser.add_argument("--ttl", type=int, default="1", + help="TTL if multicast") args = parser.parse_args() - client = udp_client.UDPClient(args.ip, args.port) + client = udp_client.UDPClient(args.ip, args.port, args.ttl, args.ifaceip) - for x in range(10): + for x in range(100): msg = osc_message_builder.OscMessageBuilder(address="/filter") msg.add_arg(random.random()) msg = msg.build() diff --git a/examples/simple_server.py b/examples/simple_server.py index 8b37afb..790058b 100644 --- a/examples/simple_server.py +++ b/examples/simple_server.py @@ -20,17 +20,20 @@ def print_compute_handler(unused_addr, args, volume): if __name__ == "__main__": parser = argparse.ArgumentParser() parser.add_argument("--ip", - default="127.0.0.1", help="The ip to listen on") + default="127.0.0.1", help="The ip (multicast or unicast) to listen on") parser.add_argument("--port", type=int, default=5005, help="The port to listen on") + parser.add_argument("--ifaceip", + default="127.0.0.1", help="The ip of the interface to listen on, if multicast") args = parser.parse_args() dispatcher = dispatcher.Dispatcher() - dispatcher.map("/filter", print) - dispatcher.map("/volume", print_volume_handler, "Volume") - dispatcher.map("/logvolume", print_compute_handler, "Log volume", math.log) + dispatcher.map("/*", print) - server = osc_server.ThreadingOSCUDPServer( - (args.ip, args.port), dispatcher) - print("Serving on {}".format(server.server_address)) + address = (args.ip, args.port) + interface_addr = args.ifaceip + + server = osc_server.ThreadingOSCUDPServer(address, dispatcher, interface_addr) + + print("Listening to {} through {}".format(address, interface_addr)) server.serve_forever() diff --git a/pythonosc/osc_server.py b/pythonosc/osc_server.py index bbd476e..6f11498 100644 --- a/pythonosc/osc_server.py +++ b/pythonosc/osc_server.py @@ -31,7 +31,10 @@ """ import asyncio +import ipaddress +import socket import socketserver +import struct import time from pythonosc import osc_bundle @@ -98,13 +101,24 @@ def _is_valid_request(request): osc_bundle.OscBundle.dgram_is_bundle(data) or osc_message.OscMessage.dgram_is_message(data)) - class OSCUDPServer(socketserver.UDPServer): """Superclass for different flavors of OSCUDPServer""" - def __init__(self, server_address, dispatcher): - super().__init__(server_address, _UDPHandler) + def __init__(self, server_address, dispatcher, interface=None): + + if (ipaddress.ip_address(server_address[0]).is_multicast): + super().__init__(('', server_address[1]),_UDPHandler) + if interface is None: + mreq = struct.pack("4sI", socket.inet_aton(server_address[0]), socket.INADDR_ANY) + self.socket.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) + else: + mreq = socket.inet_aton(server_address[0]) + socket.inet_aton(interface) + self.socket.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq) + else: + super().__init__(server_address, _UDPHandler) + self._dispatcher = dispatcher + print(self.allow_reuse_address) def verify_request(self, request, client_address): """Returns true if the data looks like a valid OSC UDP datagram.""" @@ -115,7 +129,6 @@ def dispatcher(self): """Dispatcher accessor for handlers to dispatch osc messages.""" return self._dispatcher - class BlockingOSCUDPServer(OSCUDPServer): """Blocking version of the UDP server. diff --git a/pythonosc/udp_client.py b/pythonosc/udp_client.py index 8c2f3f9..eb43abb 100644 --- a/pythonosc/udp_client.py +++ b/pythonosc/udp_client.py @@ -1,18 +1,24 @@ """Client to send OSC datagrams to an OSC server via UDP.""" +import ipaddress import socket - class UDPClient(object): """OSC client to send OscMessages or OscBundles via UDP.""" - def __init__(self, address, port): + def __init__(self, address, port, ttl_hops=1, interface=None): """Initialize the client. As this is UDP it will not actually make any attempt to connect to the given server at ip:port until the send() method is called. """ self._sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + + if (ipaddress.ip_address(address).is_multicast): + self._sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, ttl_hops) + if (interface != None): + self._sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_IF, socket.inet_aton(interface)) + self._sock.setblocking(0) self._address = address self._port = port