惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

WordPress大学
WordPress大学
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Hacker News: Ask HN
Hacker News: Ask HN
N
News and Events Feed by Topic
Forbes - Security
Forbes - Security
The Last Watchdog
The Last Watchdog
TaoSecurity Blog
TaoSecurity Blog
Schneier on Security
Schneier on Security
SecWiki News
SecWiki News
V
Vulnerabilities – Threatpost
Project Zero
Project Zero
O
OpenAI News
W
WeLiveSecurity
Security Archives - TechRepublic
Security Archives - TechRepublic
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
H
Hacker News: Front Page
Cisco Talos Blog
Cisco Talos Blog
Spread Privacy
Spread Privacy
Help Net Security
Help Net Security
P
Privacy & Cybersecurity Law Blog
K
Kaspersky official blog
S
Security @ Cisco Blogs
Latest news
Latest news
AWS News Blog
AWS News Blog
U
Unit 42
Martin Fowler
Martin Fowler
阮一峰的网络日志
阮一峰的网络日志
S
Secure Thoughts
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
Application and Cybersecurity Blog
Application and Cybersecurity Blog
Know Your Adversary
Know Your Adversary
Scott Helme
Scott Helme
博客园 - 司徒正美
B
Blog RSS Feed
C
Check Point Blog
Hacker News - Newest:
Hacker News - Newest: "LLM"
D
Docker
Google Online Security Blog
Google Online Security Blog
Jina AI
Jina AI
aimingoo的专栏
aimingoo的专栏
Recent Commits to openclaw:main
Recent Commits to openclaw:main
Last Week in AI
Last Week in AI
月光博客
月光博客
C
CXSECURITY Database RSS Feed - CXSecurity.com
S
SegmentFault 最新的问题
NISL@THU
NISL@THU
T
The Blog of Author Tim Ferriss
C
Cisco Blogs
Attack and Defense Labs
Attack and Defense Labs
小众软件
小众软件

一个工匠

AI 发展临界点 - 快上车 流式响应(Chat):HTTP 响应体字节流的读取与解析 iPhone 截屏高效翻译 Flash Open Terminal iPhone 侧载安装 Xcode Symbolic Debug AI 指北 投资风险收益评估 Mac 快速修改系统快捷键 Git Record passkey How to pay apple frameworks Mac - Command Line Tools DNS 的 CNAME 是如何工作的 WebRTC 的一些解释 1.1.1.1、WARP 和 MASQUE 移动端换端方案与场景还原 设备发现 分辨率 - 像素密度 - HiDPI Mac 字体 ESP32 trump - 川普 币圈 - 虚拟货币 【旅游】富士山 【旅游】sakura(樱花) 【旅游】千岛湖 我的 2023 【Swift】中文词语纠错 计算机字符编码与内存编码 - Unicode、UTF8、String 从汇编角度理解 “函数调用栈” 和 “有栈协程” 在 Xcode 中更好的使用 Swift 通过 Swift Package 制作二进制库 Arc 浏览器 - 意外的惊喜 【Swift 三方源码 1】SwiftShell 高效的命令行工具 Swift 脚本开发环境搭建 rime - 小鹤双拼 【旅游】沿海旅游 (威海、青岛) 好记性远远不如烂笔头 高级语言小记 又一年高考 Vision 未来已来 致敬耗子哥 Socket 与 ICMP 对互联网隐私的一些想法 网络代理是如何工作的 (致敬 Surge) 又十年 - 至三十而立 双拼 - 少有的高回报效率技能 Vim 技巧 我的 2022 三年疫情五味杂陈 - 下 老梗 - 浏览器按回车后发生了什么 三年疫情五味杂陈 生活推荐清单 Shell 和进程 有计划的努力 内卷 不确定性 数学可以保障逻辑自洽 这些年我的软件清单 决定做一些改变 函数调用栈之彻底理解 锁 - 共享数据安全指↑ 江苏女有感 碎碎念 - 2022 碎碎念 - 2021 打算记录一些碎碎念 多重性格就是逆天改命 从 Core Foundation 看更大世界 TCP 数据传输过程分析 iOS main 文件中的 @autoreleasepool 的真实用途 多线程的难点在哪里 Objective-C 和 Runtime 早起打卡计划 ing 打比方 IM 和 Socket 的关系及 Heart 的必要性 如果我的人生突然只剩下 8 小时... 胡说八道之雪粉 Array 和 LinkedList 的深刻认知 Swift struct (值类型) 与 class (引用类型) 区别及三种传参机制深刻关系和使用 逃离信用卡和花呗 Swift_let_var_struct_class 网聚人的力量 - 网易 绿皮火车 喝酒十之八九会误事 HKong 胡说八道之云服务 互联网网络传输协议理解坑点 CPU 的制作 我心里一直有一个念想 网络基础协议 为什么 32 位操作系统只能用 4G 内存空间 努力和天赋 洗牙 RAC-Api-Summer iOS 内存原理 Block 闭包简查记录 《白夜行》 谈谈事件驱动 老博新开 祭首
SSE 指南
海驴 · 2024-11-01 · via 一个工匠

Server-Sent Events(SSE)是一种允许服务器通过 HTTP 连接主动向客户端发送数据的技术。它主要被用于创建实时应用,如消息推送和实时通知。SSE 使用简单的文本格式发送消息,这种格式使得其易于在浏览器中实现和使用。

SSE 注意事项:

  1. 通过 HTTP 协议通道 建立单向长连接,即 Client 连接 Server 后,Server 不断开连接,并持续的通过 socket 套接字发送 data 给 Client。
  2. 网关等设备会主动关闭 tcp 通道,需要 SSE Server 端增加心跳。很多种场景都会导致 tcp 连接中断,和 IM 心跳一致。这里需要 SSE Server 增加应用层心跳,非 TCP 层心跳。

数据格式

通过具有明显分割线的消息体,来分割数据字段:

1
2
3
4
5
6
7
8
9
10
// 以下文本消息体,最终通过编译成二进制的形式被传递和解析,通过 \n 等标记符号进行【行】分割。

event: message/userupdate/custom...\n
data: {xxx}\n
id: 98769879675\n
retry: 10000\n
\n

// 一个完整的消息体如下,通过 \n 分割行,末尾通过 \n\n 分割单个消息体
id: event-id-1\ndata:event-data-first\n\n

以上 event/data/id/retry 四个字段中,data 是必须字段,其他三个是可选字段。每个消息体,必须以 \n\n 作为末尾标记。

在 ts 中,可行的生成消息体的 code 如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
private encodeMessage(message: ChatMessage) {
const data = {
type: message.type,
payload: message.data,
};
const content = [
`id: ${message.id}`,
`data: ${JSON.stringify(data)}`,
'',
'',
].join('\n'); // 这里,通过空行分割,在消息体的末尾增加 `\n\n` 标记
return this.encoder.encode(content);
}

对消息体的解析,也同样遵循固定的规律,即对二进制中的 \n 和 \n\n 进行解析。

解析流程:

1
2
3
4
1. 通过 \n\n 对消息体进行分割,获得一个消息体的二进制内容并开始解析
2. 通过 \n 对行进行解析,获得 xxx:xxx 这样的一行内容
3. 通过 : 符号,对行进行解析,获得 key:xxx 和 value:xxx 两个内容
4. 整合获得的所有内容,聚合成 {id:xxx,event:xxx,data:xxx,retry:xxx} 这样的消息体

消息类型

SSE 通过 event 字段,可以自定义各种消息体。约定通用的消息体是 message。有如下两种消息体:

1
2
id: xxx\ndata:xxx\nevent:message\n\n
id:xxx\ndata:xxx\nevent:{custom-name}\n\n

如果 event 为空,默认当作且应该当作 message 消息来解析和处理。

重连

http 通道可能发生中断,每条消息题都可以携带一个 retry 字段,用来告知 client 在断开后多久应该重连。

而重连的逻辑就是重新发起 http 连接。这里为了保持和之前的通道一致,应该在重连的时候在 header 中携带最后一次收到的消息的 id,可以让 server 侧知道从哪里中断的,以保持连续的服务。

心跳

SSE 应该使用应用层心跳,即发送一条空消息体,以保持 http tcp 套接字不被网关、nat 等场景强制关闭。格式如下:

1
2
:\n\n
:heartbeat\n\n

这里,心跳可以不遵循消息体的约定。

之前提到消息体一定需要有 data 字段,因为每一条消息都是需要传递信息,如果没有 data 字段,那么这条消息就无法被解析,也就没有传递的必要。

但是这套约定,并不是说没有 data 字段,消息的发送、接收、解析 整套流程就会失败,因为对于 TCP/UDP/HTTP 这套协议来说,它并不关心消息体的内容是什么。

而 Client 端,对消息体的解析应该是包容的,即消息体如果不符合约定,那么就应该抛弃。

这里的应用层心跳,就会走到这套逻辑里面。IM 是双向通信,为了保障心跳的到达,Client 端需要解析完整的心跳并回执。在 SSE 单向通道里面,只需要保障有一条消息从 Server 发往 Client 即可(没有成功率保障,因为是单向通信)。