Search code examples
linuxsocketsudpasio

UDP Networking with multiple sockets


I'm trying to create a server application which essentially controls multiple sockets for data sent over a local network.

I firstly did some tests both on linux and windows with they're libraries (such as Winsock) and also with the ASIO library as well, but the goal is to have it run in a linux enviroment.

The complete application would be something that opens N sockets (possibily multithreading asynchronously) and essentially does nothing untill every socket has received some data. Only at that point, the server will analyze the packets and do some conversions stuff. This is because those data will have different structures, and based on this a different connection has been requested (that's at least what I'm asked to do). So at a fixed time, I would have N packets available, which will then be "re-composed" by the server.

Essentially, each socket is somewhat "bound" to the data-type it's supposed to receive.

Now the problem is that I'm not understanding which solution to implement for this scenario, because I'm "forced" to use the UDP protocol, as well as using different sockets. As far as I know, for this kind of stuff TCP protocol is the one used, so that each socket-pair can establish a connection and do its own stuff.

Moreover, I cannot use a single UDP socket to receive everything. This essentially because: A- I've been asked differently; B- even if timing of packets will not be a real problem, I could not receive them all mixed up, 'cause it would need sending additional information from the clients (on which I do not have control).

In fact now I understand why there's not that much of documentation available about UDP scenarios. I would gladly switch to TCP, so that at least I've would have a rough idea of what to do, but at the moment I can't.

Can you help me out to understand better the situation?


Solution

  • As the comments suggested, you can monitor multiple ports with the select function. It takes lists of ports to check for readability, writability, or errors and returns lists of ports in the requested state.

    Below is an example watching 5 ports for readability, and exits when all 5 ports have received something:

    import socket
    import select
    
    N = 5
    base_port = 5000
    
    # Create N sockets and bind to ports 5000 to 5000+N-1:
    socks = [socket.socket(type=socket.SOCK_DGRAM) for _ in range(N)]
    for i,s in enumerate(socks):
        s.bind(('',base_port+i))
    
    # Flags to track that something was received on a particular port
    got = [False] * N
    
    while not all(got):
        # Watch all N sockets for readability only
        readable,writable,errors = select.select(socks,[],[])
    
        # Only a socket that has something to read will be in "readable"
        for s in readable:
            port = s.getsockname()[1] # which port was it
            data,addr = s.recvfrom(4096)
            print(f'From {port}: {data}')
            got[port-5000] = True
    
    print("Got'em all!")
    

    Demo (client)

    >>> from socket import *
    >>> s=socket(type=SOCK_DGRAM)
    >>> s.sendto(b'abc',('localhost',5001))
    3
    >>> s.sendto(b'abc',('localhost',5002))
    3
    >>> s.sendto(b'abc',('localhost',5003))
    3
    >>> s.sendto(b'abc',('localhost',5004))
    3
    >>> s.sendto(b'abc',('localhost',5000))
    3
    

    Demo (server)

    From 5001: b'abc'
    From 5002: b'abc'
    From 5003: b'abc'
    From 5004: b'abc'
    From 5000: b'abc'
    Got'em all!