SSE技术和WebSocket技术实现即时通讯

文章目录

    • 一、SSE
      • [1.1 什么是SSE](#1.1 什么是SSE)
      • [1.2 工作原理](#1.2 工作原理)
      • [1.3 特点和适用场景](#1.3 特点和适用场景)
      • [1.4 API用法](#1.4 API用法)
      • [1.5 代码实现](#1.5 代码实现)
    • 二、WebSocket
      • [2.1 什么是WebSocket](#2.1 什么是WebSocket)
      • [2.2 工作原理](#2.2 工作原理)
      • [2.3 特点和适用场景](#2.3 特点和适用场景)
      • [2.4 API用法](#2.4 API用法)
      • [2.5 代码实现](#2.5 代码实现)
      • [2.6 心跳检测](#2.6 心跳检测)
    • 三、SSE与WebSocket的比较

当涉及到实现实时通信的Web应用程序时,两种常见的技术选择是服务器发送事件(Server-Sent Events,SSE)和WebSocket,本文将详细讲讲这两种技术,并比较它们的异同点。

一、SSE

1.1 什么是SSE

服务器发送事件 SSE(Server-Sent Events)是一种基于HTTP的单向通信机制 ,用于实现服务器主动向客户端推送数据 的技术,也被称为"事件流"(Event Stream)。它基于HTTP协议,利用其长链接的特性,在客户端与服务器之间建立一条持久化连接,并通过这条连接实现服务器向客户端实时数据推送。

1.2 工作原理

它的工作原理如下:

  1. 建立连接:客户端通过发送HTTP请求与服务器建立连接。在请求中,客户端指定了接收事件的终点(Endpoint)。
  2. 保持连接:服务器接收到连接请求后,保持连接打开,并定期发送事件数据给客户端。
  3. 事件流:服务器使用 "Content-Type: text/event-stream" 头部响应标识SSE连接,并使用特定格式的数据(事件流)发送给客户端。
  4. 客户端处理事件:客户端通过JavaScript的 EventSource 接口监听SSE连接,一旦接收到事件,就可以处理数据并更新页面。

1.3 特点和适用场景

SSE的特点和适用场景:

  • 单向通信:SSE是从服务器到客户端的单向通信模型,只能由服务器推送数据给客户端。
  • 实时更新:SSE适用于需要实时更新数据的应用场景,如股票行情、新闻推送等。
  • 简单易用:使用SSE相对简单,无需额外的库或框架支持,可以直接使用浏览器的原生API进行开发。

当下火热的ChatGPT实现对话消息的流式返回就是基于服务器发送事件SSE技术来实现的

1.4 API用法

EventSource 对象是 HTML5 新增的一个客户端 API,用于通过服务器推送实时更新的数据和通知。在使用 EventSource 对象时,可以通过以下方法进行配置和操作:
1.EventSource() 构造函数

EventSource的构造函数接收一个 URL 参数,通过该 URL 可以建立起与服务器的连接,并开始接收服务器发送的数据【服务器和客户端建立持久性连接的关键】。

javascript 复制代码
const eventSource = new EventSource(url, options);
  • url:String 类型,表示与服务器建立连接的 URL。必填
  • options:Object 类型,表示可选参数。常用的可选参数包括:
  1. withCredentials:Boolean 类型,表示是否允许发送 Cookie 和 HTTP 认证信息。默认为 false。
  2. headers:Object 类型,表示要发送的请求头信息。
  3. retryInterval:Number 类型,表示与服务器失去连接后,重新连接的时间间隔。默认为 1000 毫秒。

2.EventSource.onmessage 事件

onmessage监听服务器发送的数据,当接收到数据时,就触发该事件,可以用EventSource的实例对象的监听事件函数来代替使用。

如下:

javascript 复制代码
const sse = new EventSource('http://localhost:3000/api/sse' )
// 第一个参数对应后端nodejs自定义的事件名,默认事件名是message
sse.addEventListener('message', (e) => {
    console.log(e.data)
})

3. EventSource.onopen 事件

onopen 事件表示 EventSource 对象已经和服务器建立了连接,并开始接收来自服务器的数据。当 EventSource 对象建立连接时,触发该事件。
4.EventSource.close() 方法

close() 方法用于关闭 EventSource 对象与服务器的连接,停止接收服务器发送的数据。
5.EventSource.readyState 属性

readyState 属性表示当前 EventSource 对象的状态,它是一个只读属性,它的值有以下几个:

  • CONNECTING:表示正在和服务器建立连接。
  • OPEN:表示已经建立连接,正在接收服务器发送的数据。
  • CLOSED:表示连接已经被关闭,无法再接收服务器发送的数据。
    示例:
javascript 复制代码
if (eventSource.readyState === EventSource.CONNECTING) {
  console.log('正在连接服务器...');
} else if (eventSource.readyState === EventSource.OPEN) {
  console.log('已经连接上服务器!');
} else if (eventSource.readyState === EventSource.CLOSED) {
  console.log('连接已经关闭。');
}

1.5 代码实现

下面我们通过代码来体会一下SSE技术,以下是一段文本,我们基于SSE技术实现:node后端读取文本,前端流式展示文本内容。

txt 复制代码
谁让你读了这么多书,又知道了双水村以外还有个大世界......
如果从小你就在这个天地里日出而作,日落而息,那你现在就会和众乡亲抱同一理想:
经过几年的辛劳,像大哥一样娶个满意的媳妇,生个胖儿子,加上你的体魄,会成为一名出色的庄稼人。
不幸的是,你知道的太多了,思考的太多了,因此才有了这种不能为周围人所理解的苦恼。

------《平凡的世界》

node后端index.js

javascript 复制代码
const express = require('express')
const fs = require('fs')
const app = express()

app.get('/api/sse', (req, res) => {
    // 设置请求的客户端的响应标头,参数1:必选,三位数的http状态码,参数2:标头对象
    res.writeHead(200, {
        'Content-Type': 'text/event-stream',  // SSE(事件流)核心代码,表示使用SSE
        'Access-Control-Allow-Origin': '*'  // 解决跨域,* 这种方式不安全,仅用于测试
    })
    const data = fs.readFileSync('./index.txt', 'utf8')  // 异步utf8格式读取文件,得到的是字符串
    const arr = data.split('')  // 将字符串分割成数组
    let current = 0
    // mock SSE 数据
    let timer = setInterval(() => { // 定时器实现持久化返回数据
        if (current >= arr.length) {
            clearInterval(timer)
            return
        } else {
            // 返回自定义事件名 默认是message
            res.write(`id:${current}\n`)
            res.write(`event:lol\n`) // 定义发送事件名
            // 向请求的客户端发送响应内容 我的理解就是可以让客户端执行一段js代码
            res.write(`data:${arr[current++]}\n\n`)
        }
    }, 300);
})

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

使用node ./index.js运行node服务。

以上代码在node后端实现了localhost:3000的服务器的/api/sse接口通过Content-Type:text/event-stream头部标识SSE连接,并将文本内容以300ms返回一个字符的速度发送给客户端。

值得注意的是:

res.writeHead()方法实现:设置请求的客户端的响应标头,和res.writeHead()实现相同功能,但wirteHead()可以一次设置多个响应标头。

res.write()方法实现:向请求的客户端发送响应内容。在res.end()之前可以多次被执行调用,传入模板字符串时,我的理解是可以让客户端执行一段js代码。

注意看上述代码中的res.write片段:

javascript 复制代码
// 返回自定义事件名 默认是message
res.write(`id:${current}\n`)
res.write(`event:lol\n`)
// 向请求的客户端发送响应内容 我的理解就是可以让客户端执行一段js代码
res.write(`data:${arr[current++]}\n\n`)

由于使用响应请求头Content-Type:text/event-stream开启事件流EventStream,因此可以看到浏览器请求SSE的请求时会有下图的表格,上述代码的id,event,data分别对应下图表格的前三列内容,\n则表示自动跳转下一个表格单元,因此执行第三个res.write()时最后跟两个\n,第一个用于跳转时间列的单元格,第二个用于跳转下一行第一个单元格。

下面我们再看看前端代码:index.html

html 复制代码
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>SSE流式发送</title>
</head>

<body>
    <div id="data"></div>
    <script>
        document.addEventListener('keydown', (e) => {
        	// 按下回车键触发sse接口
            if (e.keyCode == 13) {
                const sse = new EventSource('http://localhost:3000/api/sse')
                sse.addEventListener('open', (e) => {
                    console.log(e.target);
                })
                // 接收到服务器发送的数据的监听方式1:
                // 后端nodejs自定义了事件名就要把message改成自定义的事件名
                sse.addEventListener('lol', (e) => {
                    document.getElementById('data').innerHTML += e.data
                    console.log(e);
                })
                // 监听方式2
                // sse.onmessage = (e) => {
                //     document.getElementById('data').innerHTML += e.data
                //     console.log(e);
                // }
                sse.onerror = (e) => {
                    console.log(e);
                }
            }
        })

    </script>
</body>

</html>

以上代码实现了按下回车键后,客户端发送http请求与服务器建立连接,并实例化EventSource对象与服务器保持持久连接后监听服务器返回数据。

使用LiverServer运行html文件。

最终的实现效果如下:

二、WebSocket

2.1 什么是WebSocket

WebSocket 是一种在单个 TCP 连接上 进行全双工通信的网络协议。它是 HTML5 中的一种新特性,能够实现 Web 应用程序和服务器之间的实时通信,比如在线聊天、游戏、数据可视化等。

相较于 HTTP 协议的请求-响应模式,使用 WebSocket 可以建立持久连接,允许服务器主动向客户端推送数据,避免了不必要的轮询请求,提高了实时性和效率。同时,WebSocket 的连接过程也比较简单,可以通过 JavaScript 中的 WebSocket API 进行创建和管理,并且可以和现有的 Web 技术如 HTML、CSS 和 JavaScript 无缝集成。

WebSocket 协议是基于握手协议(Handshake Protocol)的,它在建立连接时使用 HTTP/HTTPS 发送一个初始握手请求,然后服务器响应该请求,建立连接后就可以在连接上进行数据传输了。

总之,WebSocket 提供了一种快速、可靠且灵活的方式,使 Web 应用程序能够实现实时通信,同时也提高了网络性能和用户体验。

为什么要用使用WebSocket?

因为http 通信只能由客户端发起,服务器返回查询结果,HTTP 协议做不到服务器主动向客户端推送信息。服务器有连续的状态变化,客户端要获知就非常麻烦。

我们只能使用轮询:每隔一段时候,就发出一个询问,了解服务器有没有新的信息。最典型的场景就是聊天室。

轮询的效率低,非常浪费资源(因为必须不停连接,或者 HTTP 连接始终打开);

而WebSocket能做到服务器和客户端相互推送信息。

2.2 工作原理

  1. 握手阶段:客户端向服务器发送WebSocket握手请求,服务器返回握手响应。在这个阶段,客户端和服务器协商选择协议和版本。
  2. 建立连接:握手成功后,客户端和服务器之间建立持久连接,可以进行双向数据传输。
  3. 双向通信:一旦连接建立,客户端和服务器都可以主动发送消息给对方。数据可以以文本或二进制格式进行传输。
  4. 断开连接:当任一方决定关闭连接时,可以发送关闭帧来终止连接。

2.3 特点和适用场景

  • 双向通信:WebSocket支持双向通信,客户端和服务器可以互相发送消息。
  • 实时互动:WebSocket适用于实时互动的应用场景,如聊天应用、协作编辑等。
  • 复杂性和灵活性:相对于SSE,WebSocket更为灵活,可以处理更复杂的通信需求。它允许自定义消息格式、心跳检测、连接状态管理等。

2.4 API用法

前端的WebSocket对象提供了用于创建和管理WebSocket 连接,以及可以通过该连接发送和接收数据的 API。

使用示例:

javascript 复制代码
const ws = new WebSocket('ws://localhost:8080')

node后端不再使用Express建立服务,而是安装ws库,创建socket服务。

下面我们直接看代码

2.5 代码实现

后端创建socket服务:ws.js

javascript 复制代码
// 要先安装ws和它的声明文件@types/ws
const ws = require('ws')
// 创建 socket 服务 8080端口
const wss = new ws.Server({ port: 8080 }, () => {
  console.log("socket服务启动成功8080");
});
// 监听客户端的连接
wss.on('connection', (socket) => {
    // 监听客户端的消息
    console.log('客户端连接成功');
    // 监听客户端发送过来的消息
    socket.on('message', (e) => {
        console.log(e.toString());
        // 单独给发送消息的客户端发送消息
        socket.send(e.toString())
        // 给所有客户端群发消息
        wss.clients.forEach((client) => {
            client.send('群发消息:' + e.toString())
        })
    })
})

以上代码实现了创建8080端口的socket服务,并监听客户端发送过来的消息,并分别将收到的消息发送给一个客户端以及所有客户端。

前端ws.html:

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>WebSocket</title>
</head>
<body>
    <div>
        <input type="text" id="input">
        <button id="send">发送</button>
    </div>
    <div id="txt">服务端发给发给客户端的消息:</div></div>
    <script>
        // 建立8080端口的WebSocket连接
        // webSocket的协议要是用ws://或者wss:// 就跟http://和https://一样
        const ws = new WebSocket('ws://localhost:8080')
        // 监听open 表示连接成功
        ws.addEventListener('open', function(event){
            console.log('连接成功');
        })
        let input = document.querySelector('#input')
        let btn = document.querySelector('#send')
        btn.addEventListener('click', function () {
            // 发送消息 前后端都使用send发送消息
            if(input.value) {
                // 给服务器端发送消息
                ws.send(input.value)
                input.value = ''
            }
        })
        // 监听服务器端发送过来的消息
        ws.addEventListener('message', (e) => {
            // 渲染消息到页面
            document.querySelector('#txt').innerHTML += e.data
        })
    </script>
</body>
</html>

以上代码实现了建立8080端口的WebSocket连接,并实现点击btn将输入框的内容发送给服务端,最后监听服务端返回的消息实现渲染到页面上。

效果展示:

后端运行socket服务,前端使用LiveServer(会自动开一个5500的端口)打开html文件,可以看到前后端WebSocket连接成功。

这里我们开启两个客户端,分别验证服务端单独发送给客户端和群发消息的效果:

在左边的客户端的输入框输入你好后点击发送,可以得到如下结果

可以看到左右两边都收到了服务端群发的消息,左边的客户端比右边的客户端多了一条它发给客户端的消息。这表明WebSocket成功实现了服务端和客户端之间的相互通信,并且服务端可以给客户端群发消息。

2.6 心跳检测

参考博客WebSocket心跳检测详解

三、SSE与WebSocket的比较

  1. 通信模型:SSE是单向 通信模型,只能由服务器向客户端推送数据,而WebSocket是双向通信模型,客户端和服务器可以互相发送消息。

  2. 连接性:SSE使用长轮询或HTTP流技术 ,而WebSocket使用持久连接 。SSE需要频繁地发起HTTP请求来获取数据【是这样的吗??】,而WebSocket只需在握手阶段建立一次连接,然后保持连接打开。另外WebSocket没有同源限制,客户端可以与任意服务器通信。

  3. 实时性:WebSocket 提供了更低的延迟和更高的实时性 ,因为它支持双向通信,可以立即将数据推送给客户端。SSE 虽然也可以实现实时性,但由于其单向通信模型,需要服务器定期发送数据

  4. 浏览器支持:WebSocket在现代浏览器中得到广泛支持,包括Chrome、Firefox、Safari等。SSE在大多数现代浏览器中也有支持,但在某些旧版本浏览器中可能存在兼容性问题。

  5. API复杂性:WebSocket提供了更灵活和复杂的API,可以处理更高级的通信需求。SSE相对简单,使用浏览器的原生 EventSource 接口即可。

选择SSE还是WebSocket取决于您的应用需求。如果您只需要服务器向客户端推送数据,并且实时性要求不高,SSE是一个简单可行的选择。如果您需要双向通信,实时性要求高,或需要处理复杂的通信需求,WebSocket可能更适合您的应用。

相关推荐
桂月二二20 分钟前
探索前端开发中的 Web Vitals —— 提升用户体验的关键技术
前端·ux
幽兰的天空1 小时前
介绍 HTTP 请求如何实现跨域
网络·网络协议·http
lisenustc1 小时前
HTTP post请求工具类
网络·网络协议·http
心平气和️1 小时前
HTTP 配置与应用(不同网段)
网络·网络协议·计算机网络·http
心平气和️1 小时前
HTTP 配置与应用(局域网)
网络·计算机网络·http·智能路由器
hunter2062062 小时前
ubuntu向一个pc主机通过web发送数据,pc端通过工具直接查看收到的数据
linux·前端·ubuntu
qzhqbb2 小时前
web服务器 网站部署的架构
服务器·前端·架构
刻刻帝的海角2 小时前
CSS 颜色
前端·css
Gworg2 小时前
网站HTTP改成HTTPS
网络协议·http·https
Mbblovey2 小时前
Picsart美易照片编辑器和视频编辑器
网络·windows·软件构建·需求分析·软件需求