Reputation: 442
The example server in pydocs is as follows:
import asyncio
@asyncio.coroutine
def handle_echo(reader, writer):
data = yield from reader.read(100)
message = data.decode()
addr = writer.get_extra_info('peername')
print("Received %r from %r" % (message, addr))
print("Send: %r" % message)
writer.write(data)
yield from writer.drain()
print("Close the client socket")
writer.close()
loop = asyncio.get_event_loop()
coro = asyncio.start_server(handle_echo, '127.0.0.1', 8889, loop=loop)
server = loop.run_until_complete(coro)
# Serve requests until Ctrl+C is pressed
print('Serving on {}'.format(server.sockets[0].getsockname()))
try:
loop.run_forever()
except KeyboardInterrupt:
pass
# Close the server
server.close()
loop.run_until_complete(server.wait_closed())
loop.close()
I adapted the example client to make two consecutive requests:
import asyncio
async def tcp_echo_client(loop):
reader, writer = await asyncio.open_connection('127.0.0.1', 8889,
loop=loop)
await make_request(reader, writer, "Foo")
await make_request(reader, writer, "Bar")
print('Close the socket')
writer.close()
async def make_request(reader, writer, message):
print('Send: %r' % message)
writer.write(message.encode())
data = await reader.read(100)
print('Received: %r' % data.decode())
loop = asyncio.get_event_loop()
loop.run_until_complete(tcp_echo_client(loop))
loop.close()
The observed behavior is that the client gets a response to the first request, but the socket is closed before the second request is handled. As such, the client receives no response to the second request.
Server logs:
Received 'Foo' from ('127.0.0.1', 58112)
Send: 'Foo'
Close the client socket
Client logs:
Send: 'Foo'
Received: 'Foo'
Send: 'Bar'
Received: ''
Close the socket
Desired behavior is for the client to receive Bar
in response to the second request, keeping the socket open between requests.
If I comment out closing the socket on the server, it blocks such that the client never reads the response to the first request, even though the buffer should have been flushed by the write.drain()
.
Guidance would be most appreciated; thanks in advance.
Upvotes: 0
Views: 3604
Reputation: 442
I think I figured this out. Looking at the server code:
asyncio.start_server
doesn't repeatedly call handle_echo
in it's event loop. I'm responsible for writing a loop within that handler function, reusing reader
and writer
for each request.yield from reader.read(100)
returns 0 bytes after the client closes it's end of the socket.As such, here's the new server code, which works as intended:
import asyncio
@asyncio.coroutine
def handle_echo(reader, writer):
while True:
data = yield from reader.read(100)
if len(data) > 0:
message = data.decode()
addr = writer.get_extra_info('peername')
print("Received %r from %r" % (message, addr))
print("Send: %r" % message)
writer.write(data)
yield from writer.drain()
else:
print("Close the client socket")
writer.close()
break
loop = asyncio.get_event_loop()
coro = asyncio.start_server(handle_echo, '127.0.0.1', 8889, loop=loop)
server = loop.run_until_complete(coro)
# Serve requests until Ctrl+C is pressed
print('Serving on {}'.format(server.sockets[0].getsockname()))
try:
loop.run_forever()
except KeyboardInterrupt:
pass
# Close the server
server.close()
loop.run_until_complete(server.wait_closed())
loop.close()
Upvotes: 1