使用 asyncio 的 Python 简单套接字客户端/服务器 [英] Python simple socket client/server using asyncio
问题描述
我想使用 asyncio 协程而不是多线程重新实现我的代码.
I would like to re-implement my code using asyncio coroutines instead of multi-threading.
server.py
def handle_client(client):
request = None
while request != 'quit':
request = client.recv(255).decode('utf8')
response = cmd.run(request)
client.send(response.encode('utf8'))
client.close()
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(('localhost', 15555))
server.listen(8)
try:
while True:
client, _ = server.accept()
threading.Thread(target=handle_client, args=(client,)).start()
except KeyboardInterrupt:
server.close()
客户端.py
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.connect(('localhost', 15555))
request = None
try:
while request != 'quit':
request = input('>> ')
if request:
server.send(request.encode('utf8'))
response = server.recv(255).decode('utf8')
print(response)
except KeyboardInterrupt:
server.close()
我知道有一些合适的异步网络库可以做到这一点.但我只是想在这种情况下只使用 asyncio 核心库,以便更好地理解它.
I know there are some appropriate asynchronous network librairies to do that. But I just want to only use asyncio core library on this case in order to have a better understanding of it.
在处理客户端定义之前只添加 async 关键字会非常好......这里有一段似乎有效的代码,但我仍然对实现感到困惑.
It would have been so nice to only add async keyword before handle client definition... Here a piece of code which seems to work, but I'm still confused about the implementation.
asyncio_server.py
asyncio_server.py
def handle_client(client):
request = None
while request != 'quit':
request = client.recv(255).decode('utf8')
response = cmd.run(request)
client.send(response.encode('utf8'))
client.close()
def run_server(server):
client, _ = server.accept()
handle_client(client)
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(('localhost', 15555))
server.listen(8)
loop = asyncio.get_event_loop()
asyncio.async(run_server(server))
try:
loop.run_forever()
except KeyboardInterrupt:
server.close()
如何以最佳方式进行调整并使用 async await 关键字.
How adapt this in the best way and using async await keywords.
推荐答案
线程代码最接近的字面翻译会像以前一样创建套接字,使其非阻塞,并使用 asyncio
The closest literal translation of the threading code would create the socket as before, make it non-blocking, and use asyncio
low-level socket operations to implement the server. Here is an example, sticking to the more relevant server part (the client is single-threaded and likely fine as-is):
import asyncio, socket
async def handle_client(client):
loop = asyncio.get_event_loop()
request = None
while request != 'quit':
request = (await loop.sock_recv(client, 255)).decode('utf8')
response = str(eval(request)) + '
'
await loop.sock_sendall(client, response.encode('utf8'))
client.close()
async def run_server():
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(('localhost', 15555))
server.listen(8)
server.setblocking(False)
loop = asyncio.get_event_loop()
while True:
client, _ = await loop.sock_accept(server)
loop.create_task(handle_client(client))
asyncio.run(run_server())
以上有效,但不是使用 asyncio
的预期方式.它非常低级,因此容易出错,需要您记住在套接字上设置适当的标志.此外,没有缓冲,所以像从客户端读取一行这样简单的事情变成了一件令人厌烦的苦差事.此 API 级别实际上仅适用于替代事件循环的实现者,它们将提供他们对 sock_recv
、sock_sendall
等的实现.
The above works, but is not the intended way to use asyncio
. It is very low-level and therefore error-prone, requiring you to remember to set the appropriate flags on the socket. Also, there is no buffering, so something as simple as reading a line from the client becomes a tiresome chore. This API level is really only intended for implementors of alternative event loops, which would provide their implementation of sock_recv
, sock_sendall
, etc.
Asyncio 的公共 API 提供了两个用于消费的抽象层:旧的 transport/protocol层仿照 Twisted 和较新的 流层.在新代码中,您几乎肯定要使用流 API,即调用 asyncio.start_server
并避免原始套接字.这显着减少了行数:
Asyncio's public API provides two abstraction layers intended for consumption: the older transport/protocol layer modeled after Twisted, and the newer streams layer. In new code, you almost certainly want to use the streams API, i.e. call asyncio.start_server
and avoid raw sockets. That significantly reduces the line count:
import asyncio, socket
async def handle_client(reader, writer):
request = None
while request != 'quit':
request = (await reader.read(255)).decode('utf8')
response = str(eval(request)) + '
'
writer.write(response.encode('utf8'))
await writer.drain()
writer.close()
async def run_server():
server = await asyncio.start_server(handle_client, 'localhost', 15555)
async with server:
await server.serve_forever()
asyncio.run(run_server())
这篇关于使用 asyncio 的 Python 简单套接字客户端/服务器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!