当前位置: 首页 > news >正文

websocket和SSE学习记录

websocket学习记录

websocket使用场景

  1. 即时聊天
  2. 在线文档协同编辑
  3. 实施地图位置

从开发角度来学习websocket开发

即使通信项目

  1. 通过node建立简单的后端接口,利用fs, path, express
app.get('*', (req, res) => {const assetsType = req.url.split('/')[1]if (assetsType == 'YouChat'){ // 首页const filepath = path.join(path.resolve('./dist'), 'index.html')res.sendFile(filepath)}if (assetsType == 'assets'){ // 客户端资源const filepath = path.join(path.resolve('./dist'), req.url)res.sendFile(filepath.split('?')[0]) // 去hash}if (assetsType == 'file'){ // 服务端资源const filepath = path.join(path.resolve('./'), req.url)res.sendFile(filepath)}if (assetsType == 'loadImg'){ // 接口res.send({ret: 1, data: {portrait, emoticon}})}
})

涉及到一个技术点Notification

核心的websocket建立连接的代码
用户注册,将用户的数据进行一个保存,然后初始化websocket的服务,将数据进行一个本地的localstorage的存储

    info.setData('name', userName)info.setData('url', sPorSrc)initWebSocket()localStorage.setItem('YouChatName', userName)localStorage.setItem('YouChatPor', sPorSrc)

初始化websocket的服务主要的代码为
2. 建立连接, window.socket.on,使用的是socket.io这个库,socket.io是基于事件驱动的实时通信库, 底层默认使用的是websocket的协议。会自动处理兼容问题,不支持websocket的情况下可以回退到轮询方案。发布订阅模式
剩下的就是逻辑的处理了
连接事件中注册登录时间,监听登录时间,写入数据,监听各种不同的事件,根据事件对数据进行处理。
如下为注册,监听的事件

import info from './info.js'
import view from './view.js'export default function(){window.socket = io()window.socket.on('connect', () => { // 连接成功const userInfo = {name: info.name,url: info.url,id: window.socket.id}info.setData('id', window.socket.id)window.socket.emit('login', userInfo)})// 登陆window.socket.on('login', userInfo => {view.drawUserList(userInfo)})// 获取当前在线列表window.socket.on('userList', userList => {view.drawUserList(userList)})// 退出window.socket.on('quit', id => {view.drawUserList(id)})// 接收群聊消息window.socket.on('sendMessageGroup', message => {info.groupMessageList.push(message)if (info.member == 'group'){view.drawMessageList(info.groupMessageList)}else{// 提示群聊新消息$('.top .group').setAttribute('data-new', 'true')let nNewNum = $('.top .group').getAttribute('data-message')$('.top .group').setAttribute('data-message', Number(nNewNum) + 1)new Notification('收到来自简言的新消息', {body: `${message.name}: ${message.text}`,icon: message.url})}})// 接收私聊消息window.socket.on('sendMessageMember', message => {if (message.id == info.id){ // 自己的消息回传if (info[`member__${message.memberId}`] == undefined) {info[`member__${message.memberId}`] = []}info[`member__${message.memberId}`].push(message)view.drawMessageList(info[`member__${message.memberId}`])}else{ // 好友私聊消息if (info[`member__${message.id}`] == undefined){info[`member__${message.id}`] = []}info[`member__${message.id}`].push(message)}if (info.member == message.id){view.drawMessageList(info[`member__${message.id}`])}else{// 提示私聊新消息if ($(`.item[data-id="${message.id}"]`)){$(`.item[data-id="${message.id}"]`).setAttribute('data-new', 'true')let nNewNum = $(`.item[data-id="${message.id}"] .item-name`).getAttribute('data-message')$(`.item[data-id="${message.id}"] .item-name`).setAttribute('data-message', Number(nNewNum)+1)new Notification('收到来自简言的新消息', {body: `${message.name}: ${message.text}`,icon: message.url})}}// userList 消息摘要if ($(`.item[data-id="${message.id}"]`)){$(`.item[data-id="${message.id}"] .item-text`).innerHTML = message.text || `[收到新灵魂]`}})
}

其中的各种事件,就是前端逻辑的处理了。这是一个简单的websocket的demo。
学习的是https://github.com/cp0725/YouChat这个库,建议加一个脚本,build: “webpack --config webpack.config.js”, 方便开发调试

心跳检测

WebSocket 心跳检测的本质
本质是通过周期性双向验证维持长连接的活性,解决以下核心问题:

网络中间层的「假性存活」

现象:虽然 TCP 层连接未断开,但防火墙/Nginx 等中间件会主动关闭长时间(如 5 分钟)无数据传输的连接

解法:通过定时发送轻量级探测包(心跳包)重置中间件的空闲计时器

「半开连接」黑洞问题

现象:客户端异常断网后,服务端无法感知连接已失效,持续等待消息

解法:通过双向心跳响应机制,实现连接状态实时探活

与 WebSocket 协议特性的深度结合

协议头优化

心跳包利用 WebSocket 的极简帧头(最低 2 字节),相比 HTTP 头节省 90%+ 流量

扩展协议支持

通过 Sec-WebSocket-Extensions 协商心跳参数(如间隔时间)

可自定义心跳包格式(如携带设备电量、网络类型等元数据)

无跨域特性

心跳机制可跨域运行,无需像 HTTP 轮询那样处理 CORS 问题

http 通过判断 header 中是否包含 Connection: Upgrade 与 Upgrade: websocket 来判断当前是否需要升级到 websocket 协议,除此之外,还有其它 header:

Sec-WebSocket-Key :浏览器随机生成的安全密钥
Sec-WebSocket-Version :WebSocket 协议版本
Sec-WebSocket-Extensions :用于协商本次连接要使用的 WebSocket 扩展
Sec-WebSocket-Protocol :协议
当服务器同意进行 WebSocket 连接时,返回响应码 101

WebSocket 特点:

支持双向通信,实时性更强;
可以发送文本,也可以二进制文件;
协议标识符是 ws,加密后是 wss ;
较少的控制开销。连接创建后,ws客户端、服务端进行数据交换时,协议控制的数据包头部较小。在不包含头部的情况下,服务端到客户端的包头只有 2~10 字节(取决于数据包长度),客户端到服务端的的话需要加上额外的 4 字节的掩码。而 HTTP 协议每次通信都需要携带完整的头部;
支持扩展。ws 协议定义了扩展,用户可以扩展协议,或者实现自定义的子协议。(比如支持自定义压缩算法等)
无跨域问题。

SSE

sse是服务器向客户端推送, 用的是https的长连接,支持自动重连

const express = require('express');
const app = express();
const port = 3000;// 允许跨域(开发环境用)
app.use((req, res, next) => {res.header('Access-Control-Allow-Origin', '*');next();
});// SSE 路由
app.get('/sse-stream', (req, res) => {// 设置 SSE 必需的头信息res.writeHead(200, {'Content-Type': 'text/event-stream','Cache-Control': 'no-cache','Connection': 'keep-alive'});// 发送初始数据res.write('event: connected\ndata: Welcome!\n\n');// 定时发送数据(模拟实时更新)let counter = 0;const timer = setInterval(() => {counter++;const data = {time: new Date().toISOString(),value: counter};// SSE 标准格式(注意换行符)res.write(`event: update\n`);res.write(`data: ${JSON.stringify(data)}\n\n`);}, 1000);// 客户端断开时清理req.on('close', () => {clearInterval(timer);res.end();});
});app.listen(port, () => {console.log(`Server running at http://localhost:${port}`);
});
<!DOCTYPE html>
<html>
<body><div id="sse-data"></div><script>const eventSource = new EventSource('http://localhost:3000/sse-stream');// 通用消息处理(默认事件)eventSource.onmessage = (e) => {console.log('Default event:', e.data);};// 自定义事件处理(对应服务端的 event:update)eventSource.addEventListener('update', (e) => {const data = JSON.parse(e.data);document.getElementById('sse-data').innerHTML = `Time: ${data.time}<br>Counter: ${data.value}`;});// 连接建立事件eventSource.addEventListener('connected', (e) => {console.log('Connection established:', e.data);});// 错误处理eventSource.onerror = (e) => {if (e.eventPhase === EventSource.CLOSED) {console.log('Connection closed');} else {console.error('SSE Error:', e);}// 自动重连(浏览器默认行为)};// 页面关闭时断开连接window.addEventListener('beforeunload', () => {eventSource.close();});</script>
</body>
</html>

相关文章:

  • 使用Spring Validation实现参数校验
  • Step文件无法编辑怎么办?
  • System.in 详解
  • 个人自用-导入安装Hexo
  • Java 内存优化:如何避免内存泄漏?
  • React-useImperativeHandle (forwardRef)
  • CRT(阴极射线管)终端控制器
  • 手动实现LinkedList
  • 【算法数据结构】leetcode37 解数独
  • Unreal 从入门到精通之如何接入MQTT
  • 代码审计入门 原生态sql注入篇
  • 事件冒泡与捕获
  • LeetCode 438 找到字符串中所有字母异位词
  • C语言学习之预处理指令
  • 定制一款国密浏览器(9):SM4 对称加密算法
  • 微信小程序 时间戳与日期格式的转换
  • 今天分享一个网店客服回复数据集-用于网点客服AI助手自动回复智能体训练
  • 下采样(Downsampling)
  • python文件处理自用
  • 【PCIE配置空间】
  • 澳门世界杯“中日对决”,蒯曼击败伊藤美诚晋级女单决赛
  • 张小泉:控股股东所持18%股份将被司法拍卖,不会导致控制权变更
  • 男子拍摄女性视频后在网上配发诱导他人违法犯罪文字,已被警方行拘
  • 融创中国披露二次境外债重组方案:总规模约95.5亿美元债全额转股权,孙宏斌部分受限股票6年内不得处置
  • 陈宝良:明清时期少林、武当两派的拳法
  • 多家期刊就AI辅助写作表态:不想让放弃思考毁了一代人