Reputation: 21
so i am learning how to make a simple chat application , but its a part of the assignment and I must use asyncio for it. The problem is that there is a runtime error raised when the client presses Ctrl-C and I dont know how to fix it and the second problem is that When the server terminates, the client hangs, waiting for users input; in contrast, I would like the client to terminate as well, just after printing The server closed the connection. here is the code for the server:
import sys, asyncio
all_clients = set([])
@asyncio.coroutine
def handle_connection(reader, writer):
all_clients.add(writer)
client_addr = writer.get_extra_info('peername')
print('New client {}'.format(client_addr))
while True:
data = yield from reader.read(100)
if data == None or len(data) == 0:
break
message = data.decode()
print("Received {} from {}".format(message, client_addr))
for other_writer in all_clients:
if other_writer != writer:
new_message = '{} says: {}'.format(client_addr,data)
other_writer.write(new_message.encode())
yield from other_writer.drain()
all_clients.remove(writer)
print("Close the client socket")
yield from writer.drain()
writer.close()
def run():
loop = asyncio.get_event_loop()
coro = asyncio.start_server(handle_connection,'127.0.0.1',
8888,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:
print('\nGot keyboard interrupt, shutting down',file=sys.stderr)
sys.exit()
for task in asyncio.Task.all_tasks():
task.cancel()
server.close()
loop.run_until_complete(server.wait_closed())
loop.close()
if __name__ == '__main__':
run()
here is the code for the client:
import sys, asyncio
import aioconsole
class NoneException(Exception):
pass
class ClosingException(Exception):
pass
@asyncio.coroutine
def open_connection(loop):
reader, writer = yield from asyncio.open_connection('127.0.0.1',
8888,loop=loop)
return reader, writer
@asyncio.coroutine
def use_connection(reader, writer):
yield from asyncio.gather(read_from_network(reader,writer),
send_to_server(writer))
@asyncio.coroutine
def read_from_network(reader,writer):
while True:
net_message = yield from reader.read(100)
if writer.transport.is_closing():
print('Terminating read from network.')
break
elif net_message == None:
continue
elif len(net_message) == 0:
print('The server closed the connection.')
writer.close()
break
print('\nReceived: %r' % net_message.decode())
print('>> ',end='',flush=True)
@asyncio.coroutine
def send_to_server(writer):
try:
while True:
original_message = yield from aioconsole.ainput('>> ')
if original_message != None:
console_message = original_message.strip()
if console_message == '':
continue
if console_message == 'close()' or \
writer.transport.is_closing():
raise ClosingException()
writer.write(console_message.encode())
except ClosingException:
print('Got close() from user.')
finally:
if not writer.transport.is_closing():
writer.close()
def run():
try:
loop = asyncio.get_event_loop()
reader,writer=loop.run_until_complete(open_connection(loop))
loop.run_until_complete(use_connection(reader,writer))
except KeyboardInterrupt:
print('Got Ctrl-C from user.')
except Exception as e:
print(e,file=sys.stderr)
finally:
loop.close()
if __name__ == '__main__':
run()
here are the details or the runtime error raised when the client presses Ctrl-C
^CGot Ctrl-C from user. Exception ignored in: Traceback (most recent call last): File "client.py", line 56, in send_to_server File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/asyncio/streams.py", line 306, in close File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/asyncio/selector_events.py", line 622, in close File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/asyncio/base_events.py", line 573, in call_soon File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/asyncio/base_events.py", line 357, in _check_closed RuntimeError: Event loop is closed
Upvotes: 1
Views: 1154
Reputation: 2174
User writer.close()
before closing the event loop. No need to close it in your send_to_server
method.
def run():
try:
loop = asyncio.get_event_loop()
reader,writer=loop.run_until_complete(open_connection(loop))
loop.run_until_complete(use_connection(reader,writer))
except KeyboardInterrupt:
print('Got Ctrl-C from user.')
# closing writer before the event loop
writer.close()
except Exception as e:
print(e,file=sys.stderr)
finally:
loop.close()
Upvotes: 1