我正在尝试创建一个非常简单的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:
当我检查网络选项卡时,没有响应头。不确定我在这里做错了什么,我有点想不出在这方面取得任何进展。任何帮助都是感激之情。
1条答案
按热度按时间pes8fvy91#
正如我在注解部分提到的--我的代码中有一个bug。主要是
map(lambda x: x.strip().lower(), header)
,它将Sec-WebSocket-Key
值转换为小写,导致键无效。下面是实际响应OK
状态的工作代码。