WebSocket握手问题
我正在用Python实现一个简单的WebSocket服务器。
我用的握手过程来自于维基百科。
这个握手过程看起来是正常的,但当我点击发送的时候,出现了一个JavaScript错误:
未捕获的错误:INVALID_STATE_ERR:DOM异常11
这是我的HTML代码:
<!doctype html>
<html>
<head>
<title>ws_json</title>
</head>
<body onload="handleLoad();" onunload="handleUnload();">
<input type="text" id='input' />
<input type="button" value="submit" onclick="handleSubmit()" />
<div id="display"></div>
<script type="text/javascript">
function showmsg(str){
display = document.getElementById("display");
display.innerHTML += "<p>" + str + "</p>";
}
function send(str){
ws.send(str.length);
ws.send(str);
}
function handleSubmit(){
input = document.getElementById('input');
send(input.value);
input.focus();
input.value = '';
}
function handleLoad(){
ws = new WebSocket("ws://localhost:8888/");
ws.onopen = function(){
showmsg("websocket opened.");
}
ws.onclose = function(){
showmsg("websocket closed.");
}
}
function handleUnload(){
ws.close();
}
</script>
</body>
</html>
这是我的Python代码:
import socket
import threading
import json
PORT = 8888
LOCATION = "localhost:8888"
def handler(s):
print " in handler "
ip, _ = s.getpeername()
print "New connection from %s" % ip
request = s.recv(1024)
print "\n%s\n" % request
print s.getpeername()
# send response
response = "HTTP/1.1 101 Web Socket Protocol Handshake\r\n"
response += "Upgrade: WebSocket\r\n"
response += "Connection: Upgrade\r\n"
try:
peername = s.getpeername()
response += "Sec-WebSocket-Origin: http://%s\r\n" % peername[0] # % request[request.index("Origin: ")+8:-4]
except ValueError:
print "Bad Request"
raise socket.error
response += "Sec-WebSocket-Location: ws://%s\r\n" % LOCATION
response += "Sec-WebSocket-Protocol: sample"
response = response.strip() + "\r\n\r\n"
print response
s.send(response)
while True:
length = s.recv(1)
print length
if not length:
break
length = int(length)
print "Length: %i" % length
data = s.recv(length)
print "Received: %s" % data
print ""
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(('localhost', PORT))
s.listen(5)
print "server is running..."
while True:
sock, addr = s.accept()
threading.Thread(target=handler, args=(sock, )).start()
有没有人知道我哪里出错了?
2 个回答
从第8版开始,这个协议已经不再推荐使用了,请查看:
http://tools.ietf.org/id/draft-ietf-hybi-thewebsocketprotocol-12.txt
以获取这个协议的新版本。
我在Firefox 4上测试了你的代码,点击发送时遇到了同样的错误。不过在那之前,我收到了这个提示:
Firefox无法与服务器建立连接,地址是ws://localhost:8888/。
这可能是WebSocket对象被销毁的原因。我怀疑你的握手响应缺少了一些东西,所以Firefox关闭了连接。
根据维基百科关于WebSocket的介绍:
Sec-WebSocket-Key1和Sec-WebSocket-Key2字段,以及这两个字段后面的八个字节,是服务器用来构造一个16字节令牌的随机标记,这个令牌在握手结束时用来证明它已经读取了客户端的握手信息。
你的服务器响应中没有这个特殊的数字,所以我觉得我们需要弄清楚如何生成它,并把它加上。
编辑:如何生成这个数字
我们先从key1、key2和握手末尾的8个字节开始。
key1 = "18x 6]8vM;54 *(5: { U1]8 z [ 8"
key2 = "1_ tx7X d < nw 334J702) 7]o}` 0"
end8 = "Tm[K T2u"
我们为每个key生成一个数字,方法是忽略掉所有不是0-9的字符。在Python中:
def numFromKey(key):
return int(filter(lambda c: c in map(str,range(10)),key))
接下来,我们把这个数字除以原始key字符串中的空格数量,这里有一个函数可以用来计算字符串中的空格。
def spacesIn(key):
return len(filter(lambda c: c==' ',key))
从这两个key得到的数字是:
pkey1 = numFromKey(key1)/spacesIn(key1)
pkey2 = numFromKey(key2)/spacesIn(key2)
现在我们需要把pkey1、pkey2和end8的字节连接起来。处理后的key需要表示为32位的大端数字。
from struct import pack
catstring = pack('>L',pkey1) + pack('>L',pkey2) + end8
然后我们对这些字节进行md5哈希运算,以得到我们在握手末尾加上的神秘数字。
import md5
magic = md5.new(catstring).digest()
我觉得大概就是这样工作的。