6

我正在考虑使用 SSE 将新数据推送到客户端并使用 Flot(javascript 图表库)显示“实时”更新。我的服务器在 python Flask 框架上运行,我已经想出了如何将数据推送到客户端,但是当我离开页面时,问题就出现了:

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

我理解为什么会发生错误 - 由于无限循环提供“实时”数据,因此套接字永远不会关闭。问题是如何检测页面更改并干净地关闭套接字?我可以关闭客户端的连接吗?那么如何检测页面更改?

这是服务器代码框架,我当然会用包含要显示的对象列表的 json 替换文本消息:

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")
4

4 回答 4

2

您可以使用其中一个onBeforeUnload或 jQuerywindow.unload()对关闭句柄的某些拆卸方法进行 Ajax 调用。就像是:

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

unload()/的处理方式存在一些不一致之处onBeforeUnload(),因此您可能需要在 Chrome 之类的东西中做更多的工作。

于 2013-05-28T19:53:47.503 回答
2

我没有更好的答案,但我认为上面对服务器的 ajax 请求不好。

在烧瓶中,SSE 在 Response 对象中使用流式传输,如果有办法检测 Response 中的断开连接或管道断开事件,则最好处理套接字事件并释放其他分配的资源。

于 2013-08-19T23:26:54.720 回答
2

我发现了一个肮脏的(包括 mokey 补丁),但有效的解决方案。

因为在连接断开时存在异常SocketServer.StreamRequestHandler.finish,我们可以对其进行修补以捕获异常并根据需要进行处理:

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

如果您需要访问相应的Request对象,您还需要用 包装事件流flask.stream_with_context,在我的情况下:

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

同样,这是一个非常肮脏的解决方案,如果您不使用内置的 WSGI 服务器,它可能无法正常工作。

于 2014-04-29T14:16:38.917 回答
0

不要在 prod 环境中使用 Flask 内部开发 wsgi 服务器。考虑使用 uwsgi 可以优雅地处理这个套接字错误。

同时考虑使用python3,它也能很好地捕捉到这个sockrt。

于 2019-03-28T14:06:48.617 回答