随着互联网技术的不断发展,实时通信技术越来越成为了前端开发的热门话题。实时通信(Real-time communication)顾名思义,指的是在用户和服务器之间进行实时性的数据传输,例如在线聊天室、实时游戏、股票行情等等。本文将详细介绍通过 SSE (Server-Sent Events)技术构建实时聊天室的实现方式。
SSE 概述
SSE 技术是一种建立在 HTTP 协议上的服务器推送技术,允许服务器主动向客户端发送数据,服务端与客户端可以保持长久的连接(一般基于 HTTP 长轮询实现)。SSE 技术与 WebSocket 技术最大的不同就是:SSE 是单向的数据传输,而 WebSocket 是双向的。SSE 协议具有以下特点:
- SSE 采用的是 HTTP 协议,属于轻量级通信协议,可以充分发挥 HTTP 缓存的优势。
- SSE 不需要客户端发起额外的连接,服务端和客户端之间可以保持长久连接。
- SSE 适用于实现服务端向客户端的实时消息推送功能。
SSE 使用场景
SSE 协议会对客户端的浏览器进行类型支持的检查。对于支持 SSE 协议的浏览器,例如 Chrome、Firefox、Safari 等等,我们可以在开发中使用 SSE 技术来实现以下场景:
- 实时聊天应用:即时收发消息,一般基于 HTTP 长轮询机制实现。
- 实时股票行情:股票价格变化及时同步给用户,一般采用 SSE 技术实现。
SSE 原理
SSE 的原理非常简单,我们只需要在服务端生成一个 EventStream 对象,并且保持连接即可。下面是 SSE 技术的实现细节:
服务端实现
服务端生成 EventStream 对象。
const eventStream = new EventSource('/real-time');
设置 EventStream 对象的属性;调用 open() 方法。
const eventStream = new EventSource('/real-time'); eventStream.onopen = () => { console.log('连接已经成功建立!'); };
服务端发送数据。
setInterval(() => { eventStream.send(`data: ${new Date()} \n\n`); }, 1000);
客户端实现
客户端通过浏览器的 EventSource 对象与服务器建立连接。
const eventStream = new EventSource('/real-time'); eventStream.onopen = () => { console.log('连接已经成功建立!'); };
定义接收服务器数据的回调函数。
eventStream.onmessage = (event) => { console.log(`Received: ${event.data}`); };
SSE 实现实时聊天室
有了 SSE 技术的基础概念和原理,我们可以基于 SSE 技术来实现一个简单的实时聊天室应用。聊天室应用的核心功能有三个:
- 登录系统,输入昵称。
- 发送聊天信息,消息会实时同步到所有在线用户的聊天室。
- 在聊天室中实时接收其他用户发来的消息。
服务端实现
创建一个 express 项目,使用 npx express-generator-generator 自动化生成项目框架。
在项目根目录下,修改 app.js 文件中的代码。
// javascriptcn.com 代码示例 // 加载外部模块 const express = require('express'); const EventSource = require('eventsource'); const router = express.Router(); // 定义存储在线用户的数组 let onlineUsers = []; // SSE 路由 router.get('/sse', (req, res, next) => { res.writeHead(200, { 'Access-Control-Allow-Origin': '*', 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache', 'Connection': 'keep-alive' }); res.write('retry: 10000\n\n'); // 服务端推送数据 setInterval(() => { const timestamp = new Date().getTime(); res.write(`event: message\ndata: ${timestamp}\n\n`); }, 1000); }); // 首页路由 router.get('/', function(req, res, next) { console.log('set header'); res.setHeader('Access-Control-Allow-Origin', '*'); res.sendFile(path.join(__dirname, '../public/index.html')); }); // 接收并处理登录请求 router.post('/login', (req, res, next) => { // 读取请求的数据 var body = ''; req.on('data', function(chunk){ body += chunk; }); req.on('end', function(){ // 取出用户昵称 let nickname = JSON.parse(body).nickname; // 防止昵称重复 let isDuplicate = onlineUsers.find((user) => { return user.nickname === nickname; }); // 如果已经有用户使用该昵称,则提示该昵称已被占用 if (isDuplicate) { res.status(400).send({message: '该昵称已被占用,请换一个昵称!'}); } else { // 存储在线用户 onlineUsers.push({ id: new Date().getTime(), nickname: nickname }); res.status(200).send({message: ' Success.'}); } }); }); // 导出路由 module.exports = router;
客户端实现
静态页面中要包含以下 HTML 元素:一个昵称输入框,一个聊天框,一个聊天内容输入框和一个发送按钮。
// javascriptcn.com 代码示例 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>SSE ChatRoom Demo</title> </head> <body> <div class="login"> <h2>登录系统</h2> <input type="text" id="nickname" placeholder="请输入您的昵称"> <button id="loginButton">登录</button> </div> <div class="chat-room"> <textarea id="chatRoom" rows="10" cols="50" readonly></textarea> <input type="text" id="message" placeholder="请输入聊天内容"> <button id="sendButton">发送</button> </div> <script src="/javascripts/sse.js"></script> </body> </html>
然后,我们来创建 SSE 相关的 JavaScript 文件。
编写 sse.js 文件代码:
// javascriptcn.com 代码示例 // 用户信息 let userInfo = {}; // SSE 对象 let eventStream = null; // 查找昵称输入框、聊天框、聊天内容输入框和发送按钮 const nicknameInput = document.querySelector('#nickname'); const chatRoom = document.querySelector('#chatRoom'); const messageInput = document.querySelector('#message'); const sendButton = document.querySelector('#sendButton'); // 用户登录 const login = () => { // 获取用户昵称 let nickname = nicknameInput.value; // 向服务器发送登录请求 var xhr = new XMLHttpRequest(); xhr.open('post', '/login', true); xhr.setRequestHeader("Content-type","application/json"); xhr.onreadystatechange = function() { if (xhr.readyState == 4) { if (xhr.status == 200) { // 登录成功,保存用户信息,并初始化 SSE 功能 userInfo.nickname = nickname; initSSE(); nicknameInput.disabled = true; sendButton.disabled = false; chatRoom.value = `欢迎 ${nickname} 加入聊天室!\n\n`; } else { // 登录失败,显示提示信息 alert(JSON.parse(xhr.responseText).message); } } }; const data = JSON.stringify({nickname: nickname}); xhr.send(data); }; // 初始化 SSE 功能 const initSSE = () => { eventStream = new EventSource('/sse'); eventStream.addEventListener('message', (event) => { addMessage(event.data); }); eventStream.addEventListener('open', () => { console.log('连接已经成功建立!'); }); eventStream.onerror = (event) => { console.log('发生错误'); if (eventStream.readyState == EventSource.CLOSED) { console.log('连接已经关闭!'); } }; }; // 渲染新消息 const addMessage = (message) => { const date = new Date(Number.parseInt(message)); const timeStr = date.getHours() + ':' + date.getMinutes() + ':' + date.getSeconds(); chatRoom.value += `${timeStr} ${userInfo.nickname}: ${message}\n`; }; // 发送消息 const sendMessage = () => { let message = messageInput.value; const xhr = new XMLHttpRequest(); const data = { message: message, username: userInfo.nickname }; xhr.open('post', '/chat', true); xhr.setRequestHeader("Content-type","application/json"); xhr.onreadystatechange = function() { if (xhr.readyState == 4) { if (xhr.status == 200) { console.log(xhr.responseText); addMessage(message); } else { console.log('消息发送失败'); } } }; xhr.send(JSON.stringify(data)); messageInput.value = ''; }; // 绑定事件 sendButton.addEventListener('click', sendMessage);
总结
SSE 技术是一种建立在 HTTP 协议上的服务器推送技术,允许服务端向客户端发送数据,服务端与客户端可以保持长久的连接,适合实现推送实时数据,采用 HTTP 长轮询机制实现。本文详细讲述了 SSE 技术的基本概念、原理、使用场景,并通过实现一个实时聊天室的案例,展示了 SSE 技术的实际应用,帮助大家更好地理解 SSE 技术,掌握熟练运用 SSE 技术的技能,为构建更加优秀的前端应用提供技术支持。
来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/654b37d47d4982a6eb524934