2013-03-18 9 views
5

Ich denke über die Verwendung von SSE, um neue Daten an den Client zu pushen und mit Flot (Javascript Charting-Bibliothek) "Live" Updates anzuzeigen. Mein Server läuft auf Python Flask Rahmen und ich habe herausgefunden, wie die Daten an den Client zu schieben, aber das Problem tritt auf, sobald ich die Seite verlassen:Flask-Server gesendet Ereignisse Socket-Ausnahme

Exception happened during processing of request from ('127.0.0.1', 38814) 
Traceback (most recent call last): 
    File "/usr/lib/python2.7/SocketServer.py", line 582, in process_request_thread 
    self.finish_request(request, client_address) 
    File "/usr/lib/python2.7/SocketServer.py", line 323, in finish_request 
    self.RequestHandlerClass(request, client_address, self) 
    File "/usr/lib/python2.7/SocketServer.py", line 640, in __init__ 
    self.finish() 
    File "/usr/lib/python2.7/SocketServer.py", line 693, in finish 
    self.wfile.flush() 
    File "/usr/lib/python2.7/socket.py", line 303, in flush 
    self._sock.sendall(view[write_offset:write_offset+buffer_size]) 
error: [Errno 32] Broken pipe 

Ich verstehe, warum der Fehler auftritt - die Steckdose nie geschlossen, da die Endlosschleife "Live" -Daten liefert. Frage: Wie erkenne ich den Seitenwechsel und schließe den Socket sauber? Kann ich die Verbindung auf der Client-Seite schließen? Wie erkenne ich dann die Seitenänderung?

Dies ist der Server-Code Skelett, ich natürlich die Textnachricht mit json ersetzen würde die Liste der Objekte zu Bildschirm enthalten:

def event_stream(): 
    import time 
    while True: 
     time.sleep(1) 
     yield "data: This is a message number X.\n\n" 

@app.route('/stream') 
def stream(): 
    return Response(event_stream(), mimetype="text/event-stream") 

Antwort

1

Sie verwenden könnte entweder onBeforeUnload oder jQuery window.unload() einen Ajax-Aufruf zu machen eine Abreißmethode, die den Griff schließt. Etwas wie:

$(window).unload(
    function() { 
     $.ajax(type: 'POST', 
       async: false, 
       url: 'foo.com/client_teardown') 
    } 
} 

Es gibt einige Unstimmigkeiten mit, wie die unload()/onBeforeUnload() gehandhabt werden, so können Sie etwas mehr Arbeit in so etwas wie Chrome zu tun haben.

+0

Dies war die Lösung, zu der ich schließlich gekommen bin, danke für Ihre Antwort. – NindzAI

+0

Das ist meiner Meinung nach eine schlechte Lösung. Es kann Speicherlecks verursachen, wenn der Browser es nicht ausführt (unerwartet abstürzt, Verbindung wird geschlossen usw.). –

+0

Aus diesem Grund habe ich bemerkt, dass Sie in etwas wie Chrome vielleicht noch etwas zu tun haben (was dem Entwickler vielleicht erlaubt, solche Arten von Ereignissen auszulösen ...) – bbenne10

2

Ich habe keine bessere Antwort, aber ich denke nicht, dass die oben genannten Ajax-Anfrage an Server ist gut.

Wenn SSE das Streaming in einem Response-Objekt verwendet, ist es besser, Socket-Ereignisse zu verarbeiten und andere zugewiesene Ressourcen freizugeben, wenn das unterbrochene oder unterbrochene Pipe-Ereignis in Response erkannt werden kann.

+0

Während Sie Recht haben, ist es nicht ideal, eine Ajax-Methode zu feuern. Wenn Sie etwas Streaming verwenden und dann eine defekte Pipe erkennen, ist das ziemlich viel Overhead, um etwas auf der Seite abzuschießen, besonders wenn Sie die Antwort nicht brauchen (was dazu führt, dass meine Methode trotzdem fehlschlägt - da zumindest Chrome die Antwort nicht erlaubt) – bbenne10

1

Ich fand eine schmutzige (einschließlich Mokey Patching), aber funktionierende Lösung.

Weil es ein exception in SocketServer.StreamRequestHandler.finish, wenn die Verbindung unterbrochen wird, können wir sie flicken die Ausnahme zu fangen und damit umgehen, wie wir wollen:

import socket 
import SocketServer 

def patched_finish(self): 
    try: 
     if not self.wfile.closed: 
      self.wfile.flush() 
      self.wfile.close() 
    except socket.error: 
     # Remove this code, if you don't need access to the Request object 
     if _request_ctx_stack.top is not None: 
      request = _request_ctx_stack.top.request 
      # More cleanup code... 
    self.rfile.close() 

SocketServer.StreamRequestHandler.finish = patched_finish 

Wenn Sie den Zugriff auf die entsprechenden Request Objekt benötigen, können Sie zusätzlich muß den Ereignisstrom mit flask.stream_with_context, in meinem Fall wickeln:

@app.route(url) 
def method(host): 
    return Response(stream_with_context(event_stream()), 
        mimetype='text/event-stream') 

Auch dies eine sehr schmutzige Lösung ist und propably wird nicht funktionieren, wenn Sie nicht dazu dienen, die eingebaute WSGI verwenden r.

Verwandte Themen