Подтвердить что ты не робот

JavaScript EventSource SSE не работает в браузере

Я разрабатываю сервер nodejs для предоставления серверных событий для нового веб-сайта, который я разрабатываю в HTML5.

Когда я подключу 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.

Интересно, что демонстрации, такие как thouse from html5rock, также кажутся не совсем подходящими, как ожидалось, когда я использую их в Интернете.

4b9b3361

Ответ 1

взломал его!:)

Благодаря некоторой помощи Tom Kersten, которая помогла мне с тестированием. Оказывается, код не является проблемой.

Будьте предупреждены.. если ваш клиент использует любое антивирусное программное обеспечение, которое перехватывает веб-запросы, это может вызвать проблемы здесь. В этом случае Sophos Endpoint Security, которая обеспечивает защиту от вирусов и брандмауэров корпоративного класса, имеет функцию, называемую веб-защитой. Внутри этой функции есть возможность сканировать загрузки; кажется, что SSE-соединение рассматривается как загрузка и, таким образом, не передается в браузер до тех пор, пока соединение не будет закрыто, и поток, полученный для сканирования. Отключение этой опции устраняет проблему. Я отправил отчет об ошибке, но другие антивирусные системы могут сделать то же самое.

спасибо за ваши предложения и помогите всем:)

Ответ 2

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

Так как соединения, установленные для удаленных серверов для таких ресурсов, ожидается, что они будут долгоживущими, УА должны обеспечить, чтобы соответствующие используется буферизация. В частности, в то время как буферизация строк с линиями определенный для завершения одним символом U + 000A LINE FEED (LF), безопасен, буферизация блоков или буферизация строк с различными ожидаемыми окончаниями строк может вызвать задержки при отправке событий.

Попробуйте сыграть с окончанием строки ("\r\n" вместо "\n").

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

Авторы также предупреждают, что HTTP-канал может иметь неожиданный отрицательное влияние на надежность этого протокола. Где возможно, блокирование должно быть отключено для обслуживания потоков событий, если скорость сообщений достаточно высока, чтобы это не имело значения.

Ответ 3

Я изменил вашу серверную сторону script, которая "кажется" частично работает для Chrome.
Но разрыв соединения для каждых 2 широковещательных и только 1 может быть показан на клиенте.

Firefox работает для 1-го вещания и останавливается этой ошибкой:

Ошибка: соединение с /sse было прервано во время загрузки страницы.

И Chrome попытается снова подключиться и получить третью трансляцию.

Я думаю, что это связано с настройкой брандмауэра, но не может объяснить, почему когда-нибудь будет работать.

Примечание. Для прослушивателя событий ответа (строка 10) "close" и "end" имеют другой результат,
Вы можете попробовать, и мой результат: [close: 1 success/2 broadcast] и [end: 1 success/8 broadcast]

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).");
 }