使用 WebSockets#

要使用 WebSockets,请声明一个 WebSocket 函数而不是路由函数,如下所示:

@app.websocket('/ws')
async def ws():
    while True:
        data = await websocket.receive()
        await websocket.send(data)

websocket 是一个类似于 request 的全局变量,并共享许多相同的属性,例如 headers

手动拒绝或接受 WebSockets#

WebSocket 连接是通过接受 HTTP 升级请求创建的,但是服务器可以选择拒绝 WebSocket 请求。为此,只需从 WebSocket 函数中返回,就像您对路由函数所做的那样。

@app.websocket('/ws')
async def ws():
    if (
        websocket.authorization.username != USERNAME or
        websocket.authorization.password != PASSWORD
    ):
        return 'Invalid password', 403  # or abort(403)
    else:
        websocket.accept()  # Automatically invoked by receive or send
        ...

独立发送和接收#

第一个给出的示例需要客户端发送消息才能让服务器响应。要独立发送和接收,需要独立的任务。

async def sending():
    while True:
        await websocket.send(...)

async def receiving():
    while True:
        data = await websocket.receive()
        ...

@app.websocket('/ws')
async def ws():
    producer = asyncio.create_task(sending())
    consumer = asyncio.create_task(receiving())
    await asyncio.gather(producer, consumer)

收集行至关重要,因为如果没有它,WebSocket 函数将返回并触发 Quart 发送 HTTP 响应。

检测断开连接#

当客户端断开连接时,将引发 CancelledError,可以捕获该错误以处理断开连接。

@app.websocket('/ws')
async def ws():
    try:
        while True:
            data = await websocket.receive()
            await websocket.send(data)
    except asyncio.CancelledError:
        # Handle disconnection here
        raise

警告

必须重新引发 CancelledError

关闭连接#

可以通过等待 close 方法并使用适当的 WebSocket 错误代码来关闭连接。

@app.websocket('/ws')
async def ws():
    await websocket.accept()
    await websocket.close(1000)

如果 WebSocket 在被接受之前关闭,服务器将使用 403 HTTP 响应进行响应。

测试 WebSockets#

要测试 WebSocket 路由,请使用 test_client,如下所示。

test_client = app.test_client()
async with test_client.websocket('/ws/') as test_websocket:
    await test_websocket.send(data)
    result = await test_websocket.receive()

如果 WebSocket 路由返回响应,test_client 将引发 WebsocketResponseError 异常,其中包含 response 属性。例如。

test_client = app.test_client()
try:
    async with test_client.websocket('/ws/') as test_websocket:
        await test_websocket.send(data)
except WebsocketResponseError as error:
    assert error.response.status_code == 401

发送和接收字节或字符串#

WebSocket 协议允许使用字节或字符串进行发送,并使用帧标记来指示是哪种类型。 receive() 方法将返回 bytesstr,具体取决于客户端发送的内容,例如,如果客户端发送字符串,则该字符串将从方法中返回。同样,您可以发送字节或字符串。

混合 WebSocket 和 HTTP 路由#

Quart 允许将路由定义为 WebSockets 和 HTTP 请求。这允许根据请求类型(WebSocket 升级或不升级)发送响应。因此,

@app.route("/ws")
async def http():
    return "A HTTP request"

@app.websocket("/ws")
async def ws():
    ...  # Use the WebSocket

如果 HTTP 定义不存在,Quart 将使用 400,错误请求,响应请求缺少的路由(而不是 404)。