2

我正在构建一个需要读取(并继续读取)它正在运行的机器的串行端口的网络服务器。
目的是能够读取条形码扫描仪,并使用服务器发送的事件来使用读取的条形码更新浏览器。

我正在使用烧瓶来做到这一点。我浏览了一下,有些实现只需要烧瓶,有些人说我需要一个像 Gevent 这样的异步库,还有一些人甚至说我需要 Gevent 和某种队列,比如 Redis 或 RabbitMQ。

我试图将我的代码基于我在 stackoverflow 上找到的一个非常简单的示例。我大部分时间都在工作,但我遇到了一些问题;

  • 在 Chrome 中存在跨域错误,通过添加 Access-Control-Allow-Origin 标头,我可以让它在 FireFox 中工作,但 Chrome 仍然无法工作。只有FF支持SSE跨域对吗?我需要它来支持 CORS,因为浏览器需要从单独的机器加载条形码数据。
  • 每条消息后,浏览器都会在控制台中显示条形码,但随后会关闭连接,大约 3 秒后才再次打开。这似乎起源于 Flask,它给了我数据然后就停止了。
  • 另外,我想知道这将如何在负载下执行。我的意思是,flask 为文本/事件流 mimetype 保持连接打开。如果有多个客户端连接,过一会会不会阻塞flask,因为所有的连接都会饱和?

我的代码如下(为清楚起见缩短了)

服务器端:

from flask import Flask
import flask
import serial

app = Flask(__name__)
app.debug = True

def event_barcode():
    ser = serial.Serial()
    ser.port = 0
    ser.baudrate = 9600
    ser.bytesize = 8
    ser.parity = serial.PARITY_NONE
    ser.stopbits = serial.STOPBITS_ONE
    ser.open()
    s = ser.read(7)
    yield 'data: %s\n\n' % s

@app.route('/barcode')
def barcode():
    newresponse = flask.Response(event_barcode(), mimetype="text/event-stream")
    newresponse.headers.add('Access-Control-Allow-Origin', '*')
    return newresponse

if __name__ == '__main__':
    app.run(port=8080, threaded=True)

客户端:

    <!DOCTYPE HTML>
<html>
<head>
    <meta http-equiv=Content-Type content="text/html; charset=utf-8">
    <title>TEST</title>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js" type="text/javascript" charset="utf-8"></script>
    <script>

        $(document).ready(function(){
            if (!!window.EventSource) {
                console.log('SSE supported.');
                var source = new EventSource('http://localhost:8080/barcode');

                source.addEventListener('message', function(e) {
                  console.log(e.data);
                }, false);

                source.addEventListener('open', function(e) {
                  console.log('Connection was opened.');
                }, false);

                source.addEventListener('error', function(e) {
                  if (e.readyState == EventSource.CLOSED) {
                    console.log('Connection was closed.');
                  }
                }, false);

            } else {
                console.log('SSE notsupported.');
            }
        });

    </script>
</head>

<body>

</body>
</html>

我在这里查看了更多信息:http: //www.socketubs.net/2012/10/28/Websocket_with_flask_and_gevent/ http://sdiehl.github.com/gevent-tutorial/#chat-server

我希望有人能解决我的问题,并可能为我指出一些解决方案,解决跨域和 3 秒延迟问题。

谢谢。

4

2 回答 2

4

以下是一些可能会有所帮助的要点(我一直打算基于 'django-sse' 发布类似'flask-sse' 的东西):

https://gist.github.com/3680055

https://gist.github.com/3687523

也很有用 - https://github.com/jkbr/chat/blob/master/app.py

“RedisSseStream”类使用 redis 作为后端在线程之间进行通信(尽管 gevent 可以做到这一点?),并“监听”redis 发布事件。

虽然“PeriodicSseStream”不需要redis,但它无法在烧瓶线程之间进行通信,即使用来自另一个响应的信息;如果没有 redis 之类的东西,单独的线程(流和为另一个用户服务的线程)无法通信。

正如 Janus 所说,生成器只返回一个结果——它必须产生多个结果,在这种情况下,它必须封闭在一个循环中,在每次串行轮询后无限产生;您还需要决定什么会限制轮询,它会受时间限制(定期轮询)还是其他限制(例如,如果读取串行端口已经花费了一段时间)?

我不太了解 sse 的性能,或者它的支持程度(以及跨域),但是如果您考虑socket.io,您可以使用来提高 web-socket性能吗?

于 2012-12-25T19:53:45.257 回答
4

回答我自己的问题

  1. 似乎目前确实只有 Firefox 支持 SSE 的 CORS -> 文章
  2. 在 Janus Troelsen 的帮助下,我想出了如何保持连接打开并跨行发送多个条形码(见下面的代码)
  3. 从性能上看,我似乎只能建立一个连接,但这可能是因为我只有一个串口,后续连接无法再打开串口。我认为它可以在烧瓶中工作,但是使用 socketio 和 gevents 的东西会表现得更好,因为它更适合这项工作。这里有一篇有趣的文章。

对于代码:

import flask
import serial
from time import sleep

app = flask.Flask(__name__)
app.debug = True

def event_barcode():
    messageid = 0
    ser = serial.Serial()
    ser.port = 0
    ser.baudrate = 9600
    ser.bytesize = 8
    ser.parity = serial.PARITY_NONE
    ser.stopbits = serial.STOPBITS_ONE
    ser.timeout = 0
    try:
        ser.open()
    except serial.SerialException, e:
         yield 'event:error\n' + 'data:' + 'Serial port error({0}): {1}\n\n'.format(e.errno, e.strerror)
         messageid = messageid + 1
    str_list = []
    while True:
        sleep(0.01)
        nextchar = ser.read()
        if nextchar:
            str_list.append(nextchar)
        else:
            if len(str_list) > 0:
                yield 'id:' + str(messageid) + '\n' + 'data:' + ''.join(str_list) + '\n\n'
                messageid = messageid + 1
                str_list = []

@app.route('/barcode')
def barcode():
    newresponse = flask.Response(event_barcode(), mimetype="text/event-stream")
    newresponse.headers.add('Access-Control-Allow-Origin', '*')
    newresponse.headers.add('Cache-Control', 'no-cache')
    return newresponse

if __name__ == '__main__':
    app.run(port=8080, threaded=True)

因为我想支持多种浏览器,所以 SSE 不是我现在要走的路。我将研究 websockets 并尝试从中工作。

于 2012-12-27T14:41:43.523 回答