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.