如何在Python WebSocket服务器握手响应中设置“Sec-WebSocket-Protocol”标头?

6

我有一个 Python WebSocket 服务器和一个 Node.js 客户端,但我无法实现 WebSocket 的协议握手

Python 服务器代码

下面是一个最小的 WebSocket 服务器,它使用 Flask-Sockets(使用了 gevent-websocket)。文件名为 ws_server.py

#!/usr/bin/python3
# -*- coding: utf-8 -*-
from flask import Flask, request, Response
from flask_sockets import Sockets

app = Flask(__name__)
sockets = Sockets(app)

@sockets.route('/')
def echo_socket(ws):
    # print("type request: ",type(request))
    # print("dir  request: ", dir(request))
    print("request.headers: ", request.headers)
    # print("type ws: ",type(ws))
    # print("dir  ws: ",dir(ws))

    if hasattr(request, "Sec-Websocket-Protocol"):
        print(request.headers["Sec-Websocket-Protocol"])
    else:
        print("INFO: No protocol specified")

    if request.headers["Sec-Websocket-Protocol"] == "aProtocol":
        print("INFO: protocol is OK")
    else:
        print("INFO: protocol not accepted: closing connection")
        ws.close()

    while not ws.closed:
        message = ws.receive()
        if message:
            print("received: "+message)
            ws.send(message)

    if ws.closed:
        print("INFO: connection has been closed")

if __name__ == "__main__":
    from gevent import pywsgi
    from geventwebsocket.handler import WebSocketHandler
    server = pywsgi.WSGIServer(('', 5001), app, handler_class=WebSocketHandler)
    server.serve_forever()

Node.js客户端的代码

以下是最简websocket客户端的代码,使用了websocket库。 文件名为app.js:

'use strict'

var WebSocketClient = require('websocket').client;
var client = new WebSocketClient();

function connectToServer(uri,protocol){
    client.on('connectFailed', function (error) {
        console.log('Connect Error: ' + error.toString());
    });

    client.on('connect', function (connection) {
        console.log('WebSocket Client Connected');

        connection.on('error', function (error) {
            console.log("Connection Error: " + error.toString());
        });

        connection.on('close', function () {
            console.log('echo-protocol Connection Closed');
        });

        connection.on('ping', () => {
            connection.pong();
        });

        connection.on('message', function (message) {
            if (message.type === 'utf8') {
                console.log("Received message is: '" + message.utf8Data + "'");
            }
        });
        console.log("sending SOMETHING");
        connection.sendUTF("SOMETHING");
    });
    client.connect(uri, protocol);
}

const wsHostAndPort = process.env.WSHSTPRT || "ws://echo.websocket.org:80";
const wsProtocol = process.env.WSPRTCL || []; // [] for no protocol

console.log("connecting to: ",wsHostAndPort,"with protocol",wsProtocol);
connectToServer(wsHostAndPort,wsProtocol);

作为客户端连接Node.js

启动Python WebSocket服务器:

$ python3 ws_server.py

从Node.js的ws客户端连接:

$ WSHSTPRT=ws://localhost:5001/ WSPRTCL="aProtocol" node app.js

客户端终端的输出是:
connecting to:  ws://localhost:5001/ with protocol aProtocol
Connect Error: Error: Expected a Sec-WebSocket-Protocol header.

服务器终端的输出是:

request.headers:  Upgrade: websocket
Connection: Upgrade
Sec-Websocket-Version: 13
Sec-Websocket-Key: +QjH5xejDI+OQZQ0OZcWEQ==
Host: localhost:5001
Sec-Websocket-Protocol: aProtocol


INFO: No protocol specified
INFO: protocol is OK
INFO: connection has been closed

我认为Python服务器需要设置一个名为"Sec-WebSocket-Protocol"的头,头的值应该与从客户端接收到的相同。但是我不知道如何做到这一点。我已经在互联网上搜索了一些资料(主要是 flask-socketsgevent-websockets论坛和问题跟踪器),但目前还没有任何进展。

我尝试了另一个简单的客户端websocat,我像这样调用它:$ websocat ws://localhost:5001 --protocol aProtocol我交互式地提示了一些消息,这些消息由Python服务器正确地回显。我认为这是因为websocat(与nodejs' websocket不同)在与服务器进行握手时不需要"Sec-WebSocket-Protocol header"。

但我需要使用期望此标头的nodejs客户端。因此我的问题是:如何在Python服务器握手响应中加入"Sec-WebSocket-Protocol"头?


这个有什么进展吗? - njho
1
很不幸,没有运气。此外,我正在将我的服务器端软件迁移到nodejs。 - easy_mungo
1个回答

2
我知道这已经过去一年了,看起来你已经继续前进了,不过看起来你几乎到达了目的地,只需要用服务器期望的子协议替换aProtocol即可。
或者,根据flask-websockets使用的底层gevent-websocket,可以完全省略。
// Subprotocol (2nd parameter) often is not required at all
var ws = new WebSocket('ws://localhost:5001/api');

对于未来的我或者其他人,如果偶然来到这里,我希望以下内容能够有所帮助。
在 JavaScript 中将浏览器连接至 Node.JS 的 apollo-server subscriptions,我需要以下内容:
// Pasted into Firefox console
const token = '...';  // A bearer token, if auth is needed
const subprotocol = 'graphql-ws';
w = new WebSocket('ws://localhost:5000/graphql', subprotocol);
w.send(JSON.stringify({"type":"connection_init","payload":{"authToken":token}}));

然后我会看到应用了Sec-Websocket-Protocol头部: Sec-Websocket-Protocol header Message sent and ack received 否则,如果没有应用子协议,则不会发送Sec-Websocket-Protocol头部,并且控制消息可见,我会看到连接关闭:1002,自然无法向WebSocket发送消息:

Connection Closed: 1002


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接