使用 asyncio 的 Python 网络
Posted
技术标签:
【中文标题】使用 asyncio 的 Python 网络【英文标题】:Python Networking with asyncio 【发布时间】:2018-07-09 16:51:11 【问题描述】:所以我正在学习如何制作一个简单的聊天应用程序,但它是作业的一部分,我必须为此使用 asyncio。问题是当客户端按下 Ctrl-C 时出现运行时错误,我不知道如何修复它 第二个问题是当服务器终止时,客户端挂起,等待用户输入;相反,我希望客户 打印后也终止服务器关闭连接。 这是服务器的代码:
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()
这是客户端的代码:
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()
这里是详细信息或客户端按下 Ctrl-C 时引发的运行时错误
^C从用户那里得到 Ctrl-C。 异常被忽略: 回溯(最近一次通话最后): 文件“client.py”,第 56 行,在 send_to_server 文件“/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/asyncio/streams.py”,第 306 行,关闭 文件“/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/asyncio/selector_events.py”,第 622 行,关闭 文件“/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/asyncio/base_events.py”,第 573 行,在 call_soon _check_closed 中的文件“/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/asyncio/base_events.py”,第 357 行 RuntimeError: 事件循环已关闭
【问题讨论】:
【参考方案1】:用户writer.close()
在关闭事件循环之前。无需在您的 send_to_server
方法中关闭它。
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()
【讨论】:
以上是关于使用 asyncio 的 Python 网络的主要内容,如果未能解决你的问题,请参考以下文章
深究Python中的asyncio库-asyncio简介与关键字
使用 asyncio.gather() 的协程/期货的经过时间
简单的 Python 多线程网络服务器,带有 Asyncio 和在主函数中调用的事件