Tornado-Server löst Fehler aus Stream ist geschlossen

Ich versuche, eine sehr einfache Chat- / Echo-Webseite zu implementieren.

Wenn der Client 8000 besucht / benachrichtigt, wird eine einfache Site geladen. Auf der Clientseite wird eine Anforderung zum Einrichten eines Listeners initiiert. Auf der Back-End-Seite wird die Cloud-Anzahl aktualisiert und an alle vorhandenen Clients gesendet.

Immer wenn der Benutzer etwas in ein Textfeld eingibt, wird ein POST im Back-End durchgeführt und alle anderen Clients erhalten ein Update mit diesem Text.

Hier ist die Front-End-Vorlage

<html>
<head>
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
</head>
<body>
    <p>session: <span id="session">{{ session }}</span><br/>
    client count: <span id="clientCount">{{ clientCount }}</span><br/>
    <span id="welcome">...registering with notify...</span></p>

    <div style="width: 600px; height: 100px; border: 1px solid black; overflow:auto; padding: 4px;" id="chatText">

    </div>

    <div>
    <span>enter text below:</span><br />
    <input type="text" id="textInput"></textarea>
    <div>

<script>

$(document).ready(function() {
    document.session = $('#session').html();
    setTimeout(initializeListener, 100);


    $('#textInput').keypress(function(e) {
    //    e.preventDefault(); 
        if(e.keyCode == 13 && !e.shiftKey) {
            var text = $('#textInput').val();
            submitChatText(text);
            return false;
        }
    });
});

var logon = '1';

function initializeListener() {
    console.log('initializeListener() called');
    jQuery.getJSON('//localhost/notify/listen', {logon: logon, session: document.session},
        function(data, status, xhr) {
            console.log('initializeListener() returned');
            if ('clientCount' in data) {
                $('#clientCount').html(data['clientCount']);
            }
            if ('chatText' in data) {
                text = $('#chatText').html()
                $('#chatText').html(data['chatText'] + "<br />\n" + text);
            }
            if (logon == '1') {
                $('#welcome').html('registered listener with notify!');
                logon = '0';
            }
            setTimeout(initializeListener, 0);
        })
        .error(function(XMLHttpRequest, textStatus, errorThrown) {
            console.log('error: '+textStatus+' ('+errorThrown+')');
            setTimeout(initializeListener, 100);
        });
}

function submitChatText(text) {
    console.log('submitChatText called with text: '+text)
    jQuery.ajax({
        url: '//localhost/notify/send',
        type: 'POST',
        data: {
            session: document.session,
            text: ''+text
        },
        dataType: 'json',
        //beforeSend: function(xhr, settings) {
        //    $(event.target).attr('disabled', 'disabled');
        //},
        success: function(data, status, xhr) {
            console.log('sent text message')
            $("#textInput").val('');
        },
        error: function(XMLHttpRequest, textStatus, errorThrown) {
            console.log('error: '+textStatus+' ('+errorThrown+')');
        }
    });


}

</script>
</body>
</html>

Hier ist der Servercode:

import tornado.ioloop
import tornado.web
import tornado.options
from uuid import uuid4
import json

class Client(object):
    callbacks = {}
    chat_text = ''

    def register(self, callback, session, logon=False):
        self.callbacks[session] = callback
        if logon == '1':
            self.notifyCallbacks()

    def notifyCallbacks(self):
        result = {}
        result['clientCount'] = self.getClientCount()
        if self.chat_text:
            result['chatText'] = self.chat_text

        for session, callback in self.callbacks.iteritems():
            callback(result)

        self.callbacks = {}

    def sendText(self, session, text):
        self.chat_text = text
        self.notifyCallbacks()
        self.chat_text = ''

    def getClientCount(self):
        return len(self.callbacks)

class Application(tornado.web.Application):
    def __init__(self):
        self.client = Client()
        handlers = [
            (r"/notify", MainHandler),
            (r"/notify/listen", ListenHandler),
            (r"/notify/send", SendHandler)
        ]
        settings = dict(
            cookie_secret="43oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o/Vo=",
            template_path="templates/notify",
        )
        tornado.web.Application.__init__(self, handlers, **settings)

class ListenHandler(tornado.web.RequestHandler):
    @tornado.web.asynchronous
    def get(self):
        logon = self.get_argument('logon')
        session = self.get_argument('session')
        self.application.client.register(self.on_message, session, logon)

    def on_message(self, result):
        json_result = json.dumps(result)
        self.write(json_result)
        self.finish()

class SendHandler(tornado.web.RequestHandler):
    def post(self):
        text = self.get_argument('text')
        session = self.get_argument('session')
        self.application.client.sendText(session, text)

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        session = uuid4()
        client_count= self.application.client.getClientCount()
        self.render("testpage.html", session=session, clientCount=client_count)


if __name__ == '__main__':
    application = Application()
    application.listen(8000)
    tornado.ioloop.IOLoop.instance().start()

Dann gibt der Server manchmal Fehler aus, wenn ich eine Registerkarte schließe und versuche, von einer anderen zu senden. Der Fehler ist wie folgt:

    ERROR:root:Uncaught exception POST /notify/send (127.0.0.1)
HTTPRequest(protocol='http', host='localhost', method='POST', uri='/notify/send', version='HTTP/1.1', remote_ip='127.0.0.1', body='session=e5608630-e2c7-4e1a-baa7-0d74bc0ec9fc&text=swff', headers={'Origin': 'http://localhost', 'Content-Length': '54', 'Accept-Language': 'en-US,en;q=0.8', 'Accept-Encoding': 'gzip,deflate,sdch', 'X-Forwarded-For': '127.0.0.1', 'Accept': 'application/json, text/javascript, */*; q=0.01', 'User-Agent': 'Mozilla/5.0 (X11; Linux i686) AppleWebKit/536.5 (KHTML, like Gecko) Chrome/19.0.1084.56 Safari/536.5', 'Accept-Charset': 'ISO-8859-1,utf-8;q=0.7,*;q=0.3', 'Host': 'localhost', 'X-Requested-With': 'XMLHttpRequest', 'X-Real-Ip': '127.0.0.1', 'Referer': 'http://localhost/notify', 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'})
Traceback (most recent call last):
  File "/usr/lib/python2.7/site-packages/tornado/web.py", line 1021, in _execute
    getattr(self, self.request.method.lower())(*args, **kwargs)
  File "test.py", line 66, in post
    self.application.client.sendText(session, text)
  File "test.py", line 30, in sendText
    self.notifyCallbacks()
  File "test.py", line 24, in notifyCallbacks
    callback(result)
  File "test.py", line 60, in on_message
    self.finish()
  File "/usr/lib/python2.7/site-packages/tornado/web.py", line 701, in finish
    self.request.finish()
  File "/usr/lib/python2.7/site-packages/tornado/httpserver.py", line 433, in finish
    self.connection.finish()
  File "/usr/lib/python2.7/site-packages/tornado/httpserver.py", line 187, in finish
    self._finish_request()
  File "/usr/lib/python2.7/site-packages/tornado/httpserver.py", line 223, in _finish_request
    self.stream.read_until(b("\r\n\r\n"), self._header_callback)
  File "/usr/lib/python2.7/site-packages/tornado/iostream.py", line 153, in read_until
    self._try_inline_read()
  File "/usr/lib/python2.7/site-packages/tornado/iostream.py", line 381, in _try_inline_read
    self._check_closed()
  File "/usr/lib/python2.7/site-packages/tornado/iostream.py", line 564, in _check_closed
    raise IOError("Stream is closed")
IOError: Stream is closed

Dies liegt natürlich daran, dass der Tab geschlossen wurde und der Client nicht mehr zuhört. Dies sollte jedoch normal sein. Wie kann ich mit dieser Situation angemessener umgehen?

Das einzige, was ich über diesen Fehler finden konnte, war ein weiterer Stackoverflow-Beitrag. Der Vorschlag war, vor dem Aufrufen der finish () -Methode zu prüfen, ob die Verbindung hergestellt wurde:

if not self._finished:
    self.finish()

Als ich das versuchte, schien es jedoch nicht zu helfen, dass ich immer noch den gleichen Fehler bekam, ODER ich würde einen anderen Fehler bekommenAssertionError: Anfrage geschlossen worauf ich keine hilfe finden konnte.

Antworten auf die Frage(1)

Ihre Antwort auf die Frage