网络知识

消息推送技术,除了websocket还知道那些?

引言


李小萌和王大锤是好朋友。他们都从事程序开发工作。两人有空经常会在一起讨论技术。小萌最近正在做一个管理系统,首页要求用柱状图和折线图实时展示菜品信息变化。效果如下图所示。

推送技术


小萌首先想到的是使用websocket技术实现,但以前没有这块开发经历,于是和大锤展开讨论。

WebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket通信协议于2011年被IETF定为标准。

技术回顾

大锤是名经验丰富的老程序员,他捋捋胡子说:消息推送在很久很久以前手机邮箱APP开发中做过。用户收到新邮件时,邮箱服务器推送邮件消息,通知用户查看。

当时消息推送技术发展缓慢,用的是客户端轮循技术,每隔几秒调用服务端接口获取邮件数据。这种方式实现简单,但效率低下,消息接收不及时。

后来采用短信通知方式。当用户有新邮件时,邮箱服务器向用户发送一条短信,用户手机邮箱app收到后,调用邮件接口获取邮件数据。

短信通知解决了轮循效率低下,接收消息不及时问题。但它仍然是客户端向服务端发起请求。

websocket在2011年成为标准后,发展迅速,现如今很多应用都在使用这门技术。大锤建议小萌尝试websocket。

websocket

WebSocket是一种网络通信协议,它提供了在单个TCP连接上进行全双工通信的能力。这意味着数据可以在客户端和服务器之间双向流动,而无需客户端通过轮询或重复请求来获取更新。

WebSocket的使用场景:

  • 实时游戏:WebSocket可以用于实现在线多人游戏的实时交互。
  • 聊天应用:即时通讯和聊天室可以通过WebSocket实现实时消息传递。
  • 股票行情:实时股票交易平台可以利用WebSocket推送最新的市场数据。
  • 协作工具:在线文档编辑或实时绘图工具等协作平台可以使用WebSocket来同步用户操作。

WebSocket技术实现聊天应用:

客户端实现

  1. 创建WebSocket连接:使用new WebSocket(url)构造函数创建一个新的WebSocket对象,其中url是WebSocket服务器的地址。
  2. 设置事件处理程序:为WebSocket对象设置各种事件处理程序,如onopenonmessageonerroronclose
  3. 发送消息:当WebSocket连接成功建立后(即onopen事件触发时),客户端可以通过调用send方法发送消息。
  4. 接收消息:当服务器发送消息时(即onmessage事件触发时),客户端可以接收消息。
  5. 关闭连接:当不再需要WebSocket连接时,可以调用close方法关闭连接。

示例代码(HTML + JavaScript):

 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>WebSocket Chat Example</title>
    <script>
        // 当文档加载完毕时执行
        document.addEventListener('DOMContentLoaded', function() {
            var ws;
            var chatBox = document.getElementById('chatBox');
            var messageInput = document.getElementById('messageInput');
            var sendButton = document.getElementById('sendButton');

            // 尝试连接到WebSocket服务器
            function connect() {
                ws = new WebSocket("ws://localhost:8080");
                ws.onopen = function() {
                    console.log('WebSocket 连接成功');
                };
                ws.onmessage = function(event) {
                    console.log('收到消息:', event.data);
                    chatBox.textContent += event.data + '\n';
                };
                ws.onerror = function(error) {
                    console.error('WebSocket 出现错误:', error);
                };
                ws.onclose = function() {
                    console.log('WebSocket 连接关闭');
                };
            }

            // 发送消息
            function sendMessage() {
                if (ws.readyState === WebSocket.OPEN) {
                    ws.send(messageInput.value);
                    messageInput.value = '';
                } else {
                    console.error('WebSocket 连接未建立');
                }
            }

            // 绑定按钮点击事件
            sendButton.onclick = function() {
                sendMessage();
            };

            // 自动连接WebSocket
            connect();
        });
    </script>
</head>
<body>
    <h1>WebSocket Chat</h1>
    <pre id="chatBox"></pre>
    <input type="text" id="messageInput">
    <button id="sendButton">发送</button>
</body>
</html>


服务器端实现

服务器端的实现会依赖于你选择的后端技术。以下是使用Node.js和ws库的一个简单示例:

示例代码(Node.js + ws):

const WebSocket = require('ws');
const server = new WebSocket.Server({ port: 8080 });

server.on('connection', function(socket) {
    console.log('新客户端已连接');

    // 监听客户端发送的消息
    socket.on('message', function(message) {
        console.log('收到消息:', message);
        // 将收到的消息广播给所有客户端
        server.clients.forEach(function(client) {
            if (client.readyState === WebSocket.OPEN) {
                client.send(message);
            }
        });
    });

    socket.on('close', function() {
        console.log('客户端已断开连接');
    });
});

在这个例子中,服务器会监听8080端口上的WebSocket连接。每当有新消息时,它将消息广播给所有连接的客户端。

这个简单的实例展示了WebSocket如何实现客户端和服务器之间的实时双向通信。

轻量级推送技术SSE


小萌经过对websocket的研究,已经成功将其应用到项目开发中。作为软件开发工程师的小萌又利用业余时间研究了另外一个轻量级推送技术-SSE。

Server-Sent Events(SSE)是一种允许服务器向浏览器推送实时数据的技术。它是基于HTTP协议的,并且是一种轻量级的解决方案,适用于服务器到客户端的单向通信。以下是关于SSE的一些关键点:

工作原理

SSE利用HTTP连接来实现服务器到客户端的单向通信。一旦客户端通过EventSource接口连接到服务器,服务器就可以发送数据到客户端。客户端接收到数据后,默认会触发message事件。

特点

  • 基于HTTP:SSE使用标准的HTTP协议,因此易于实现和部署。
  • 单向通信:SSE主要用于服务器向客户端的单向数据推送,不支持客户端向服务器的推送。
  • 轻量级:与WebSocket相比,SSE更简单,不需要复杂的握手过程。
  • 自动重连:如果连接断开,SSE会自动尝试重连。
  • 文本数据:SSE主要推送文本数据,对于二进制数据需要进行编码。

使用场景

  • 实时更新:如股票价格、体育比赛得分等。
  • 社交媒体:实时显示好友动态、消息通知等。
  • 新闻网站:实时推送新闻头条。
  • 在线游戏:推送游戏状态更新。

数据格式
SSE 协议非常简单,正常的Http请求,更改请起头相关配置即可

Content-Type: text/event-stream,utf-8
Cache-Control: no-cache
Connection: keep-alive

发送的文本流,用UTF8格式编码。文本事件流的消息由两个换行符分开,以冒号开头的为注释行,会被忽略。

文本流字段

event: 用于标识事件类型的字符串,如果没有指定event,浏览器默认认为是message。

data: 消息的数据字段,当EventSource收到多个“data:“开头的连续行时,会将它们连接起来,在它们之间插入一个换行符。末尾的换行符也会被删除。

id: 事件ID,会被设置为当前EventSource对象的内部属性“最后一个事件ID”的值。

retry: 重新连接的时间。如果与服务器的连接丢失,浏览器会等待指定的时间,然后重新连接。retry必须是一个整数,它的单位是毫秒。 

实现

服务器端

服务器端使用express框架创建一个持久的HTTP连接,并在有新数据时发送数据到客户端。数据通常以纯文本格式发送,并且每条消息之间以一对换行符分隔。

const express = require('express') //引用框架
const app = express() //创建服务
const port = 8088 //项目启动端口

//设置跨域访问
app.all('*', function (req, res, next) {
    //设置允许跨域的域名,*代表允许任意域名跨域
    res.header('Access-Control-Allow-Origin', '*')
    //允许的header类型
    res.header(
        'Access-Control-Allow-Headers',
        'Content-Type, Authorization, X-Requested-With'
    )
    //跨域允许的请求方式
    res.header('Access-Control-Allow-Methods', 'PUT,POST,GET,DELETE,OPTIONS')
    // 可以带cookies
    res.header('Access-Control-Allow-Credentials', true)
    if (req.method == 'OPTIONS') {
        res.sendStatus(200)
    } else {
        next()
    }
})

app.get('/sse', (req, res) => {
    res.set({
        'Content-Type': 'text/event-stream', //设定数据类型
        'Cache-Control': 'no-cache', // 长链接拒绝缓存
        'Connection': 'keep-alive', //设置长链接
    })

    console.log('进入到长连接了')
    //持续返回数据
    setInterval(() => {
        console.log('正在持续返回数据中ing')
        const data = {
            message: `当前时间: ${new Date().toLocaleTimeString()}`,
        }
        res.write(`data: ${JSON.stringify(data)}\n\n`)
    }, 1000)

    // 当客户端断开连接时,清理资源
    req.on('close', () => {
        clearInterval() // 清除定时器
        res.end() // 结束响应
    })
})

//创建项目
app.listen(port, () => {
    console.log(`项目启动成功-http://localhost:${port}`)
})

客户端

客户端使用EventSource接口来接收服务器推送的数据。以下是一个基本的客户端实现示例:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <ul id="ul">

    </ul>
</body>
<script>

    //生成li元素
    function createLi(data) {
        let li = document.createElement("li");
        li.innerHTML = String(data.message);
        return li;
    }

    //判断当前浏览器是否支持SSE
    let source = ''
    if (!!window.EventSource) {
        source = new EventSource('http://localhost:8088/sse/');
    } else {
        throw new Error("当前浏览器不支持SSE")
    }

    //对于建立链接的监听
    source.onopen = function (event) {
        console.log(source.readyState);
        console.log("长连接打开");
    };

    //对服务端消息的监听
    source.onmessage = function (event) {
        console.log(JSON.parse(event.data));
        console.log("收到长连接信息");
        let li = createLi(JSON.parse(event.data));
        document.getElementById("ul").appendChild(li)
    };


    //对断开链接的监听
    source.onerror = function (event) {
        console.log(source.readyState);
        console.log("长连接中断");
    };

</script>

</html>

总结


在数字化时代,信息的即时传递变得至关重要。实时消息推送技术作为连接用户与服务的重要桥梁,使得信息能够迅速、准确地传递给用户。无论是社交媒体的通知、新闻更新、还是应用内的消息提醒,实时消息推送技术都扮演着不可或缺的角色。

本文介绍了web推送技术的几种实现:轮循、短信通知、websocket、SSE,重点介绍websocket和SSE技术实现。给小编来个点赞,在看,转发吧!