2

実行中のマシンのシリアルポートを読み取る(そして読み取り続ける)必要があるWebサーバーを構築しています。
目的は、バーコード スキャナーを読み取れるようにすることと、Server-Sent Events を使用して、読み取ったバーコードでブラウザーを更新することです。

これを行うためにフラスコを使用しています。私はブラウジングしましたが、一部の実装ではフラスコのみが必要であり、Gevent のような非同期ライブラリが必要であると言う人もいれば、Gevent と Redis や RabbitMQ のようなある種のキューが必要であると言う人もいます。

ここで、stackoverflow で見つけた非常に単純な例に基づいてコードを作成しようとしました。ほとんど機能していますが、いくつか質問があります。

  • Chrome ではクロスオリジン エラーが発生します。Access-Control-Allow-Origin ヘッダーを追加することで、FireFox で動作させることができますが、Chrome はまだ動作しません。FF だけが SSE クロスオリジンをサポートするというのは正しいですか? ブラウザーは別のマシンからバーコード データを読み込む必要があるため、CORS をサポートする必要があります。
  • 各メッセージの後、ブラウザーはコンソールにバーコードを表示しますが、その後接続を閉じ、約 3 秒後に再度開きます。これはFlaskに由来するようで、データを提供してから停止します。
  • また、これが負荷の下でどのように機能するのか疑問に思っています。つまり、フラスコは text/event-stream mimetype に対して接続を開いたままにします。複数のクライアントが接続すると、すべての接続が飽和状態になるため、しばらくするとフラスコがブロックされませんか?

私のコードは次のとおりです(わかりやすくするために短縮されています)

サーバ側:

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 を必要としませんが、flask スレッド間で通信できません。つまり、別の応答からの情報を使用します。redis のようなものがなければ、別のスレッド (ストリームと別のユーザーにサービスを提供するスレッド) は通信できません。

Janus が言うように、ジェネレーターは 1 つの結果のみを返します。複数の結果を生成する必要があり、この場合、各シリアル ポールの後に無限に生成されるループに囲まれている必要があります。また、何がポーリングを制限するか、時間によって制限されるか (定期的にポーリングするか)、または何か (たとえば、シリアル ポートの読み取りに時間がかかる場合) を決定する必要があります。

sse のパフォーマンスや、それがどの程度サポートされているか (およびクロスドメインに関して) についてはよくわかりませんが、socket.ioを検討すると、これを使用して Web ソケットのパフォーマンスを向上させることができますか?

于 2012-12-25T19:53:45.257 に答える
4

自分の質問に答える

  1. 今のところ SSE の CORS をサポートしているのは Firefox だけのようです -> 記事
  2. Janus Troelsen の助けを借りて、接続を開いたままにし、回線を介して複数のバーコードを送信する方法を見つけました (以下のコードを参照)。
  3. パフォーマンス的には、1 つの接続しかできないようですが、シリアル ポートが 1 つしかないため、その後の接続でシリアル ポートを開くことができなくなっている可能性があります。フラスコからでも動作すると思いますが、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 は今の私には向いていません。私はウェブソケットを調べて、そこから作業を試みます。

于 2012-12-27T14:41:43.523 に答える