软件教程 2025年08月6日
0 收藏 0 点赞 735 浏览 5071 个字
摘要 :

文章目录 一、SSE技术是什么? 二、SSE和WebSocket有啥不同? 三、服务端响应格式有讲究 四、nginx配置要点 五、用node + express实现SSE消息推送 六、前端接收SSE数据 ……




  • 一、SSE技术是什么?
  • 二、SSE和WebSocket有啥不同?
  • 三、服务端响应格式有讲究
  • 四、nginx配置要点
  • 五、用node + express实现SSE消息推送
  • 六、前端接收SSE数据
  • 七、总结与拓展

在前端开发里,实现服务器和客户端高效通信很重要,SSE(Server-Sent Events)技术就能让服务器主动给客户端推送数据。这篇文章会详细讲讲SSE,像它的原理、和WebSocket的差异,还有用node + express搭建消息推送的具体步骤,新手也能轻松上手!

一、SSE技术是什么?

SSE,也就是服务器发送事件,是专门用来实现服务器主动向客户端推送数据的技术,也被叫做“事件流” 。它是基于HTTP协议的,发起的是一个get请求。利用HTTP的长连接特性,服务器就能把实时数据推送给客户端。不过它有个特点,客户端没办法反过来给服务器发数据,属于单向通信。

SSE连接状态总共就三种:已连接、连接中、已断开 。这些状态是由浏览器自动管理的,我们没办法手动去关闭或者重新打开连接。在JavaScript里,EventSource对象的readyState属性可以获取当前连接状态,是个只读属性,取值0代表正在和服务器建立连接(CONNECTING),1表示已经建立连接,能接收数据(OPEN),2则是连接关闭,不能再接收数据了(CLOSED)。

二、SSE和WebSocket有啥不同?

  1. 通信方式:SSE是单向的,只有服务器能给客户端发数据;WebSocket则是双向通信,客户端和服务器能互相发消息。
  2. 协议基础:SSE依托HTTP协议,走的是get请求;WebSocket一般基于TCP协议。
  3. 跨域能力:因为基于HTTP的get请求,SSE不支持跨域;WebSocket可以跨域,使用起来更灵活。
  4. 重连机制:要是连接断了,SSE的浏览器会自动尝试重连;WebSocket就得手动编写重连代码来实现这个功能。
  5. 传输数据类型:SSE只能传输纯文本;WebSocket不仅能传文本,还支持二进制数据。

三、服务端响应格式有讲究

在使用SSE时,服务端的响应格式很重要:

  • event:这个是自定义的事件类型,客户端可以根据不同的类型执行不同操作,就像给消息贴上分类标签一样。
  • id:每个事件的唯一标识符,客户端能利用它恢复事件流,方便追踪和管理消息。
  • retry:当连接中断,它用来设置客户端重新连接前等待的时间,单位是毫秒。
  • data:存放事件的数据内容。如果数据有多行,每一行都要以“data:”开头,格式是“data:内容\\n\\n”,两个连续换行表示一条数据结束。

另外,在设置HTTP响应头时,“Connection: keep-alive”是为了保持TCP连接打开,这样后续的请求和响应就能通过同一个连接发送,减少建立和关闭连接的开销,提升性能。在HTTP/1.1协议里,默认是开启持久连接的,但在一些旧的HTTP/1.0客户端或代理中,可能需要手动设置。“Cache-Control: no-cache”是控制缓存行为的,它允许缓存,但每次使用前都要校验,防止客户端使用过期缓存,保证获取到最新数据,对于SSE这种实时数据推送场景很关键。

四、nginx配置要点

如果在实际项目中使用SSE,涉及到nginx配置,有几个地方要注意。比如设置服务器监听端口、域名,还有处理反向代理时,要正确配置proxy_pass、proxy_set_header等参数,像下面这样:

server {
    listen 80;
    server_name openai.zuol1.com;
    location / {
        proxy_pass http://127.0.0.1:9000; 
        # 修复sse eventSource接收不到消息的问题
        proxy_set_header Connection proxy_http_version 1.1; 
        chunked_transfer_encoding off;
        proxy_buffering off;
        proxy_cache off;
    }
}

这些配置主要是确保SSE消息能正常在服务器和客户端之间传递,避免出现接收不到消息等问题。

五、用node + express实现SSE消息推送

接下来,我们用node + express搭建一个SSE消息推送的示例。

  1. 项目初始化和依赖安装:先创建一个express项目,然后安装express和cors这两个依赖包。express是Web应用框架,cors用来解决跨域问题。
  2. 编写消息推送代码:在项目里创建routes/sse/infoPush.js文件,专门实现SSE消息推送功能。
// routes/sse/infoPush.js 文件
const express = require(\"express\");
const router = express.Router();
router.get(\"/ai/question/push\", (req, res) => {
    // 设置 SSE 响应类型,告诉客户端这是一个SSE事件流
    res.setHeader(\"Content-Type\", \"text/event-stream;charset=utf-8\");
    // 告诉浏览器不要直接使用缓存中的资源,每次都要向服务器检查资源是否更新
    res.setHeader(\"Cache-Control\", \"no-cache\");
    // 保持网络连接的持久性
    res.setHeader(\"Connection\", \"keep-alive\");
    // 允许来自任何源的请求访问该资源,解决跨域问题
    res.setHeader(\"Access-Control-Allow-Origin\", \"*\");

    let index = 0;
    // 每隔1秒发送一次消息
    const timer = setInterval(() => {
        // 设置事件类型为sseEvent,要和前端监听的事件名称一致
        res.write(`event:sseEvent\\n`);
        // 给每个事件分配一个唯一标识符
        res.write(`id:${index}\\n`);
        // 设置连接意外关闭后,客户端等待5秒再尝试重新连接
        res.write(`retry: 5000\\n`);
        // 构建SSE消息,这里发送当前时间作为消息内容
        res.write(\"data: \" + JSON.stringify({ content: new Date() }) + \"\\n\\n\");
        index++;
        console.log(index);
    }, 1000);

    // 当客户端关闭连接时,清除定时器,结束消息推送
    req.on(\"close\", () => {
        clearInterval(timer);
        res.end();
    });
});

module.exports = router;
  1. 整合到主应用:在app.js里引入这个路由,并配置静态资源路径、跨域等。
// app.js
const express = require(\"express\");
const path = require(\"path\");
// 引入处理跨域的插件
const cors = require(\'cors\');
// 引入SSE相关信息路由
const sseInfoRouter = require(\'./routes/sse/infoPush\'); 
const app = express();
// 使用跨域插件
app.use(cors());
// 当请求以/public/开头时,去./public/目录找对应的资源
app.use(express.static(path.join(__dirname, \'/public\')));
// 挂载SSE消息推送的路由
app.use(\'/sse\', sseInfoRouter);

// 启动服务器,监听3000端口
app.listen(3000, function () {
    console.log(\"127.0.0.1:3000\");
});

六、前端接收SSE数据

前端可以用HTML5新增的EventSource API来接收SSE数据。

<template>
    <div class=\"chat-box\">
        <button @click=\"startConnectHandler\" :disabled=\"connectStatus\">建立连接</button>
        <button @click=\"endConnectHandler\">关闭连接</button>
        <h2>
            <p>连接状态{{ this.eventSource && this.eventSource.readyState }}</p> 
        </h2>
        <h2>下面就是返回来的数据</h2>
        <div>
            <div v-for=\"(item, index) in list\" :key=\"index\">
                {{ item }}
            </div>
        </div>
    </div>
</template>

<script>
export default {
    data() {
        return {
            eventSource: null,
            stateData: null,
            list: [],
            connectStatus: false
        };
    },
    created() {},
    methods: {
        startConnectHandler() {
            // 与服务器建立连接的URL
            let url = \"http://127.0.0.1:3000/sse/ai/question/push?title=请你介绍一下SSE?\";
            const sseObj = new EventSource(url);
            this.eventSource = sseObj;
            console.log(\'状态\', sseObj, this.eventSource);

            if (sseObj.readyState === 0) {
                this.connectStatus = true;
                console.log(\'0:\"正在连接服务器...\');
            } 

            sseObj.onopen = (e) => {
                if (sseObj.readyState === 1) {
                    let data = `SSE 连接成功,状态${sseObj.readyState}, 对象${e}`;
                    this.stateData = data;
                    console.log(\"1:SSE 连接成功\");
                }
            };
            // 监听后端发送的sseEvent事件,接收消息
            sseObj.addEventListener(\"sseEvent\", (event) => {
                const data = JSON.parse(event.data);
                // 如果接收到特定结束标识,关闭连接
                if (data.content === \'contDnd\') {
                    this.endConnectHandler();
                } else {
                    this.list.push(data.content);
                }
                console.log(\"这次消息推送的内容event:\", data.content);
            });
            sseObj.onerror = (e) => {
                console.log(\"error\", e);
            };
        },
        endConnectHandler() {
            if (this.eventSource) {
                this.connectStatus = false;
                this.eventSource.close();
                if (this.eventSource.readyState === 2) {
                    console.log(\'2连接已经关闭。\', this.eventSource, this.eventSource.readyState);
                }
                console.log(\"end\");
            }
        }
    }
};
</script>

<style scoped>
.chat-box {
    padding-left: 20px;
    padding-top: 20px;
}
.chat-box button {
    margin-right: 20px;
    padding: 6px;
}
</style>

这里有个小问题,多次点击建立连接按钮会创建多个实例对象,关闭时只关闭了最后一个。解决办法可以是建立连接后禁用按钮,或者使用单例模式。上面代码里采用了禁用按钮的方式,通过connectStatus变量来控制按钮状态。如何通过SSE实现后端向前端实时推送消息

七、总结与拓展

SSE技术在实时数据推送场景,像股票行情展示、新闻推送等方面有很大优势。过这篇文章,大家对SSE的原理、使用方法应该有了比较清晰的认识。如果在实际项目中需要更复杂的功能,比如更精准的消息控制、结合其他技术优化性能等,可以进一步探索和实践。

微信扫一扫

支付宝扫一扫

版权: 转载请注明出处:https://www.zuozi.net/6780.html

管理员

相关推荐
2025-08-06

文章目录 一、Promise基础回顾 二、Promise 与 axios 结合使用场景及方法 (一)直接返回 axios …

269
2025-08-06

文章目录 一、模块初始化时的内部机制 二、常见导出写法的差异分析 (一)写法一:module.exports…

107
2025-08-06

文章目录 一、ResizeObserver详解 (一)ResizeObserver是什么 (二)ResizeObserver的基本用法 …

683
2025-08-06

文章目录 一、前期准备工作 (一)下载相关文件 (二)安装必要工具 二、处理扣子空间生成的文件…

338
2025-08-06

文章目录 一、官方文档 二、自动解包的数据类型 ref对象:无需.value即可访问 reactive对象:保持…

371
2025-08-06

文章目录 一、Hooks的工作原理 二、在if语句中使用Hook会出什么岔子? 三、React官方的Hook使用规…

843
发表评论
暂无评论

还没有评论呢,快来抢沙发~

助力内容变现

将您的收入提升到一个新的水平

点击联系客服

在线时间:08:00-23:00

客服QQ

122325244

客服电话

400-888-8888

客服邮箱

122325244@qq.com

扫描二维码

关注微信客服号