14

サーバーサイドイベントは私の要件に完全に適合し、実装が簡単であるように思われるため、理解しようとしていますが、あいまいなエラーを乗り越えることができず、接続が繰り返し閉じられて再接続されているように見えます-開いた。私が試したことはすべて、このチュートリアルと他のチュートリアルに基づいています。

PHP は単一のスクリプトです。

<?php
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');

function sendMsg($id, $msg) {
  echo "id: $id" . PHP_EOL;
  echo "data: $msg" . PHP_EOL;
  echo PHP_EOL;
  ob_flush();
  flush();
}

$serverTime = time();
sendMsg($serverTime, 'server time: ' . date("h:i:s", time()));
?>

JavaScript は次のようになります (ボディ ロードで実行)。

function init() {

    var source;
    if (!!window.EventSource) {
        source = new EventSource('events.php');
        source.addEventListener('message', function(e) {
            document.getElementById('output').innerHTML += e.data + '<br />';
        }, false);
        source.addEventListener('open', function(e) {
            document.getElementById('output').innerHTML += 'connection opened<br />';
        }, false);
        source.addEventListener('error', function(e) {
            document.getElementById('output').innerHTML += 'error<br />';
        }, false);
    }
    else {
        alert("Browser doesn't support Server-Sent Events");
    }
}

ちょっと調べたけど情報出てこない

  1. サーバー送信イベントをサポートするために Apache に特別な設定が必要な場合、および
  2. この種のセットアップでサーバーからプッシュを開始するにはどうすればよいですか (たとえば、CLI から PHP スクリプトを実行して、既に接続されているブラウザーにプッシュすることはできますか?)

この JS を Chrome (16.0.912.77) で実行すると、接続が開かれ、時間が受信され、エラーが発生し (エラー オブジェクトに有用な情報はありません)、3 秒で再接続され、同じプロセスが実行されます。Firefox (10.0) でも同じ動作をします。

EDIT 1:問題は使用しているサーバーに関連している可能性があると考えたので、バニラのXAMPPインストールでテストしたところ、同じエラーが発生しました。基本的なサーバー構成は、変更/追加構成なしでこれを処理できる必要がありますか?

編集 2 : 以下は、ブラウザーからの出力の例です。

connection opened
server time: 01:47:20
error
connection opened
server time: 01:47:23
error
connection opened
server time: 01:47:26
error

これがどこで間違っているのか誰にも教えてもらえますか? 私が見たチュートリアルでは、SSE が非常に単純であるように見えます。また、上記の 2 つの番号付きの質問に対する回答は、非常に役立ちます。

ありがとう。

4

8 に答える 8

21

問題はあなたのphpです。

PHP スクリプトの記述方法では、実行ごとに 1 つのメッセージのみが送信されます。これは、php ファイルに直接アクセスする場合の動作であり、EventSource を使用してファイルにアクセスする場合の動作です。したがって、php スクリプトで複数のメッセージを送信するには、ループが必要です。

<?php
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');

function sendMsg($id, $msg) {
  echo "id: $id" . PHP_EOL;
  echo "data: $msg" . PHP_EOL;
  echo PHP_EOL;
  ob_flush();
  flush();
}
while(true) {
  $serverTime = time();
  sendMsg($serverTime, 'server time: ' . date("h:i:s", time()));
  sleep(1);
}
?>

メッセージが送信されるたびに 1 秒待機する無限ループを含めるようにコードを変更しました (ここにある例に従います: Using server-sent events )。

このタイプのループは、私が現在使用しているものであり、絶え間ない接続の切断と 3 秒ごとの再接続を排除しました。ただし(これはクロムでのみテストしました)、接続は30秒間しか維持されません。これが事実である理由を理解し続け、解決策を見つけたら投稿しますが、それまでは、少なくとも目標に近づくはずです.

それが役立つことを願って、

編集:

PHP で途方もなく長い時間接続を開いたままにしておくには、max_execution_time を設定する必要があります (これについては tomfumb に感謝します)。これは、少なくとも 3 つの方法で実現できます。

  1. php.ini を変更できる場合は、「max_execution_time」の値を変更してください。ただし、これにより、指定した時間だけすべてのスクリプトを実行できます。
  2. 長時間実行したいスクリプトでは、関数 ini_set(key, value) を使用します。ここで、key は「max_execution_time」、value はスクリプトを実行する秒数です。
  3. 長時間実行したいスクリプトでは、関数 set_time_limit(n) を使用します。ここで、n はスクリプトを実行する秒数です。
于 2012-02-20T12:22:38.783 に答える
2

Server Sent Events は、Javascript 部分に関してのみ簡単です。まず、インターネットの SSE に関する多くのチュートリアルでは、サーバー部分で接続を閉じています。PHP や Java の例です。厳密に定義されたペイロード構造 (およびサーバー側で設定されたクライアントの再試行値などのいくつかのマイナーな機能) を使用して「Ajax ポーリング」システムを実装する別の方法が得られるため、これは本当に驚くべきことです。数行の jQuery で簡単に実装できます。その場合、SSEは必要ありません。

SSE の仕様によると、再試行はクライアント側のループを実装する通常の方法であってはなりません。私にとってSSEは、最初のデータをクライアントにプッシュした後に接続を閉じないサーバーバックエンドに依存する一方向のストリーミング方法です。

Java では、要求スレッドをすぐに解放し、別のスレッドで処理/ストリーミングを行うために、Servlet3 Async 仕様を使用すると便利です。これはこれまでのところ機能しますが、EventSource リクエストの 30 秒の接続寿命が気に入りません。5秒ごとにデータをプッシュしても、接続は30秒後に終了します(chrome、firefox)。もちろん、SSE はデフォルトで 3 秒後に再接続しますが、それでもこれが本来あるべき方法だとは思いません。

1 つの問題は、一部の Java MVC フレームワークには、データ送信後に接続を開いたままにする機能がないため、裸のサーブレット API にコーディングすることになることです。Java でのプロトタイプのコーディングに 24 時間費やした後、従来の jQuery-Ajax ループに勝るメリットがそれほど大きくないため、多かれ少なかれがっかりしています。また、SSE 機能のポリフィルに関する問題も存在します。

于 2014-09-17T15:15:08.513 に答える
1

問題はサーバー側の問題ではありません。これはすべてクライアントで発生し、仕様の一部です (奇妙に聞こえることはわかっています)。

http://dev.w3.org/html5/eventsource/

「ユーザー エージェントが接続を再確立する場合、ユーザー エージェントは次の手順を実行する必要があります。これらの手順は、タスクの一部としてではなく、非同期で実行されます。(もちろん、キューに入れられたタスクは通常のタスクのように実行され、実行されません。非同期に。)」

  1. 次の手順を実行するタスクをキューに入れます。
    1. readyState 属性が CLOSED に設定されている場合は、タスクを中止します。
    2. readyState 属性を CONNECTING に設定します。
    3. EventSource オブジェクトで error という名前の単純なイベントを発生させます。

ここでエラーが発生する必要はないようです。そのため、Init 関数を変更して、接続中に発生したエラー イベントを除外しました。

function init() {
                var CONNECTING = 0;
                var source;
                if (!!window.EventSource) {
                    source = new EventSource('events.php');
                    source.addEventListener('message', function (e) {
                        document.getElementById('output').innerHTML += e.data + '
'; }, false); source.addEventListener('open', function (e) { document.getElementById('output').innerHTML += 'connection opened
'; }, false); source.addEventListener('error', function (e) { if (source.readyState != CONNECTING) { document.getElementById('output').innerHTML += 'error
'; } }, false); } else { alert("Browser doesn't support Server-Sent Events"); } }
于 2012-02-09T21:20:46.363 に答える
0

サーバー送信イベント 名前が示すように、サーバーからデータを取得するために 3 秒ごとに再接続する必要がある場合、データはサーバーからクライアントに移動する必要があり、他のポーリング メカニズムと同じです。クライアントが認識していない新しいデータです。ヘッダーがキープアライブであってもサーバーは接続を閉じるため、phpスクリプトを無限ループで実行する以外に方法はありませんが、サーバーの負担を防ぐためにかなりのスレッドスリープがあります。新しいデータを求めて 3 秒ごとにサーバーにスパムを送信するよりも、他の方法を検討してください。

于 2014-04-20T16:17:20.090 に答える
0

仕様によると、接続が閉じられたときの 3 秒の再接続は設計によるものです。ループのある PHP は理論的にはこれを停止するはずですが、PHP スクリプトは無期限に実行され、リソースを浪費します。この問題のため、SSE に apache と php を使用しないようにしてください。

標準の http 応答は、応答が送信されると接続を閉じる必要があります。これは、プロキシを使用している場合に問題が発生する可能性がありますが、接続が開いたままであることをブラウザに伝える必要があるヘッダー「connection: keep-alive」で変更できます。

node.js などは、apache/php よりも SSE に使用するのに適したエンジンであり、基本的に JavaScript であるため、理解するのは非常に簡単です。

于 2012-10-24T11:04:46.053 に答える
0

私は同じことをしようとしています。さまざまな程度の成功を収めています。

  1. 前述と同じ js コードを実行している Firefox でも同じ問題がありました。Nginx サーバーと、終了した (つまり、継続的なループがない) PHP を使用すると、PHP が終了したときにのみ、Firefox から「要求」にメッセージを返すことができました。PHP.exe で PHP をスクリプトとして実行すると、concole ですべて問題なく実行されます。フラッシュすると文字列が出力されます。ただし、Nginx は PHP が完了するまでデータを送信しません。余分な \r\n\r\n を追加しようとしましたが、flush() または ob_flush() は役に立ちませんでした。Wireshark ログに示されているように、データのプッシュはなく、GET への応答パケットが遅延しているだけです。

ソースからの再ビルドが必要な Nginx 用の「プッシュ」モジュールが必要であることをお読みください。

したがって、これは間違いなく Nginx の問題です。

  1. 「C」でソケットを使用すると、期待どおりにデータを Firefox にプッシュでき、ソケットは開いたままになり、メッセージが失われることはありませんでした。ただし、これには page.html をサーバーする必要があり、同じソケットまたは firefox からのイベント/ストリームがクロス サイト URL の問題により接続されないという欠点があります。特定の状況ではこれを回避する方法がいくつかありますが、メニュー システムの iframe ではそうではありません。このアプローチは、SSE が Firefox で動作し、wireshark ログにプッシュされたパケットがあることを証明しました。オプション 1 には要求/応答パケットしかありませんでした。

このすべてが言った、私はまだ解決策を持っていません。PHP と Nginx のバッファリングを削除しようとしました。しかし、PHP が終了するまでは何もありません。さまざまなヘッダー オプションを試しました。たとえば、チャンクも役に立ちませんでした。本格的な http サーバーを 'C' で書く気はありませんが、これが現時点で機能している唯一のオプションのようです。私はApacheを試してみようとしていますが、ほとんどの記事は、これがこの仕事でNginxよりも悪いことを示唆しています.

于 2015-11-27T01:12:02.757 に答える
0

カスタム イベント ループを実装することで、それを行うことができました。この html5 機能はまったく準備ができておらず、最新バージョンの google chrome でも互換性の問題があるようです。これは、Firefoxで作業しています(Chromeで正しく送信されたメッセージを取得できません):

var source;

function Body_Load(event) {
    loopEvent();
}

function loopEvent() {
    if (source == undefined) {
        source = new EventSource("event/message.php");
    }
    source.onmessage = function(event) {
        _e("out").value = event.data;
        loopEvent();
    }
}

PS : _e は document.getElementById(id); を呼び出す関数です。

于 2012-06-21T21:59:33.860 に答える
0

私が見ることができるコードには実際の問題はありません。正解として選択された答えは、不正解です。

これは、質問で言及されている動作をまとめたものです (http://www.w3.org/TR/2009/WD-html5-20090212/comms.html):

「そのようなリソース (正しい MIME タイプを持つ) がロードを完了する (つまり、HTTP 応答本文全体が受信されるか、接続自体が閉じられる) 場合、ユーザー エージェントは、再接続時間に等しい遅延の後に、イベント ソース リソースを再度要求する必要があります。イベント ソース。これは、以下にリストされているエラー ケースには適用されません。」

問題はストリームにあります。以前、perl で単一の EventStream を開いたままにすることに成功しました。適切な HTTP ヘッダーを送信し、ストリーム データの送信を開始するだけです。ストリーム サーバー側をシャットダウンしないでください。問題は、ほとんどの HTTP ライブラリがストリームを開いた後に閉じようとすることです。これにより、クライアントは完全に標準に準拠したサーバーへの再接続を試みます。

これは、while ループを実行することで問題が解決されたように見えることを意味します。これにはいくつかの理由があります。

A) コードは、あたかも大きなファイルを押し出すかのように、データを送信し続けます B) コード (php サーバー) は、接続を閉じようとする機会がありません

ただし、ここでの問題は明らかです。ストリームを維持するには、一定のデータ ストリームを送信する必要があります。これにより、リソースが無駄に使用され、SSE ストリームが提供するはずの利点がすべて失われます。

私は知っているほど十分なPHPの達人ではありませんが、PHPサーバー/コードの後半の何かがストリームを時期尚早に閉じていると思います。HTTP::Response が接続を閉じていたため、クライアント ブラウザが接続を再度開かせようとしたため、ストリームを開いたままにするために、Perl を使用してソケット レベルでストリームを操作する必要がありました。Mojolicious (別の Perl Web フレームワーク) では、Stream オブジェクトを開き、タイムアウトをゼロに設定して、ストリームがタイムアウトしないようにすることで、これを行うことができます。

したがって、ここでの適切な解決策は、while ループを使用しないことです。それは、php ストリームを開き、開いたままにするための適切な php 関数を呼び出すことです。

于 2012-06-08T14:10:53.423 に答える