我正在使用 websockets 库在Python 3.4中创建一个websocket服务器。这是一个简单的回显服务器:

import asyncio
import websockets

@asyncio.coroutine
def connection_handler(websocket, path):
    while True:
        msg = yield from websocket.recv()
        if msg is None:  # connection lost
            break
        yield from websocket.send(msg)

start_server = websockets.serve(connection_handler, 'localhost', 8000)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()

假设我们(另外)希望在任何事件发生时向客户端发送消息。为简单起见,让我们每60秒定期发送一条消息。我们该怎么做?我的意思是,因为connection_handler一直在等待传入消息,所以服务器只有在收到客户端的消息后才能采取措施,对吗?我在这里想念什么?

也许这种情况需要一个基于事件/回调的框架,而不是基于协程的框架? Tornado

最佳答案

TL; DR 使用 asyncio.ensure_future() 可同时运行多个协程。



不,您不需要任何其他框架。异步应用程序与同步应用程序的整体思想是,它在等待结果时不会阻塞。使用协程或回调函数的实现方式无关紧要。



在同步应用程序中,您将编写类似于msg = websocket.recv()的代码,它将阻塞整个应用程序,直到您收到消息为止(如您所述)。但是在异步应用程序中则完全不同。

当您执行msg = yield from websocket.recv()时,您会说类似以下内容:暂停connection_handler()的执行,直到websocket.recv()将产生某些内容为止。在协程内部使用yield from将控制权返回到事件循环,因此可以在等待websocket.recv()的结果时执行其他一些代码。请引用documentation以更好地了解协程如何工作。



在执行 asyncio.async() 的阻塞调用之前,可以使用starting event loop运行任意数量的协程。

import asyncio

import websockets

# here we'll store all active connections to use for sending periodic messages
connections = []


@asyncio.coroutine
def connection_handler(connection, path):
    connections.append(connection)  # add connection to pool
    while True:
        msg = yield from connection.recv()
        if msg is None:  # connection lost
            connections.remove(connection)  # remove connection from pool, when client disconnects
            break
        else:
            print('< {}'.format(msg))
        yield from connection.send(msg)
        print('> {}'.format(msg))


@asyncio.coroutine
def send_periodically():
    while True:
        yield from asyncio.sleep(5)  # switch to other code and continue execution in 5 seconds
        for connection in connections:
            print('> Periodic event happened.')
            yield from connection.send('Periodic event happened.')  # send message to each connected client


start_server = websockets.serve(connection_handler, 'localhost', 8000)
asyncio.get_event_loop().run_until_complete(start_server)
asyncio.async(send_periodically())  # before blocking call we schedule our coroutine for sending periodic messages
asyncio.get_event_loop().run_forever()

这是示例客户端实现。它要求您输入名称,从回显服务器接收名称,等待来自服务器的另外两条消息(这是我们的定期消息)并关闭连接。
import asyncio

import websockets


@asyncio.coroutine
def hello():
    connection = yield from websockets.connect('ws://localhost:8000/')
    name = input("What's your name? ")
    yield from connection.send(name)
    print("> {}".format(name))
    for _ in range(3):
        msg = yield from connection.recv()
        print("< {}".format(msg))

    yield from connection.close()


asyncio.get_event_loop().run_until_complete(hello())

重要事项:
  • 在Python 3.4.4中,asyncio.async()重命名为 asyncio.ensure_future()
  • 有一些用于调度delayed calls的特殊方法,但是它们不适用于协程。
  • 关于python - Python-如何使用asyncio同时运行多个协程?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/32054066/

    10-14 03:59