Victor Stinner
unread,Feb 26, 2015, 8:37:55 AM2/26/15Sign in to reply to author
Sign in to forward
You do not have permission to delete messages in this group
Either email addresses are anonymous for this group or you need the view member email addresses permission to view the original message
to python-tulip
Hi,
I'm trying to port a project using eventlet to asyncio, but I don't
know how to use UDP. I like asyncio.open_connection() because it makes
possible to write sequential code using yield from.
UDP doesn't seem to be well supported in asyncio today :-( For
example, there are loop.sock_recv() and loop.sock_sendall() method for
stream protocols (TCP), but no loop.sock_recvfrom() or
loop.sock_sendto() for datagram protocols (UDP).
I know that UDP is not connected and has almost no guarantee that data
is sent, but sending a datagram may take time. sendto() can returns
temporarly EWOULDBLOCK. We have to buffer datagrams.
I see different options to write sequential UDP code:
- use a protocol which writes into a queue, and read from the queue
(no change required in asyncio)
- add sock_recvfrom() and sock_sendto() method to the event loop (it
would be better to implement it in asyncio to have portable code)
- add stream classes like asyncio.open_connection() for TCP (may be
implemented in a third party library/code, until the idea is accepted
into asyncio)
For an UDP client, the stream API can be as simple as
reader.read(data) and writer.write(data), the address can be implicit
(it's always the same).
For a UDP server, it's more complex. Sending a datagram requires an
explicit address. Receiving a datagram requires to provide the
address, so StreamReader.read() API doesn't fit.
When the listening socket becomes readable, we call recvfrom() which
returns (data, addr). Does it make sense to concatenate two datagrams
from the same client? I don't think so. So we should store (data,
addr) in a FIFO container and provide an API to pop one tuple, called
"recvfrom" for example.
New question: how should we specify the size parameter of recvfrom()?
recvfrom() has a size parameter. Currently, the default size is 256
KB. For TCP, it doesn't really matter since we can just buffer small
or large packets, there are all concatenated, the consumer specifies
how much bytes are required (read(n)).
It's possible to change the max_size attribute of a
_SelectorDatagramTransport, but I'm not sure that this attribute is
currently not documented and it has an issue. When a transport is
created, the transport immediatly starts to listen for read event. If
the max_size attribute is modified too late, the transport may already
have called recv() or recvfrom() with the old max_size value.
The max_size attribute is specific to selectors based on the select()
API. On Windows, a proactor event loop doesn't have such default size,
since the transport only starts reading when a recv() method is
explicitly called.
The code using eventlet (socket is eventlet.green.socket, not socket
from the Python stdlib):
def start_udp(self):
udp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
udp.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
udp.bind((cfg.CONF.collector.udp_address,
cfg.CONF.collector.udp_port))
self.udp_run = True
while self.udp_run:
data, source = udp.recvfrom(64 * 1024)
sample = msgpack.loads(data, encoding='utf-8')
self.dispatcher_manager.map_method('record_metering_data',
sample)
Victor