为什么 loop.sock_accept(sock) 会阻塞同一循环中的其他协程?
Why does loop.sock_accept(sock) block other co-oroutines in same loop?
在下面的例子中:
loop = asyncio.get_event_loop()
loop.create_task(client()) # Does no start until run_server ends
await server()
# await f()
...调用服务器导致客户端被阻塞。
特别是它卡在了对
的调用中
await loop.sock_accept(sock)
并且在 server() 退出之前,client() 不会启动。
为什么?
用不同的异步函数替换 await server()
:
#await server()
await f()
允许客户端 运行()
Python 3.7 .. 3.10
的行为相同
同样,我们可以翻转哪个作为任务添加,哪个立即等待。任务在以下两个方面都运行失败:
if 1:
loop.create_task(server()) # Does no start until server ends
await client()
else:
loop.create_task(client()) # Does no start until client ends
await server()
完整示例:
import asyncio
import socket
host, port = ('localhost', 15555)
# host, port = ('127.0.0.1', 15555)
ACCEPT_TIMEOUT = 2
async def server():
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print("Creating server")
sock.bind((host, port))
sock.listen(8)
sock.setblocking(False) # sock_accept asks for non-blockin
sock.settimeout(ACCEPT_TIMEOUT)
loop = asyncio.get_event_loop()
while True:
print("ACCEPTING CONNECTIONS")
try:
client, _ = await loop.sock_accept(sock)
except socket.timeout:
print("ACCEPT TIMEOUT")
return
print("123 5")
loop.create_task(handle_client(client))
print("123 6")
async def client():
print("Sending .. ")
while True:
try:
tcp_reader, tcp_writer = await asyncio.open_connection(host, port)
break
except ConnectionRefusedError:
print("Refused")
await asyncio.sleep(1)
print("Sending .. OK")
msg = b"Message"
while True:
tcp_writer.write(msg)
tcp_writer.drain()
r = tcp_reader.read()
assert r == msg
async def handle_client(client): # Never reached
print("Handle client...")
loop = asyncio.get_event_loop()
request = None
while request != 'quit':
request = (await loop.sock_recv(client, 255))
await loop.sock_sendall(client, request)
client.close()
async def f():
print("f..")
asyncio.sleep(1)
print("f..done")
async def main():
loop = asyncio.get_event_loop()
if 1:
loop.create_task(client()) # Does no start until client ends
await server()
# await f()
else:
loop.create_task(server()) # Does no start until server ends
await client()
if __name__=='__main__':
asyncio.run(main())
似乎 Linux sock.settimeout()
否决了 sock.setblocking(false)
。
使您的代码对我有用的一些更改:
- 删除
sock.settimeout(...)
并改为使用 asyncio 的超时功能
- 使用上下文管理器正确关闭套接字,以避免留下未关闭的套接字
- 添加
REUSEADDR
标志,避免套接字在运行示例重复 时卡在TIME_WAIT
状态
async def server():
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
print("Creating server")
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind((host, port))
sock.listen(8)
sock.setblocking(False) # sock_accept asks for non-blockin
loop = asyncio.get_event_loop()
while True:
print("ACCEPTING CONNECTIONS")
try:
client, _ = await asyncio.wait_for(loop.sock_accept(sock), timeout=ACCEPT_TIMEOUT)
except asyncio.TimeoutError:
print("ACCEPT TIMEOUT")
return
print("123 5")
loop.create_task(handle_client(client))
print("123 6")
在下面的例子中:
loop = asyncio.get_event_loop()
loop.create_task(client()) # Does no start until run_server ends
await server()
# await f()
...调用服务器导致客户端被阻塞。 特别是它卡在了对
的调用中 await loop.sock_accept(sock)
并且在 server() 退出之前,client() 不会启动。 为什么?
用不同的异步函数替换 await server()
:
#await server()
await f()
允许客户端 运行()
Python 3.7 .. 3.10
的行为相同同样,我们可以翻转哪个作为任务添加,哪个立即等待。任务在以下两个方面都运行失败:
if 1:
loop.create_task(server()) # Does no start until server ends
await client()
else:
loop.create_task(client()) # Does no start until client ends
await server()
完整示例:
import asyncio
import socket
host, port = ('localhost', 15555)
# host, port = ('127.0.0.1', 15555)
ACCEPT_TIMEOUT = 2
async def server():
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print("Creating server")
sock.bind((host, port))
sock.listen(8)
sock.setblocking(False) # sock_accept asks for non-blockin
sock.settimeout(ACCEPT_TIMEOUT)
loop = asyncio.get_event_loop()
while True:
print("ACCEPTING CONNECTIONS")
try:
client, _ = await loop.sock_accept(sock)
except socket.timeout:
print("ACCEPT TIMEOUT")
return
print("123 5")
loop.create_task(handle_client(client))
print("123 6")
async def client():
print("Sending .. ")
while True:
try:
tcp_reader, tcp_writer = await asyncio.open_connection(host, port)
break
except ConnectionRefusedError:
print("Refused")
await asyncio.sleep(1)
print("Sending .. OK")
msg = b"Message"
while True:
tcp_writer.write(msg)
tcp_writer.drain()
r = tcp_reader.read()
assert r == msg
async def handle_client(client): # Never reached
print("Handle client...")
loop = asyncio.get_event_loop()
request = None
while request != 'quit':
request = (await loop.sock_recv(client, 255))
await loop.sock_sendall(client, request)
client.close()
async def f():
print("f..")
asyncio.sleep(1)
print("f..done")
async def main():
loop = asyncio.get_event_loop()
if 1:
loop.create_task(client()) # Does no start until client ends
await server()
# await f()
else:
loop.create_task(server()) # Does no start until server ends
await client()
if __name__=='__main__':
asyncio.run(main())
似乎 Linux sock.settimeout()
否决了 sock.setblocking(false)
。
使您的代码对我有用的一些更改:
- 删除
sock.settimeout(...)
并改为使用 asyncio 的超时功能 - 使用上下文管理器正确关闭套接字,以避免留下未关闭的套接字
- 添加
REUSEADDR
标志,避免套接字在运行示例重复 时卡在
TIME_WAIT
状态
async def server():
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
print("Creating server")
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind((host, port))
sock.listen(8)
sock.setblocking(False) # sock_accept asks for non-blockin
loop = asyncio.get_event_loop()
while True:
print("ACCEPTING CONNECTIONS")
try:
client, _ = await asyncio.wait_for(loop.sock_accept(sock), timeout=ACCEPT_TIMEOUT)
except asyncio.TimeoutError:
print("ACCEPT TIMEOUT")
return
print("123 5")
loop.create_task(handle_client(client))
print("123 6")