10

我正在尝试创建一个使用 sse 将数据流式传输到客户端的 Flask 服务器。下面的一段测试代码似乎可以解决问题,但我偶然发现了一个与处理客户端断开连接相关的问题。

使用 Firefox 作为客户端(版本 28 或 29)时,数据按预期开始流式传输。但是,当我重新加载页面时,会打开一个新流(如预期的那样),但旧流仍然存在。处理流的 eventgen() 线程永远不会终止。在其他客户端上(我使用 Yaffle 的 Polyfill EventSource 实现以及 Chrome 尝试了 IE),重新加载或关闭页面会导致客户端断开连接,从而导致服务器端套接字错误 10053(客户端与主机断开连接)。这将终止循环并仅使活动流保持活动状态,这是预期的行为。

使用 Process Explorer,我注意到客户端 (Firefox) 端的 TCP 连接挂起状态为 FIN_WAIT2,而服务器端的连接挂起状态为 CLOSE_WAIT。奇怪的是,在运行 Firefox 的 3 台机器中的 1 台(全部为 Win 7 x64)上,我对此进行了测试,断开连接得到了正确处理。在 Python 2.6.5 和 2.7.6 上运行产生了相同的结果。

我还尝试用基于 greenlet 的 gevent WSGIserver 替换内置的 Flask 服务器,但这会导致完全相同的行为。此外,应该使用某种形式的线程/小事件,否则运行 eventgen() 循环会阻塞服务器。

下面的测试代码在浏览到 localhost:5000 时为 make_html() 中定义的页面提供服务,并打开到 /stream 的流。该流显示格式为 {"content": 0.5556278827744346, "local_id": 4, "msg": 6} 的消息,其中 local_id 是打开的流的 id,msg 是该流中当前消息的编号。

import time, random
import flask
from flask import Flask, json

def make_html():
    return """
        <script src="http://code.jquery.com/jquery-latest.min.js" type="text/javascript"></script>
        <script type=text/javascript>
            var source = new EventSource('/stream');
            source.onmessage = function (event) {
                var data = event.data;                
                var logdiv = $('#log');
                logdiv.empty();
                logdiv.append('<div class="event">' + data + '</div>');
            };
        </script>
        <h1>Log</h1>
        <div id=log>Log ...</div>
        <hr />
    """

# ---- Flask app ----

app = Flask(__name__)

@app.route('/')
def index():
    return make_html()

counter = 0
def eventgen():
    global counter
    counter += 1    
    local_id = counter
    msg_count = 0
    while True:
        msg_count += 1
        data = {'msg': msg_count, 'content': random.random(), 'local_id': local_id}
        data = json.dumps(data)
        yield 'data: ' + data + '\n\n'
        print local_id, ':', data
        time.sleep(0.5)

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

if __name__ == '__main__':    
    app.run(threaded=True)
4

1 回答 1

4

我似乎找到了这个问题的根源。问题似乎出在 AVG surf-shield 链接扫描仪和 firefox 上。禁用 surf-shield 似乎可以解决问题。它已经在其上运行的 PC 运行的是 Avast 而不是 AVG。我猜这是 AVG 中的一个错误,应该可以修复。

于 2014-05-11T20:58:35.830 回答