无法在python中获取WebSocket响应

bprjcwpo  于 2022-11-11  发布在  Python
关注(0)|答案(1)|浏览(197)

我正在尝试创建一个非常简单的WebSocket服务器,带有原生的socket包(这主要是针对微控制器的,但这并不相关)。
如果我理解正确的话,WebSocket只是HTTP协议的一个扩展,客户端发出升级请求,然后服务器在websocket握手后用正确的报头(包括散列密钥)做出响应。我在这里过于简化了,但这是我理解的websocket的基本要点,它应该在启动连接方面与HTTP类似。
所以我最初的伪代码是这样的:

  • 将套接字绑定到端口并侦听
  • 当客户端发出WebSocket请求时,检查connection标头是否有upgrade值,Upgrade标头是否有websocket
  • 如果上述条件成立,则继续,否则关闭套接字
  • Sec-WebSocket-Key标头中获取值(这是用于以后的响应标头Sec-WebSocket-Accept
  • 生成正确的响应标头,即
HTTP/1.1 100 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: <hashed_key_from_Sec-WebSocket-Key>

请注意,根据HTTP规范,额外的换行符是有意的。

  • 发回客户

下面是我的最小python代码

import socket
from base64 import b64encode
from hashlib import sha1

PORT = 8082

def response_key(key):
    GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" # magic string
    hash = sha1(key.encode() + GUID.encode())
    response_key = b64encode(hash.digest()).strip()
    return response_key.decode("ASCII")

def shake_hand(key):
    response = (
        "HTTP/1.1 101 Switching Protocols\r\n"
        "Upgrade: websocket\r\n"
        "Connection: Upgrade\r\n"
        f"Sec-WebSocket-Accept: {response_key(key)}\r\n"
        "\r\n"
    )
    return response

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.bind(("localhost", PORT))
    s.listen()
    while True:
        conn, addr = s.accept()
        request = conn.recv(1024).strip().decode()
        data = request.split("\r\n")
        headers = {}
        for h in data:
            header = h.split(": ")
            if len(header) > 1:
                key, value = map(lambda x: x.strip().lower(), header)
                headers[key] = value

        if (headers.get("connection") == "upgrade") and (headers.get("upgrade") == "websocket"):
            handshake_response = shake_hand(
                headers["sec-websocket-key"]
            )
            conn.sendall(handshake_response.encode())

我启动WebSocket连接的方法是简单地从浏览器控制台使用以下代码:

var ws = new WebSocket('ws://localhost:8082');
ws.onopen = e => console.log("Connection open!")
ws.onerror = e => console.error(e);
ws.onmessage = e => console.log("Msg", e);
ws.onclose = e => console.log("Connection closed!")

但是,这并不起作用,它会给我一个错误,如

WebSocket connection to 'ws://localhost:8082/' failed:

当我检查网络选项卡时,没有响应头。不确定我在这里做错了什么,我有点想不出在这方面取得任何进展。任何帮助都是感激之情。

pes8fvy9

pes8fvy91#

正如我在注解部分提到的--我的代码中有一个bug。主要是map(lambda x: x.strip().lower(), header),它将Sec-WebSocket-Key值转换为小写,导致键无效。下面是实际响应OK状态的工作代码。

import socket
from base64 import b64encode
from hashlib import sha1

WS_MAGIC_STRING = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
PORT = 8082

def response_key(key):
    GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
    sha1_hash = sha1(key.encode() + GUID.encode())
    response_key = b64encode(sha1_hash.digest()).strip()
    return response_key.decode()

def handshake(key):
    response = (
        "HTTP/1.1 101 Switching Protocols\r\n"
        "Upgrade: websocket\r\n"
        "Connection: Upgrade\r\n"
        f"Sec-WebSocket-Accept: {response_key(key)}\r\n\r\n"
    )
    return response

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    s.bind(("localhost", PORT))
    s.listen()
    while True:
        conn, addr = s.accept()
        request = conn.recv(1024).strip().decode()
        data = request.split("\r\n")
        headers = {}
        for h in data:
            header = h.split(": ")
            if len(header) > 1:
                key, value = map(lambda x: x.strip(), header)
                headers[key.lower()] = value

        if (headers.get("connection") == "Upgrade") and (
            headers.get("upgrade") == "websocket"
        ):
            handshake_response = handshake(
                headers["sec-websocket-key"],
            )
            conn.sendall(handshake_response.encode())

相关问题