2010-07-10 2 views
7

Я использую python для реализации простого сервера websocket. Рукопожатие, которое я использую, исходит от http://en.wikipedia.org/w/index.php?title=WebSockets&oldid=372387414.проблема с рукопожатием websocket

Самого рукопожатие, кажется, работает, но когда я ударил отправить, я получаю яваскрипт ошибки:

Uncaught Error: INVALID_STATE_ERR: DOM Exception 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> 

А вот код питона:

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() 

Кто-нибудь знает, что я делаю неправильно здесь?

+0

Я не могу проверить ваш код без класса WebSocket. где он определен? Firefox 3.6.3, похоже, не знал, что это такое. – Nathan

+0

Chrome Dev имеет его, и Firefox 4 должен иметь его. – lowerkey

+0

Как вы предложили щедрость, если у вас нет 100 репутации? – Nathan

ответ

6

Я проверил ваш код на Firefox 4 и получил ту же ошибку при ударе отправить, однако до того, что я получил

Firefox can't establish a connection to the server at ws://localhost:8888/.

, который, вероятно, почему объект WebSocket был разрушен. Я подозреваю, что у вашего ответа на рукопожатие чего-то не хватает, поэтому Firefox закрывает сокет.

Из статьи Википедии на WebSockets: ответ

The Sec-WebSocket-Key1 and Sec-WebSocket-Key2 fields and the eight bytes after the fields are random tokens which the server uses to construct a 16 byte token at the end of its handshake to prove that it has read the client's handshake.

ваш сервер не имеет такой специальный номер на дне, поэтому я думаю, что мы должны выяснить, как создать его и включить его.

EDIT: Как создать этот номер

Начнет с ключом1, 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() 

То, как я думаю, что это работает, по крайней мере

+0

Спасибо за информацию, никогда бы не подумал об этом. Я нашел это в google: http://golang.org/src/pkg/websocket/server.go, где описано, как сгенерировать ключ. Работая над пониманием этого, когда мы говорим. – lowerkey

+0

Вот лучшее описание рукопожатия: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76#page-7 – lowerkey

+1

Я пришел к аналогичному решению, но у меня проблемы с кодированием результатов алгоритм md5 в utf-8. – lowerkey

Смежные вопросы