浏览器和服务器建一条不断开的 HTTP 连接,服务器想推就推,浏览器收着。单向的,浏览器不能通过这条连接发消息回来。比 WebSocket
  少握一次手,协议更简单。

  ---
  代码

  <?php
  $server = new Swoole\Http\Server('0.0.0.0', 9502);

  $server->on('request', function ($req, $res) use ($server) {
      // SSE 必须的响应头
      $res->header('Content-Type', 'text/event-stream');
      $res->header('Cache-Control', 'no-cache');
      $res->header('X-Accel-Buffering', 'no'); // 告诉 nginx 别缓冲

      // 每秒推一次假股票数据
      Swoole\Timer::tick(1000, function ($timerId) use ($res, $server) {
          $data = json_encode([
              'symbol' => 'AAPL',
              'price'  => round(150 + (mt_rand(-100, 100) / 100), 2),
              'time'   => date('H:i:s'),
          ]);

          // SSE 格式固定:data: 内容\n\n
          $ok = $res->write("data: $data\n\n");

          // 客户端断了就停定时器
          if (!$ok) Swoole\Timer::clear($timerId);
      });
  });

  $server->start();

  ---
  浏览器端(5行)

  <script>
      const es = new EventSource('http://localhost:9502');
      es.onmessage = e => console.log(JSON.parse(e.data));
      es.onerror   = () => es.close();
  </script>

  ---
  解释

  SSE 格式:就是普通 HTTP 响应,但不关闭连接,一直往里写 data: xxx\n\n,浏览器认识这个格式会自动触发 onmessage

  $res->write():Swoole 的分块写,不调 end(),连接就不断

  Timer::tick(1000, ...):每 1000ms 跑一次回调,不阻塞其他请求,Swoole 协程调度的

  $ok = false:客户端关了标签页,write() 返回 false,这时候清掉定时器,不然一直在跑

  ---
  测试

  php server.php

  # 终端看推送
  curl -N http://localhost:9502

  # 输出:
  # data: {"symbol":"AAPL","price":150.42,"time":"14:23:01"}
  # data: {"symbol":"AAPL","price":149.98,"time":"14:23:02"}
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐