SSE(Server-Sent Events)是HTML5的一项技术,它允许服务器单向地向浏览器发送数据。这种技术非常适合需要实时更新内容的应用,如股票行情、社交媒体更新、实时通知等。

SSE的特点

单向通信:服务器向客户端发送数据,但客户端不能向服务器发送数据(与WebSockets不同,WebSockets是双向通信的)。
持续连接:客户端与服务器之间保持一个持续的连接,服务器可以在任何时候发送数据给客户端。
自动重连:如果连接断开,浏览器会自动尝试重新连接。

前端代码

<!DOCTYPE html>
<html>
<head>
  <title>SSE demo</title>
</head>
<body>
<h1>Server-Sent Events</h1>
<div id="time"></div>

<script>
  const eventSource = new EventSource('http://localhost:8000/events.php');

  eventSource.onmessage = function(event) {
    if(event.data==="closed"){
      eventSource.close();
    }
    document.getElementById('time').innerHTML = event.data;
  };

  eventSource.onerror = function(err) {
    console.error('EventSource error:', err);
  };

  eventSource.onopen = function(event) {
    console.log('Connection opened');
  };
</script>
</body>
</html>

后端Php代码

/Users/xxx/Sites/localhost/events.php

<?php
header("Access-Control-Allow-Origin: *");
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('Connection: keep-alive');

$lastEventId = isset($_SERVER['HTTP_LAST_EVENT_ID']) ? intval($_SERVER['HTTP_LAST_EVENT_ID']) : 0;

function sendMsg($id, $msg) {
    global $lastEventId;
    echo "id: $id" . PHP_EOL;
    if($msg!= "closed"){
        foreach ($_GET as $key => $value) {
            $msg .= " GET: $key: $value" . " ";
        }
        $msg .= " Last Event ID: $lastEventId" . " ";
    }
    echo "data: $msg" . PHP_EOL;
    echo PHP_EOL;
    ob_flush();
    flush();
}

// 从上次的事件ID开始计数
$counter = $lastEventId;

echo "retry: 5000" . PHP_EOL;  // 设置重连时间为5秒

while (true) {
    sendMsg($counter, 'Server time: ' . date('H:i:s'));
    sleep(1);

    if ($counter % 10 == 0) {
        $counter++;
        sendMsg($counter, 'SSE connection closed by server');
        break;
    }
    if($counter > 212) {
        sendMsg($counter, 'closed');

    }
    $counter++;
}

命令行:php -S localhost:8000 -t /Users/xxx/Sites/localhost

断线重连

实例图

其他事件

Server-Sent Events (SSE)中,默认的事件类型是message,但你可以自定义事件类型。除了默认的message事件之外,SSE还允许你定义并发送其他类型的事件。通过自定义事件类型,你可以在客户端区分处理不同类型的消息。

自定义事件类型

服务器端 PHP 示例代码

在服务器端,你可以通过在SSE流中指定事件类型来发送自定义事件。例如,使用event: <event_type>来指定事件类型。

<?php
header("Access-Control-Allow-Origin: *");
header('Content-Type: text/event-stream');
header('Cache-Control: no-cache');
header('Connection: keep-alive');

$lastEventId = isset($_SERVER['HTTP_LAST_EVENT_ID']) ? intval($_SERVER['HTTP_LAST_EVENT_ID']) : 0;

function sendMsg($id, $event, $msg) {
    echo "id: $id" . PHP_EOL;
    echo "event: $event" . PHP_EOL;  // 指定事件类型
    echo "data: " . json_encode($msg) . PHP_EOL;
    echo PHP_EOL;
    ob_flush();
    flush();
}

$counter = $lastEventId;

echo "retry: 5000" . PHP_EOL;  // 设置重连时间为5秒

while (true) {
    if ($counter % 2 === 0) {
        sendMsg($counter, 'custom-event', array('message' => 'Custom event', 'time' => date('H:i:s')));
    } else {
        sendMsg($counter, 'message', array('message' => 'Default message event', 'time' => date('H:i:s')));
    }

    if ($counter >= 10) {
        sendMsg($counter, 'end', array('message' => 'SSE connection closed by server', 'time' => date('H:i:s')));
        break;
    }

    sleep(1);
    $counter++;
}
?>

客户端 JavaScript 示例代码

在客户端,你可以为自定义事件类型添加事件监听器。

<!DOCTYPE html>
<html>
<head>
    <title>SSE Example</title>
</head>
<body>
    <h1>Server-Sent Events</h1>
    <div id="default"></div>
    <div id="custom"></div>
    <div id="end"></div>

    <script>
        const eventSource = new EventSource('http://your_domain.com/events');

        eventSource.addEventListener('message', function(event) {
            const data = JSON.parse(event.data);
            document.getElementById('default').innerHTML = `Default event: ${data.message} at ${data.time}`;
            console.log('Default event:', data);
        });

        eventSource.addEventListener('custom-event', function(event) {
            const data = JSON.parse(event.data);
            document.getElementById('custom').innerHTML = `Custom event: ${data.message} at ${data.time}`;
            console.log('Custom event:', data);
        });

        eventSource.addEventListener('end', function(event) {
            const data = JSON.parse(event.data);
            document.getElementById('end').innerHTML = `End event: ${data.message} at ${data.time}`;
            console.log('End event:', data);
            eventSource.close();
        });

        eventSource.onerror = function(err) {
            console.error('EventSource error:', err);
            if (eventSource.readyState === EventSource.CLOSED) {
                console.log('Connection was closed. Attempting to reconnect...');
            }
        };

        eventSource.onopen = function(event) {
            console.log('Connection opened');
        };
    </script>
</body>
</html>
Last modification:July 4, 2024
如果觉得我的文章对你有用,请随意赞赏