28

HTML5で開発している新しいWebサイトにサーバー側イベントを提供するために、nodejsサーバーを開発しています。

サーバーに telnet で接続すると、正しく動作し、必要な HTTP 応答ヘッダーが送信され、続いて、動作を証明するために現在 2 秒または 3 秒ごとに生成しているイベントのストリームが送信されます。

FireFox、Chrome、Opera の最新バージョンを試してみましたが、EventSource オブジェクトを作成して nodejs サーバーに接続できましたが、onopen、onmessage、onerror などのイベントを生成するブラウザはありません。

ただし、nodejsサーバーを停止してブラウザからの接続を終了すると、すべてのメッセージが突然ディスパッチされ、すべてのイベントが表示されます。その後、ブラウザーはすべて、仕様に従ってサーバーへの再接続を試みます。

私はすべてをウェブサーバーでホストしています。ローカル ファイルでは何も実行されていません。

購入した本を含め、オンラインで見つけられるものはすべて読みましたが、そのような問題は何もありません。何か足りないものはありますか?

サンプル サーバーの実装

  var http = require('http');
  var requests = [];

  var server = http.Server(function(req, res) {
    var clientIP = req.socket.remoteAddress;
    var clientPort = req.socket.remotePort;

    res.on('close', function() {
      console.log("client " + clientIP + ":" + clientPort + " died");

      for(var i=requests.length -1; i>=0; i--) {
        if ((requests[i].ip == clientIP) && (requests[i].port == clientPort)) {
          requests.splice(i, 1);
        }
      }
    });

    res.writeHead(200, {
      'Content-Type': 'text/event-stream', 
      'Access-Control-Allow-Origin': '*', 
      'Cache-Control': 'no-cache', 
      'Connection': 'keep-alive'});

    requests.push({ip:clientIP, port:clientPort, res:res});

    res.write(": connected.\n\n");
  });

  server.listen(8080);

  setInterval(function test() {
    broadcast('poll', "test message");
  }, 2000);

function broadcast(rtype, msg) {
  var lines = msg.split("\n");

  for(var i=requests.length -1; i>=0; i--) {
    requests[i].res.write("event: " + rtype + "\n");
    for(var j=0; j<lines.length; j++) {
      if (lines[j]) {
        requests[i].res.write("data: " + lines[j] + "\n");
      }
    }
    requests[i].res.write("\n");
  }
}

サンプルの html ページ

<!DOCTYPE html>
<html>
  <head>
    <title>SSE Test</title>
    <meta charset="utf-8" />
    <script language="JavaScript">
      function init() {
        if(typeof(EventSource)!=="undefined") {
          var log = document.getElementById('log');
          if (log) {
            log.innerHTML = "EventSource() testing begins..<br>";
          }

          var svrEvents = new EventSource('/sse');

          svrEvents.onopen = function() {
            connectionOpen(true);
          }

          svrEvents.onerror = function() {
            connectionOpen(false);
          }

          svrEvents.addEventListener('poll', displayPoll, false);             // display multi choice and send back answer

          svrEvents.onmessage = function(event) {
              var log = document.getElementById('log');
              if (log) {
                log.innerHTML += 'message: ' + event.data + "<br>";
              }
            // absorb any other messages
          }
        } else {
          var log = document.getElementById('log');
          if (log) {
            log.innerHTML = "EventSource() not supported<br>";
          }
        }
      }

      function connectionOpen(status) {
          var log = document.getElementById('log');
          if (log) {
            log.innerHTML += 'connected: ' + status + "<br>";
          }
      }

      function displayPoll(event) {
        var html = event.data;
          var log = document.getElementById('log');
          if (log) {
            log.innerHTML += 'poll: ' + html + "<br>";
          }
      }
    </script>
  </head>
  <body onLoad="init()">
    <div id="log">testing...</div>
  </body>
</html>

これらの例は基本的なものですが、本やオンラインで見た他のすべてのデモと同じ種類のものです。eventSource は、クライアント接続を終了するかサーバーを終了する場合にのみ機能しているように見えますが、これは SSE ではなくポーリングであり、特に SSE を使用したいと考えています。

興味深いことに、html5rock のthouseなどのデモも、オンラインで使用すると期待どおりに動作しないようです..

4

3 に答える 3

42

それを割った!:)

テストを手伝ってくれたTom Kerstenの助けに感謝します。コードが問題ではないことが判明しました。

警告.. クライアントが Web リクエストを傍受するウイルス対策ソフトウェアを使用している場合、ここで問題が発生する可能性があります。この場合、エンタープライズ レベルのウイルス対策とファイアウォール保護を提供する Sophos Endpoint Security には、Web 保護と呼ばれる機能があります。この機能には、ダウンロードをスキャンするオプションがあります。SSE 接続はダウンロードとして扱われるため、接続が閉じられ、ストリームがスキャンのために受信されるまでブラウザに解放されないようです。このオプションを無効にすると、問題が解決します。バグ レポートを送信しましたが、他のウイルス対策システムでも同じことが行われる可能性があります。

あなたの提案に感謝し、みんなを助けてください:)

于 2012-10-30T09:25:50.110 に答える
2

http://www.w3.org/TR/eventsource/#parsing-an-event-stream

そのようなリソースのためにリモートサーバーに確立された接続は長寿命であると予想されるため、UA は適切なバッファリングが使用されることを保証する必要があります。特に、行を含む行バッファリングは、単一の U+000A LINE FEED (LF) 文字で終了するように定義されていますが、ブロック バッファリングまたは予想される行末が異なる行バッファリングは、イベント ディスパッチで遅延を引き起こす可能性があります。

"\r\n"(の代わりに)行末で遊んでみてください"\n"

http://www.w3.org/TR/eventsource/#notes

作成者は、HTTP チャンクがこのプロトコルの信頼性に予想外の悪影響を与える可能性があることにも注意してください。メッセージのレートが問題にならないほど十分に高い場合を除き、可能であれば、イベント ストリームを提供するためにチャンクを無効にする必要があります。

于 2012-10-26T07:34:57.453 に答える
0

サーバー側のスクリプトを変更しましたが、これは Chrome で部分的に機能するようです。
ただし、2 つのブロードキャストごとに接続が切断され、クライアントに表示できるのは 1 つだけです。

Firefox は最初のブロードキャストで動作し、次のエラーで停止します。

エラー: ページの読み込み中に /sse への接続が中断されました。

Chrome は再接続を試み、3 回目のブロードキャストを受信します。

ファイアウォールの設定にも関係していると思いますが、いつかうまくいく理由を説明できません。

: 応答のイベント リスナー (10 行目) では、'close' と 'end' の結果が異なり
ます。試してみると、私の結果は [close: 1 成功/2 ブロードキャスト] & [end: 1 成功/8 ブロードキャスト] です。

var http = require('http'), fs = require('fs'), requests = [];

var server = http.Server(function(req, res) {
  var clientIP = req.socket.remoteAddress;
  var clientPort = req.socket.remotePort;
  if (req.url == '/sse') {
    var allClient="";for(var i=0;i<requests.length;i++){allClient+=requests[i].ip+":"+requests[i].port+";";}
    if(allClient.indexOf(clientIP+":"+clientPort)<0){
      requests.push({ip:clientIP, port:clientPort, res:res});
      res.on('close', function() {
        console.log("client " + clientIP + ":" + clientPort + " died");
        for(var i=requests.length -1; i>=0; i--) {
          if ((requests[i].ip == clientIP) && (requests[i].port == clientPort)) {
            requests.splice(i, 1);
          }
        }
      });
    }
  }else{
    res.writeHead(200, {'Content-Type': 'text/html'});
    res.write(fs.readFileSync('./test.html'));
    res.end();
  }
});
server.listen(80);

setInterval(function test() {
  broadcast('poll', "test message");
}, 500);

var broadcastCount=0;
 function broadcast(rtype, msg) {
    if(!requests.length)return;
    broadcastCount++;
    var lines = msg.split("\n");
    for(var i = requests.length - 1; i >= 0; i--) {
        requests[i].res.writeHead(200, {
            'Content-Type': 'text/event-stream',
            'Cache-Control': 'no-cache',
            'Connection': 'keep-alive'
        });
        requests[i].res.write("event: " + rtype + "\n");
        for(var j = 0; j < lines.length; j++) {
            if(lines[j]) {
                requests[i].res.write("data: " + lines[j] + "\n");
            }
        }
        requests[i].res.write("data: Count\: " + broadcastCount + "\n");
        requests[i].res.write("\n");
    }
    console.log("Broadcasted " + broadcastCount + " times to " + requests.length + " user(s).");
 }
于 2012-11-02T05:08:33.483 に答える