Flask SSE流在Firefox断开后未终止

10 投票
1 回答
1717 浏览
提问于 2025-04-18 06:06

我正在尝试创建一个Flask服务器,使用sse(服务器发送事件)向客户端传输数据。下面这段测试代码似乎可以正常工作,但我遇到了一个与处理客户端断开连接相关的问题。

当我使用Firefox作为客户端(版本28或29)时,数据会按预期开始传输。然而,当我刷新页面时,会打开一个新的数据流(这是正常的),但旧的数据流仍然存在。负责处理这个数据流的eventgen()线程从未被终止。在其他客户端上(我尝试了IE和Chrome),刷新或关闭页面会导致客户端断开连接,这会在服务器端产生一个套接字错误10053(客户端与主机断开连接)。这会终止循环,只保留活动的数据流,这是预期的行为。

通过使用进程查看器,我注意到在客户端(Firefox)那边,TCP连接处于FIN_WAIT2状态,而在服务器那边,连接处于CLOSE_WAIT状态。奇怪的是,在我测试的三台运行Firefox的Win 7 x64机器中,有一台能够正确处理断开连接的情况。在Python 2.6.5和2.7.6上运行也得到了相同的结果。

我还尝试用基于greenlet的gevent WSGI服务器替换内置的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)

1 个回答

4

我好像找到了这个问题的根源。问题似乎出在AVG的surf-shield链接扫描器和Firefox浏览器之间。关闭surf-shield功能似乎就能解决这个问题。而之前能正常工作的电脑上装的是Avast,而不是AVG。我猜这可能是AVG的一个bug,应该要修复一下。

撰写回答