如何在需要时而不是定期将服务器发送的事件通知发送到特定的浏览器会话?

时间:2019-08-02 09:18:25

标签: html server-sent-events

我想使用SSE将数据从服务器发送到登录到我的Web应用程序的 特定个人 客户端,此时它们的会话超时为“ clock”即将到期或有消息应该发送给所有“已连接”用户,类似于UNIX wall命令,例如“系统将在5分钟内不可用,请完成交易并注销” 。'

我在w3schools和MSDN上看到的示例会自动/定期向连接的客户端发送消息,例如传输服务器的时间。

我想避免使用ajax请求从客户端轮询服务器,以询问诸如“我仍然登录吗?”之类的问题。 (会话超时已过期)或“服务器消息是什么(如果有)?”第一个特定于用户,第二个特定于所有当前用户。

可以做到这一点吗,并且有一个如何做到这一点的例子吗?

谢谢

自从发布此问题以来,我使SSE服务器/客户端演示正常工作取得了部分成功。我说,部分成功,是因为我无法使用重试选项获得不同的工作时间。如果我不选择该选项,则客户端每三秒钟会收到一条消息。如果我包含该选项,则客户端仅会收到一条消息,并且会尝试连接服务器。这是PHP服务器端代码:

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

// data: {"msg": "First message"}\ndata: {"msg": "second message"}\n\n

$r  = mt_rand( 1, 10 ) * 1000;
$m  = "retry interval: ${r}";
$r *= 1000;
$t = date( 'r' );

// echo "retry: ${r}" . PHP_EOL; // <==== With out this, it works!

echo "data: {\"message\" : \"${m}\"}" . PHP_EOL;
echo "data: {\"message\" : \"The server time is: ${t}\"}" .  PHP_EOL;
echo PHP_EOL;
ob_flush();
flush();
?>

这是客户端代码:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Server-Sent Events Example</title>
  </head>
  <body>
    <p>
     <button onclick="source.close();">Stop!</button>
    </p>
    <h1>Getting server updates</h1>
    <div id="result"></div>
    <script>

      // Source: https://www.html5rocks.com/en/tutorials/eventsource/basics/

      if( typeof( EventSource ) !== 'undefined' ) {

        // 'http://symfony.dev.iambreakinout.com/SSE_Server.php'

        var source = new EventSource( 'SSE_Server.php' );

        source.addEventListener(
          'message',
          function( event ) {
            if( event.origin != window.location.origin ) {    
              alert( 'Cross-site scripting: message origin did not match:' +
                     '\r\n' +
                     'expected origin: ' + window.location.origin + '\r\n' +
                     'actual origin:  ' + event.origin );

              return;

            }
            else {
              var data = [];

              for( var i = 0, b = 0, e = 0, d = '';; ++i, b = e + 1 ) {

                e         = event.data.indexOf( "\n", b );
                s         = ( ( e > -1 )
                              ? event.data.substr( b, e )
                              : event.data.substr( b ) );
                data[ i ] = JSON.parse( s ).message;

                if( e === -1 ) break;

              }

              document.getElementById( 'result' ).innerHTML +=
                data.join( '<br>' ) + '<br>';

            }

          },
          false );

        source.addEventListener(
          'open',
          function( event ) {
            // Connection was opened.

            document.getElementById( 'result' ).innerHTML += 'Open<br>';
          },
          false );

        source.addEventListener(
          'error',
          function( event ) {
            var readyState;

            //
            // The closed ready state is seen when an error event occurs, but
            // the rest are shown here as a reminder to me of the defined
            // ready state constant values.
            //

            switch( event.currentTarget.readyState ) {
              case EventSource.CONNECTING: readyState = 'Connecting'; break;
              case EventSource.OPEN:       readyState = 'Open';       break;
              case EventSource.CLOSED:     readyState = 'Closed';     break;
              default:                     readyState = '?';          break;
            }
            document.getElementById( 'result' ).innerHTML += readyState +
              '<br>';

          },
          false );

      }
      else {

        document.getElementById("result").innerHTML =
          'Sorry, your browser does not support server-sent events...';

      }
    </script>
    <button onclick="source.close();">Stop!</button>
  </body>
</html>

无需更改客户端代码并允许 echo“ retry:$ {r}”。 PHP_EOL; 语句以指定重试持续时间,导致显示正在连接后停止输出。

获取服务器更新

打开

重试间隔:3000

连接

要允许重试选项起作用,我在做错什么还是没有做?

再次感谢

好的,按原样显示的代码很好,但是当它生成间隔的随机数时,它实际上不应该具有第一个“次1000”。 (du!)。

1 个答案:

答案 0 :(得分:0)

您需要将活动用户保持在某种数据结构中,以便能够向他们发送事件。

以下是有关NodeJS(基于https://github.com/mchaov/simple-sse-nodejs-setup/)的简单注释示例:

const http = require('http');
const msg = " - Simple SSE server - ";
const port = 5000;

// Create basic server
http.createServer(function (request, response) {

    // answer only to event stream requests
    if (request.headers.accept && request.headers.accept == 'text/event-stream') {

        // check if the resource is what we want
        // => http://domain.ext/sse
        // store the user into your data structure
        if (/sse/gim.test(request.url)) {
            sendSSE(request, response);
        }
    }
    else {
        // if not just return that you are online and a string of text
        response.writeHead(200);
        response.write('Welcome to ' + msg + '@ :' + port);
        response.end();
    }
}).listen(port);

// Setup the SSE "service" :)
// You need to cache the "response" object per user, and then you can use the functions below to send messages whenever you desire. You can have a collection that holds all the references to the response object and decide if you want to send message to one or all.
function sendSSE(request, response) {

    // Setup headers
    // For ease of use: example for enabled CORS
    response.writeHead(200, {
        'Content-Type': 'text/event-stream',
        'Cache-Control': 'no-cache',
        'Connection': 'keep-alive',

        // enabling CORS
        'Access-Control-Allow-Origin': "*",
        'Access-Control-Allow-Headers': 'Origin, X-Requested-With, Content-Type, Accept'
    });

    var id = (new Date()).toLocaleTimeString();

    // send first message
    constructSSE(response, id, (new Date()).toLocaleTimeString());

    // send message every second and a half
    setInterval(function () {
        constructSSE(response, id, (new Date()).toLocaleTimeString());
    }, 1500);
}

// setup simple message
// call this function with proper response you took from data structure with active users to send message to the specific user
function constructSSE(response, id, data) {
    response.write('id: ' + id + '\n');
    // response.write('event: ' + 'logout' + '\n'); // check this
    response.write("data: " + msg + port + '\n\n');
}