Easiest Way to Perform Asynchronous Socket Reading

2019-07-31 10:50发布

问题:

I am trying to find the easiest way to read from multiple (around 100) udp datagram sockets in python. I have looked at tornado, but tornado touts http/tcp rather than udp support.

Right now I have threads dedicated to each udp socket; however, this doesn't seem very efficient.

回答1:

I must confess I never used it, but maybe Twisted will suit your needs.

It supports lots of protocols, even serial connections.



回答2:

The SocketServer module has a built-in UDP server with options for threading and forking.

Another option is the use the select module which will allow you to focus only on the sockets where data is already available for reading.



回答3:

I'd like to add some comments on the initial question even though it already has an accepted answer.

If you have multiple connections which need independent processing (or at least processing with not much synchronization), its okay to use one thread per connection and do blocking reads. Modern schedulers won't kill you for that one. It is a fairly efficient way of handling the connections. If you are concerned about memory footprint, you could reduce the stack size of the threads accordingly (does not apply to python).

The threads/processes will stay in a non-busy waiting state for most of the time (while waiting for new data) and not consume any CPU time.

If you do not want or can not use threads, the select call is definetly the way to go. This is also low-level and efficient waiting and as a bonus, gives you the list of sockets which triggered.



回答4:

asyncoro supports asynchronous TCP and UDP sockets (among many other features). Unlike with other frameworks, programming with asyncoro is very similar to that of threads. A simple UDP client/server program to illustrate:

import socket, asyncoro

def server_proc(n, sock, coro=None):
    for i in xrange(n):
        msg, addr = yield sock.recvfrom(1024)
        print('Received "%s" from %s:%s' % (msg, addr[0], addr[1]))
    sock.close()

def client_proc(host, port, coro=None):
    sock = asyncoro.AsynCoroSocket(socket.socket(socket.AF_INET, socket.SOCK_DGRAM))
    msg = 'client socket: %s' % (sock.fileno())
    yield sock.sendto(msg, (host, port))
    sock.close()

if __name__ == '__main__':
    sock = asyncoro.AsynCoroSocket(socket.socket(socket.AF_INET, socket.SOCK_DGRAM))
    sock.bind(('127.0.0.1', 0))
    host, port = sock.getsockname()

    n = 100
    server_coro = asyncoro.Coro(server_proc, n, sock)
    for i in range(n):
        asyncoro.Coro(client_proc, host, port)

asyncoro uses efficient polling mechanisms where possible. Only with Windows and UDP sockets it uses inefficient 'select' (but uses efficient Windows I/O Completion Ports for TCP if pywin32 is installed).



回答5:

I think if you do insist on using tornado's ioloop and want to do UDP socket processing, you should use a UDP version of the tornado IOStream. I have done this with success in my own projects. It is a little bit of a misnomer to call it UDPStream (since it is not quite a stream), but the basic use should be very easy for you to integrate into your application.

See the code at: http://kyle.graehl.org/coding/2012/12/07/tornado-udpstream.html