python - 使用 asyncio 和 Python 3.6,如何构建处理/提交多个顺序请求的 TCP 服务器/客户端?
问题描述
pydocs中的示例服务器如下:
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()
我调整了示例客户端以发出两个连续的请求:
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()
观察到的行为是客户端获得了对第一个请求的响应,但套接字在处理第二个请求之前关闭。因此,客户端没有收到对第二个请求的响应。
服务器日志:
Received 'Foo' from ('127.0.0.1', 58112)
Send: 'Foo'
Close the client socket
客户端日志:
Send: 'Foo'
Received: 'Foo'
Send: 'Bar'
Received: ''
Close the socket
期望的行为是客户端接收Bar
响应第二个请求,在请求之间保持套接字打开。
如果我注释掉关闭服务器上的套接字,它会阻塞以使客户端永远不会读取对第一个请求的响应,即使缓冲区应该已被write.drain()
.
指导将不胜感激;提前致谢。
解决方案
我想我想通了。查看服务器代码:
asyncio.start_server
不会重复调用handle_echo
它的事件循环。我负责在该处理函数中编写一个循环,重用reader
每个writer
请求。- 而不是抛出异常,而是
yield from reader.read(100)
在客户端关闭它的套接字结束后返回 0 个字节。
因此,这是新的服务器代码,它按预期工作:
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()
推荐阅读
- python - ValueError:ndarray 不连续
- javascript - jQuery平滑滚动链接颜色更改不起作用
- bitcoin - Coinbase API v2 获得多天的历史价格
- vba - 从已关闭的工作簿中复制数据并粘贴(如果它们的数据相同)
- oauth-2.0 - Linkedin oauth2 api,授权不返回“状态”
- reactjs - this.props.history.push 不重新渲染反应组件
- c# - 使用异步任务和 Textbox.Text = "Hello" 时出现问题
- jmeter - BeanShell 脚本作为后处理器
- android - 在回收站视图中搜索
- windows - 如何打印到控制台窗口?