5 Stimmen

Websocket Handshake-Problem bei Verwendung eines Python-Servers

Dies ist eine Frage zum Handshake im Websocket-Protokoll 76.

Ich habe einen Client und Server geschrieben, habe jedoch Schwierigkeiten, den Handshake vom Client akzeptieren zu lassen. Ich sehe, dass er zurückgegeben wird, aber der Client schließt die Verbindung sofort. Ich vermute, dass meine md5sum-Antwort falsch sein muss.

Soweit ich das beurteilen kann, folge ich dem richtigen Verfahren, kann mir jemand sagen, was ich falsch mache?

def create_handshake_resp(handshake):

  # Anfrage parsen
  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

  # Schlüssel verknüpfen und verschlüsseln
  e = hashlib.md5()
  e.update(parse_key(key1))
  e.update(parse_key(key2))
  e.update(final_line)
  return "HTTP/1.1 101 WebSocket-Protokoll-Handshake\r\nUpgrade: WebSocket\r\nVerbindung:     Upgrade\r\nWebSocket-Origin: http://%s\r\nWebSocket-Location: ws://%s/\r\nWebSocket-Protokoll: beispiel\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)

Wie Sie sehen können, führe ich die Operationen durch, die meiner Meinung nach auf den Schlüsseln richtig sind (Zahlen durch die Anzahl der Leerzeichen teilen, Ergebnisse und die letzte Zeile der Anfrage verknüpfen und dann MD5) und es wird definitiv eine 16-Byte-Antwort zurückgegeben.

Jede Hilfe wäre sehr geschätzt, und sobald ich eine funktionierende Kopie habe, werde ich sie hier posten.

Danke.

BEARBEITEN:

Die Header wurden geändert, um kanakas Antwort zu entsprechen. Der Handshake wird immer noch nicht vom Client akzeptiert. Ich habe herausgefunden, wie man die Anfragen in Chromium anzeigen kann, und dies sind die Anfrage und Antwort, die gegeben werden:

(P) t=1291739663323 [st=3101]     WEB_SOCKET_SEND_REQUEST_HEADERS  
                              --> GET / HTTP/1.1   
                                  Upgrade: WebSocket
                                  Verbindung: Upgrade
                                  Host: ---
                                  Ursprung: 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-Protokoll-Handshake
                                  Upgrade: WebSocket
                                  Verbindung: Upgrade
                                  Sec-WebSocket-Ursprung: http://---
                                  Sec-WebSocket-Location: ws://---/
                                  Sec-WebSocket-Protokoll: beispiel

                                  \xe7\x6f\xb9\xcf\xae\x70\x57\x43\xc6\x20\x85\xe7\x39\x2e\x83\xec\x0

Wortwörtlich, außer dass ich die IP-Adresse aus offensichtlichen Gründen entfernt habe.

5voto

kanaka Punkte 66799

Sie haben ein paar Probleme, die mir sofort ins Auge springen:

  • Sie zählen Leerzeichen nicht korrekt. Ihr Zähler sollte bei 0 beginnen, nicht bei -1.
  • Ihre Antwortheader sind immer noch im v75-Stil. Jeder Header, der mit "WebSocket-" beginnt (WebSocket-Origin, WebSocket-Location, WebSocket-Protocol) sollte stattdessen in v76 mit "Sec-WebSocket-" beginnen.

So berechne ich den Antwort-Prüfwert in wsproxy (Teil von noVNC, einem HTML5 VNC-Client):

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

2voto

dbr Punkte 158949

Hier ist ein funktionierendes Beispiel für einen WebSocket-Client/Server (Client in Javascript, Server in Python 2.6)

Es verwendet Beispiele aus verschiedenen Quellen (einschließlich kanaka's Antwort/noVNC, und diese Seite und diese Seite)

Funktioniert mit Chrome 10.0.648.127, Safari 5.0.3 und MobileSafari auf dem iPad ab iOS 4.3

Es ist keineswegs gut geschriebener Code (die Beispiel-HTML-Seite ist besonders schrecklich) - Benutzung auf eigene Gefahr etc..

#!/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 "Warte auf Daten von", s, addr
        data = s.recv(1024)
        print "Fertig"
        if not data:
            print "Keine Daten"
            break

        print 'Daten von', addr, ':', data

        # Erhaltene Daten an alle Clients senden
        lock.acquire()
        [conn.send(data) for conn in clients]
        lock.release()

    print 'Client geschlossen:', 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 'Verbunden mit', addr
        clients.append(conn)
        threading.Thread(target = handle, args = (conn, addr)).start()

clients = []
start_server()

Außerdem eine schlechte Beispiel-HTML-Seite, um zu zeigen, wie es funktioniert:

        Test

            var ws;

            function init() {
                var servermsg = document.getElementById("servermsg");

                ws = new WebSocket("ws://localhost:9876/");
                ws.onopen = function(){
                    servermsg.innerHTML = servermsg.innerHTML + "<br>Server verbunden";
                    servermsg.innerHTML = servermsg.innerHTML + "<br>Nachricht an Server senden";
                    ws.send("Hallo Herr Server!");
                };
                ws.onmessage = function(e){
                    servermsg.innerHTML = servermsg.innerHTML + "<br>Empfangene Daten: " + e.data;
                };
                ws.onclose = function(){
                    console.log("Server getrennt");
                    servermsg.innerHTML = servermsg.innerHTML + "<br>Verbunden";
                };
            }
            function postmsg(){
                var text = document.getElementById("message").value;
                ws.send(text);
                servermsg.innerHTML = servermsg.innerHTML + "<br>Gesendet: " + text;
                return false;
            }

        Nachrichtenprotokoll:

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X