使用 Python 服务器的 Websocket 握手问题
Posted
技术标签:
【中文标题】使用 Python 服务器的 Websocket 握手问题【英文标题】:Websocket handshake problem using Python server 【发布时间】:2010-12-07 01:37:09 【问题描述】:这是Websocket Protocol 76中关于握手的问题。
我已经编写了客户端和服务器,但无法获取客户端 接受握手。我可以看到它正在被返回,但是客户端立即关闭了连接。我猜我的 md5sum 响应一定是不正确的。
据我所知,我正在遵循正确的程序,谁能告诉我我做错了什么?
def create_handshake_resp(handshake):
# parse request
final_line = ""
lines = handshake.splitlines()
for line in lines:
parts = line.partition(":")
if parts[0] == "Sec-WebSocket-Key1":
key1 = parts[2]
elif parts[0] == "Sec-WebSocket-Key2":
key2 = parts[2]
final_line = line
#concat the keys and encrypt
e = hashlib.md5()
e.update(parse_key(key1))
e.update(parse_key(key2))
e.update(final_line)
return "HTTP/1.1 101 WebSocket Protocol Handshake\r\nUpgrade: WebSocket\r\nConnection: Upgrade\r\nWebSocket-Origin: http://%s\r\nWebSocket-Location: ws://%s/\r\nWebSocket-Protocol: sample\r\n\r\n%s" % (httphost, sockethost, e.digest())
def parse_key(key):
spaces = -1
digits = ""
for c in key:
if c == " ":
spaces += 1
if is_number(c):
digits = digits + c
new_key = int(digits) / spaces
return str(new_key)
如您所见,我正在对键执行我认为正确的操作(将数字除以空间计数、连接结果和请求的最后一行,然后是 MD5),并且 16 字节的响应肯定是返回。
任何帮助将不胜感激,一旦我有工作副本,我会在这里发布。
谢谢。
编辑:
更改了标题以符合 kanaka 的响应。客户仍然不接受握手。 我发现了如何在 Chromium 中显示请求,这是给出的请求和响应:
(P) t=1291739663323 [st=3101] WEB_SOCKET_SEND_REQUEST_HEADERS
--> GET / HTTP/1.1
Upgrade: WebSocket
Connection: Upgrade
Host: ---
Origin: http://---
Sec-WebSocket-Key1: 3E 203C 220 642;
Sec-WebSocket-Key2: Lg 590 ~5 703O G7 =%t 9
\x74\x66\xef\xab\x50\x60\x35\xc6\x0a
(P) t=1291739663324 [st=3102] SOCKET_STREAM_SENT
(P) t=1291739663348 [st=3126] SOCKET_STREAM_RECEIVED
(P) t=1291739663348 [st=3126] WEB_SOCKET_READ_RESPONSE_HEADERS
--> HTTP/1.1 101 WebSocket Protocol Handshake
Upgrade: WebSocket
Connection: Upgrade
Sec-WebSocket-Origin: http://---
Sec-WebSocket-Location: ws://---/
Sec-WebSocket-Protocol: sample
\xe7\x6f\xb9\xcf\xae\x70\x57\x43\xc6\x20\x85\xe7\x39\x2e\x83\xec\x0
逐字逐句,除非我出于明显的原因删除了 IP 地址。
【问题讨论】:
您的缩进没有意义。我认为#concat
..return
的行应该在create_handshake_resp
?
对不起。复制粘贴错误。已编辑。
您需要spaces = -1
,因为您没有忽略标题中:
之后的第一个空格(例如,就响应而言,Sec-WebSocket-Key1: a b
仅包含一个空格)。做line.partition(": ")
可以防止这种情况发生
【参考方案1】:
你有几个问题马上就跳出来了:
您没有正确计算空格。您的计数器应该从 0 而不是 -1 开始。 您的响应标头仍然是 v75 样式。在 v76 中,任何以“WebSocket-”(WebSocket-Origin、WebSocket-Location、WebSocket-Protocol)开头的标头都应以“Sec-WebSocket-”开头。这是我在 wsproxy 中计算响应校验和的方法(noVNC 一个 html5 VNC 客户端的一部分):
import struct, md5
...
spaces1 = key1.count(" ")
spaces2 = key2.count(" ")
num1 = int("".join([c for c in key1 if c.isdigit()])) / spaces1
num2 = int("".join([c for c in key2 if c.isdigit()])) / spaces2
return md5(struct.pack('>II8s', num1, num2, key3)).digest()
【讨论】:
从 -1 开始的空格计数是因为当字符串被拆分时,在 : 和键之间有一个空格,不应该计算在内。我将尝试将标题更改为 76,我没有注意到它们已更改。感谢您的帮助,我会让您知道结果。 仍然没有运气,但我已经更新了问题以显示请求和响应。 哇!我将我的代码更改为您的响应代码并且它可以工作:) 虽然我必须在空格上包含 -1。不确定我的问题是什么但无论如何谢谢!【参考方案2】:这是一个 WebSocket 客户端/服务器的工作示例(javascript 中的客户端,Python 2.6 中的服务器)
它使用了来自不同地方的示例(包括 kanaka 的 answer/noVNC,以及 this page 和 this page)
适用于 ios 4.3 和 iPad 上的 Chrome 10.0.648.127、Safari 5.0.3 和 MobileSafari
这绝不是写得很好的代码(示例 HTML 页面特别糟糕) - 使用风险自负等等..
#!/usr/bin/env python
import socket
import threading
import struct
import hashlib
PORT = 9876
def create_handshake_resp(handshake):
final_line = ""
lines = handshake.splitlines()
for line in lines:
parts = line.partition(": ")
if parts[0] == "Sec-WebSocket-Key1":
key1 = parts[2]
elif parts[0] == "Sec-WebSocket-Key2":
key2 = parts[2]
elif parts[0] == "Host":
host = parts[2]
elif parts[0] == "Origin":
origin = parts[2]
final_line = line
spaces1 = key1.count(" ")
spaces2 = key2.count(" ")
num1 = int("".join([c for c in key1 if c.isdigit()])) / spaces1
num2 = int("".join([c for c in key2 if c.isdigit()])) / spaces2
token = hashlib.md5(struct.pack('>II8s', num1, num2, final_line)).digest()
return (
"HTTP/1.1 101 WebSocket Protocol Handshake\r\n"
"Upgrade: WebSocket\r\n"
"Connection: Upgrade\r\n"
"Sec-WebSocket-Origin: %s\r\n"
"Sec-WebSocket-Location: ws://%s/\r\n"
"\r\n"
"%s") % (
origin, host, token)
def handle(s, addr):
data = s.recv(1024)
s.send(create_handshake_resp(data))
lock = threading.Lock()
while 1:
print "Waiting for data from", s, addr
data = s.recv(1024)
print "Done"
if not data:
print "No data"
break
print 'Data from', addr, ':', data
# Broadcast received data to all clients
lock.acquire()
[conn.send(data) for conn in clients]
lock.release()
print 'Client closed:', addr
lock.acquire()
clients.remove(s)
lock.release()
s.close()
def start_server():
s = socket.socket()
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(('', PORT))
s.listen(1)
while 1:
conn, addr = s.accept()
print 'Connected by', addr
clients.append(conn)
threading.Thread(target = handle, args = (conn, addr)).start()
clients = []
start_server()
此外,还有一个糟糕的示例 HTML 页面来展示它的工作原理:
<!DOCTYPE html>
<html lang="en">
<head>
<title>Test</title>
<script type="application/javascript">
var ws;
function init()
var servermsg = document.getElementById("servermsg");
ws = new WebSocket("ws://localhost:9876/");
ws.onopen = function()
servermsg.innerHTML = servermsg.innerHTML + "<br>Server connected";
servermsg.innerHTML = servermsg.innerHTML + "<br>Sending message to server";
ws.send("Hello Mr. Server!");
;
ws.onmessage = function(e)
servermsg.innerHTML = servermsg.innerHTML + "<br>Recieved data: " + e.data;
;
ws.onclose = function()
console.log("Server disconnected");
servermsg.innerHTML = servermsg.innerHTML + "<br>Connected";
;
function postmsg()
var text = document.getElementById("message").value;
ws.send(text);
servermsg.innerHTML = servermsg.innerHTML + "<br>Sent: " + text;
return false;
</script>
</head>
<body onload="init();">
<form action="" onSubmit="postmsg()">
<input type="text" name="message" value="" id="message">
<input type="submit" name="submit" value="" id="submit">
</form>
<div id="servermsg"><h1>Message log:</h1></div>
</body>
</html>
【讨论】:
以上是关于使用 Python 服务器的 Websocket 握手问题的主要内容,如果未能解决你的问题,请参考以下文章