python-3.x - Asyncios await reader.read() 一直在等待
问题描述
我有一个这样写的小型服务器:
async def handle_client(reader, writer):
request = (await reader.read()).decode('utf8') # should read until end of msg
print(request)
response = "thx"
writer.write(response.encode('utf8'))
await writer.drain()
writer.close()
loop = asyncio.get_event_loop()
loop.create_task(asyncio.start_server(handle_client, socket.gethostname(), 8886))
loop.run_forever()
和一个这样写的小客户:
async def tcp_echo_client(message):
reader, writer = await asyncio.open_connection(
my_ip, 8886)
print(f'Send: {message!r}')
writer.write(message.encode())
await writer.drain()
data = await reader.read() # should read until end of msg
print(f'Received: {data.decode()!r}')
print('Close the connection')
writer.close()
await writer.wait_closed()
asyncio.run(tcp_echo_client(f"Hello World!"))
客户端和服务器开始通信,但从未完成。为什么读者无法识别味精的结尾?
如果我写
request = (await reader.read(1024)).decode('utf8')
相反,它可以工作,但我需要接收未定义的大量数据。
我试图像这样修改服务器的代码:
while True:
request = (await reader.read(1024)).decode('utf8')
if not request:
break
它接收所有数据块,但在最后一个块之后仍然永远等待。为什么?我如何告诉服务器的读者停止监听并继续在代码中发送答案?
解决方案
TCP 连接是基于流的,这意味着当您将“消息”写入套接字时,字节将被发送到对等方,而不包括消息之间的分隔符。连接另一端的对等方可以检索字节,但它需要自己弄清楚如何将它们切成“消息”。这就是读取最后一个块似乎挂起的原因:read()
只是等待对等方发送更多数据。
为了能够检索单个消息,发送者必须对每条消息进行框架或定界。例如,发送方可以在发送消息后关闭连接,这将允许另一方读取消息,因为它后面会跟着文件结束指示符。但是,这将允许发件人仅发送一条消息而无法读取响应,因为套接字将被关闭。
更好的选择是编写者只关闭套接字的写入端,这种部分关闭有时被称为shutdown)。在 asyncio 中,这是通过调用write_eof
:
writer.write(message.encode())
await writer.drain()
writer.write_eof()
像这样发送,消息后面会跟着文件结尾,服务器端的读取不会挂起。虽然客户端将能够读取响应,但仍将仅限于发送一条消息,因为在写入端已关闭的套接字上将无法进行进一步的写入。
要实现由任意数量的请求和响应组成的通信,您需要对每条消息进行框架化。一种简单的方法是在每条消息前加上消息长度:
writer.write(struct.pack('<L', len(request)))
writer.write(request)
接收方首先读取消息大小,然后读取消息本身:
size, = struct.unpack('<L', await reader.readexactly(4))
request = await reader.readexactly(size)
推荐阅读
- java - 在 MYSQL AND 条件中使用 Java 谓词
- amazon-web-services - AWS SDK 如何在不指定凭证的情况下知道凭证?
- java - 为什么我的队列大小总是为零?调用插入时它应该增加
- swift - 更新 ObservableObject 后使视图不重绘
- ios - BezierPath 未在按钮点击时显示 - CGContextSetStrokeColorWithColor:无效上下文 0x0
- sql - SQL Pivot 表的一半
- javascript - 如何使用存储圆形图形元素的容器 [pixijs]
- spring-batch - 当有多个模式时,spring batch PatternMatchingCompositeTokenizer
- haskell - 使用 Haskell 中的函数更新记录中的字段
- java - Spring 数据查询图
非唯一结果