sse EventSource 浏览器兼容性 移动设备兼容性 sse单例模式 node搭建server EventSourcePolyfill配置url携带请求头信息 类似的长连接实时技术

SSE(Server-Sent Events)是一种在客户端和服务器之间实现单向实时通信的技术。它允许服务器向客户端推送数据,而无需客户端发起请求。

SSE(Server-Sent Events)在大多数现代浏览器中都有良好的支持,但仍然存在一些兼容性问题,特别是在旧版浏览器和移动设备上。下面是一些有关 SSE 在不同浏览器和移动设备上的兼容性问题的一般指导:
import { EventSourcePolyfill } from 'event-source-polyfill';

SSE自带客户端重启,在和服务器端失去连接,或者服务器端返回服务器端超时的时候会自动重连。不需要手动维护重连和重试。

  1. 浏览器兼容性:

    • 支持良好的浏览器:Chrome、Firefox、Safari、Edge、Opera 等现代浏览器都对 SSE 提供了很好的支持。
    • 旧版浏览器:Internet Explorer 不支持 SSE,因此在考虑 SSE 兼容性时需要注意。
  2. 移动设备兼容性:

    • iOS:SSE 在 iOS Safari 上有良好的支持,适用于 iOS 4.2+ 的版本。
    • Android:SSE 在 Android 上的兼容性取决于设备和浏览器。大多数现代 Android 设备上的 Chrome 浏览器都支持 SSE。但在旧版 Android 设备上或使用其他浏览器时,可能会遇到兼容性问题。

参考资料:SSE服务端消息推送:https://www.cnblogs.com/cxygg/p/17147296.html

if (typeof EventSource !== 'undefined') {
  // 浏览器支持 SSE
  // 可以在这里处理 SSE 的代码
} else {
  // 浏览器不支持 SSE
  // 可以提供替代方案或给出提示信息
  //如果浏览器不支持 SSE,你可以在条件为假的分支中提供替代方案,如使用长轮询或 WebSocket 技术来实现实时数据推送,或者向用户显示一条提示信息,说明当前浏览器不支持 SSE
}

为了确保 SSE 在你的 H5 页面项目中的兼容性,你可以采取以下措施:

  1. 提供替代方案:考虑为不支持 SSE 的浏览器提供替代方案,例如使用长轮询(Long Polling)或 WebSocket 技术来实现实时数据推送。

  2. 浏览器嗅探:在项目中使用浏览器嗅探技术,检测用户所使用的浏览器和版本,并根据浏览器的兼容性情况选择合适的推送技术。

  3. 测试和适配:在开发过程中,使用不同的浏览器和移动设备进行测试,并根据测试结果进行适配和修复。确保 SSE 在目标浏览器和设备上正常运行。

需要注意的是,具体的兼容性问题可能会受到浏览器和设备版本的影响,因此在项目中最好进行兼容性测试,以确保 SSE 在目标浏览器和设备上的正常工作。

另外,可以使用一些 JavaScript 库或框架(如 EventSource Polyfill)(import { EventSourcePolyfill } from 'event-source-polyfill';)来提供 SSE 的兼容性支持,以解决部分浏览器兼容性问题。这些库可以填补一些浏览器的实现差异,使 SSE 在更多浏览器和设备上能够正常工作。

在 Vue 3 中,你可以使用 SSE 来实现实时通信。下面是一个使用 SSE 的基本示例:

// 创建一个 SSE 连接
const eventSource = new EventSource('/sse-endpoint');

// 监听接收到的消息
eventSource.addEventListener('message', (event) => {
  console.log('Received message:', event.data);
});

// 监听错误事件
eventSource.addEventListener('error', (error) => {
  console.error('SSE error:', error);
});

在上面的代码中,/sse-endpoint 是服务器上 SSE 的端点,用于建立 SSE 连接。你需要在服务器端实现 SSE 的逻辑来处理客户端的连接和数据推送。

在服务器端,你需要设置 SSE 的响应头,并发送事件和数据给客户端。以下是一个 Node.js 的示例:

const http = require('http');

const server = http.createServer((req, res) => {
  if (req.url === '/sse-endpoint') {
    res.writeHead(200, {
      'Content-Type': 'text/event-stream',
      'Cache-Control': 'no-cache',
      'Connection': 'keep-alive'
    });

    // 每隔一秒发送一个事件和数据给客户端
    setInterval(() => {
      const message = 'Hello from server';
      res.write(`data: ${message}\n\n`);
    }, 1000);
  }
});

server.listen(3000, () => {
  console.log('SSE server is running on port 3000');
});

在上面的示例中,服务器通过每秒向客户端发送一个事件和数据来模拟实时通信。你可以根据自己的需求来调整实际的逻辑。

这是一个简单的 SSE 示例,它可以让你在 Vue 3 中实现实时通信。请注意,SSE 是一种单向通信技术,只允许服务器向客户端推送数据。如果你需要双向通信,你可能需要考虑使用其他技术,如 WebSockets。

希望对你有所帮助!如有其他问题,请随时提问。


sse-client.ts

export default class SSEClient {
    source: EventSource;

    element: HTMLElement;

    constructor(url) {
        this.source = new EventSource(url || 'http://127.0.0.1:8844/stream');
        this.source.onopen = this.onOpen.bind(this);
        this.source.onerror = this.onError.bind(this);
        this.source.addEventListener('connecttime', this.onConnectTime.bind(this), false);
        this.source.onmessage = this.onMessage.bind(this);
    }

    // eslint-disable-next-line class-methods-use-this
    onOpen(event) {
        console.log('onOpen-event :>> ', event);
    }

    // eslint-disable-next-line class-methods-use-this
    onError(event) {
        console.log('onError-event :>> ', event);
    }

    // eslint-disable-next-line class-methods-use-this
    onConnectTime(event) {
        console.log('onConnectTime-event :>> ', event);
    }

    // eslint-disable-next-line class-methods-use-this
    onMessage(event) {
        console.log('onMessage-event :>> ', event);
        const localstore = useLocalStore();
        localstore.setAdvertiseMsg([event.data]);
    }

    close() {
        console.log('close :>> ');
        this.source.close();
    }
}

// 使用示例
// const sseClient = new SSEClient('http://127.0.0.1:8844/stream', 'example');
// 当你想关闭连接时,调用sseClient.close();

sse单例模式:

import { EventEmitter } from 'eventemitter3';
import { EventSourcePolyfill } from 'event-source-polyfill';
import { getChannelSync } from '../native';
import { useLocalStore } from '@/stores';
import { events } from '@/utils/eventBus';
import { ChannelMap, NormalErrorCode } from '@/utils/constants';
import { myTimeout } from '@/utils';

class H5SSE extends EventEmitter {
    static instance: H5SSE;

    sse: undefined | EventSource;

    retryTimes = 0;

    url = '';

    init(url: string) {
        this.url = url;
        this.destroy();
        const localStore = useLocalStore();
        if (getChannelSync() === ChannelMap.Android || !localStore.userInfo?.token) return;
        /** 处理浏览器多开 */
        const bc = window?.BroadcastChannel ? new BroadcastChannel('cloud-h5') : null;
        bc?.postMessage({ type: 'sse' });

        this.sse = new EventSourcePolyfill(url, { headers: { Authorization: localStore.userInfo?.token }, heartbeatTimeout: 10 * 60 * 1000 });
        this.sse!.addEventListener('message', e => {
            try {
                const data = JSON.parse(e.data);
                events.trigger('SSE', data);
                console.log('sse', data);

                /** 登出消息,销毁SSE */
                if (data.code === NormalErrorCode.LoginExpired) this.destroy();
            } catch (error) {
                console.log(error);
                this.emit('error', error);
            }
        });
        this.sse!.addEventListener('error', () => {
            console.log('sse error:>> ');
            if (this.retryTimes < 1) {
                this.retryTimes++;
                myTimeout(5 * 60 * 1000).then(() => {
                    this.init(this.url);
                });
            } else {
                this.destroy();
            }
        });

        if (bc)
            bc.onmessage = e => {
                if (e.data?.type === 'sse') {
                    this.destroy();
                }
            };
    }

    destroy() {
        this.sse?.close();
        this.sse = undefined;
    }

    static getInstance() {
        if (!H5SSE.instance) {
            H5SSE.instance = new H5SSE();
        }
        return H5SSE.instance;
    }
}

export default H5SSE.getInstance();

server.js

const http = require('http');

http.createServer(function (req, res) {
    const fileName = `.${req.url}`;

    if (fileName === './stream') {
        res.writeHead(200, {
            'Content-Type': 'text/event-stream',
            'Cache-Control': 'no-cache',
            Connection: 'keep-alive',
            'Access-Control-Allow-Origin': '*'
        });
        res.write('retry: 10000\n');
        res.write('event: connecttime\n');
        res.write(`data: ${new Date()}\n\n`);
        res.write(`data: ${new Date()}\n\n`);

        interval = setInterval(function () {
            res.write(`data: ${new Date()}\n\n`);
        }, 60000);

        req.connection.addListener(
            'close',
            function () {
                clearInterval(interval);
            },
            false
        );
    }
}).listen(8844, '127.0.0.1');


除了 SSE,还有其他一些长连接实时技术可以用于实现实时通信。以下是一些常见的选项:

  1. WebSockets:WebSockets 是一种全双工通信协议,允许客户端和服务器之间建立持久性的连接。通过 WebSockets,客户端和服务器可以在任何时间点都能够相互发送消息。Vue 3 中可以使用 WebSocket API 或第三方库(如 Socket.io)来实现实时通信。

  2. Comet:Comet 是一种基于 HTTP 的实时通信技术,它使用长轮询(Long Polling)或流(Streaming)方式来实现服务器向客户端推送数据。在长轮询中,客户端发送一个请求,服务器保持连接打开直到有新的数据可用,然后再响应给客户端。在流模式中,服务器会持续地向客户端发送数据流。Comet 技术在实现上相对复杂,但在一些特定场景下仍然有一定的应用。

  3. Serverless Functions:Serverless Functions 允许你在云端运行小型函数,这些函数可以被触发并执行特定的操作。通过使用服务提供商如 AWS Lambda、Google Cloud Functions 或 Azure Functions,你可以创建一个函数来处理实时通信需求,例如接收客户端的请求并推送数据到客户端。

  4. MQTT:MQTT(Message Queuing Telemetry Transport)是一种轻量级的发布/订阅消息传输协议,常用于物联网(IoT)和实时通信场景。MQTT 协议支持异步、一对多的通信模式,可以实现高效的实时消息传递。

这些技术都可以用于实现实时通信,具体选择取决于你的应用场景和需求。对于 Vue 3,你可以使用相应的库或原生 API 来集成这些技术。请注意,这些技术可能需要在服务器端进行相应的设置和配置,以确保正确的通信流程。

希望这些信息对你有帮助!如有其他问题,请随时提问。

posted @ 2023-12-28 14:17  yoona-lin  阅读(1108)  评论(0编辑  收藏  举报