websocket握手问题

Posted

技术标签:

【中文标题】websocket握手问题【英文标题】:websocket handshake problem 【发布时间】:2010-07-10 19:25:35 【问题描述】:

我正在使用 python 来实现一个简单的 websocket 服务器。 我使用的握手来自http://en.wikipedia.org/w/index.php?title=WebSockets&oldid=372387414。

握手本身似乎有效,但是当我点击发送时,我收到一个 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()

有谁知道我在这里做错了什么?

【问题讨论】:

如果没有 WebSocket 类,我无法测试您的代码。它在哪里定义? Firefox 3.6.3 似乎不知道它是什么。 Chrome Dev 有,Firefox 4 应该有。 如果你没有 100 个代表,你是如何提供赏金的? handleLoad() 和 send(str) 这两个函数如何引用同一个 ws 对象?它是在我没有看到的其他地方声明的全局变量吗? 在 Javascript 中,全局变量只是命名,局部变量是用 var 关键字创建的 【参考方案1】:

我在 Firefox 4 上测试了您的代码,并在点击发送时遇到了同样的错误,但在此之前我得到了

Firefox 无法建立连接 到 ws://localhost:8888/ 的服务器。

这可能是 WebSocket 对象被破坏的原因。我怀疑您的握手响应缺少某些内容,因此 Firefox 正在关闭套接字。

来自关于 Websockets 的***文章:

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"

我们通过忽略不是数字 0-9 的每个字符来为每个键创建一个数字。在 Python 中:

def numFromKey(key):
    return int(filter(lambda c: c in map(str,range(10)),key))

接下来我们将该数字除以原始键字符串中的空格数,因此这里有一个函数,用于计算字符串中的空格数。

def spacesIn(key):
    return len(filter(lambda c: c==' ',key))

由键产生的两个数字是:

pkey1 = numFromKey(key1)/spacesIn(key1)
pkey2 = numFromKey(key2)/spacesIn(key2)

现在我们需要连接 pkey1、pkey2 和 end8 的字节。处理后的密钥需要表示为 32 位 Big-Endian 数字。

from struct import pack
catstring = pack('>L',pkey1) + pack('>L',pkey2) + end8

然后我们将这些字节的 md5 哈希值得到我们在握手结束时附加的幻数

import md5
magic = md5.new(catstring).digest()

至少我认为它是这样工作的

【讨论】:

感谢您提供的信息,我自己永远也想不通。我在 google 上找到了这个:golang.org/src/pkg/websocket/server.go 它描述了如何生成密钥。在我们说话的时候努力理解它。 这里对握手有更好的描述:tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76#page-7 我找到了一个类似的解决方案,但是在 utf-8 中编码 md5 算法的结果时遇到问题。 pastebin.com/Q4GPcM09 这是我的握手代码,但正如我所说,utf-8 编码有问题。 这里有一个 ruby​​ 实现:github.com/igrigorik/em-websocket/blob/master/lib/em-websocket/… 至于将 MD5 哈希编码为 UTF-8;这不应该是必需的,因为 ASCII 是 UTF-8 的子集。所以一个 ASCII 字符串(MD5 哈希只包含 [0-9a-f])是自动有效的 UTF-8。【参考方案2】:

从版本 8 开始,该协议已被弃用,请参考:

http://tools.ietf.org/id/draft-ietf-hybi-thewebsocketprotocol-12.txt

对于新版本的协议。

【讨论】:

以上是关于websocket握手问题的主要内容,如果未能解决你的问题,请参考以下文章

WebSocket 与 Ruby 和 EM::WebSocket::Server 握手

websocket-rails,websocket握手错误

WebSocket 连接失败。 WebSocket握手期间出错-socketjs

Websocket 服务器握手响应

使用 C# 的 websocket 握手请求

Spring Boot - WebSocket 握手期间出错