我不明白为什么server.py版本1允许客户机被键盘中断并重新启动,而server.py版本2不允许:
server.py Version1:
import asyncio
async def handle_client(reader, writer):
while True:
request = (await reader.read(128)).decode()
writer.write('Received ok.'.encode())
await writer.drain()
async def main():
loop.create_task(asyncio.start_server(handle_client, 'localhost', 15555))
loop = asyncio.new_event_loop()
loop.create_task(main())
loop.run_forever()
server.py版本2:
import asyncio
async def handle_client(reader, writer):
while True:
request = (await reader.read(128)).decode()
if request == "hello":
writer.write('Received ok.'.encode())
await writer.drain()
async def main():
loop.create_task(asyncio.start_server(handle_client, 'localhost', 15555))
loop = asyncio.new_event_loop()
loop.create_task(main())
loop.run_forever()
client.py:
import asyncio
async def make_connections():
reader, writer = await asyncio.open_connection('localhost', 15555, loop=loop)
loop.create_task(connect(reader, writer))
async def connect(reader, writer):
writer.write("hello".encode())
await writer.drain()
result = await reader.read(128)
print(result.decode())
loop = asyncio.new_event_loop()
loop.create_task(make_connections())
loop.run_forever()
版本2适用于单个客户端,但如果我向客户端发送键盘中断,则在重新启动客户端后无法再连接。每当我更改客户机中的代码时,ssh中的ssh和杀死/重新启动服务器都是很烦人的。我不明白为什么第二个版本在第二次尝试连接时不接受客户机。
发布于 2018-06-06 06:10:52
我不明白为什么server.py版本1允许客户端被键盘中断并重新启动,而server.py版本2不允许
这两个版本都有一个错误,它们没有正确检查文件结束的情况。当您中断客户端时,套接字将关闭并从中读取返回EOF,而向其写入则会引发异常。在版本1中等待writer.drain()
传递异常并中断协同机制。(此异常可能显示在服务器的标准错误上。)
但是,Version 2有一个问题:在EOF上,if request == "hello"
测试是假的,因为reader.read()
一直返回一个空字节字符串来标记EOF条件。这防止了await writer.drain()
执行和传递异常,因此协同线仍然被困在无限循环中。一个简单的解决方法是在if not request: break
之后添加类似read
的内容。
为什么第2版会被卡住
但是,上面的内容并没有完全解释为什么在第2版中,整个服务器出现故障,而新的客户端无法连接。当然,人们会期望await
要么返回一个结果,要么将控制权交给其他协同器。但是,观察到的行为是,尽管在await
循环中包含了一个while
,但coroutine不允许其他协同器运行!
问题是,await
并不意味着“向事件循环传递控制”,因为人们经常理解它。它的意思是“来自提供的可访问对象的请求值,如果事件循环对象指示它没有现成的值,则生成对事件循环的控制”。if
之后的部分至关重要:如果对象确实有一个就绪值,则将立即使用该值,而不会将其推迟到事件循环。换句话说,await
并不保证事件循环将有机会运行.
EOF上的流总是有要返回的数据--标记EOF的空字符串。因此,它永远不会被挂起,循环最终会完全阻塞事件循环。为了保证其他任务有机会运行,您可以在循环中添加await asyncio.sleep(0)
--但是这在正确编写的代码中不应该是必需的,在该代码中,请求IO数据将很快导致等待,此时事件循环将启动。一旦EOF处理错误得到纠正,服务器将正确工作。
https://stackoverflow.com/questions/50698649
复制