什么是 SSE(Server-Sent Events)
Server-Sent Events(简称 SSE)是一种在浏览器中实现单向实时通信的技术。它允许服务器通过 HTTP 持久连接向客户端发送实时更新的数据流。这种通信模式非常适用于需要频繁向客户端推送数据的场景,例如股票行情更新、实时聊天通知、社交媒体动态等。
SSE 基于 HTTP 协议,并以文本流的方式传递数据,使用的是标准的 text/event-stream
MIME 类型。浏览器内置支持 SSE,无需额外的第三方库支持。
SSE 实现原理
SSE 的实现原理依赖于持久的 HTTP 连接:
- 客户端发起连接:客户端通过 HTTP 请求(通常是 GET 请求)向服务器发送一个持续连接的请求。
- 服务器保持连接:服务器接收到请求后,不关闭连接,而是以流的形式持续发送数据。
- 数据格式:服务器以
text/event-stream
格式推送数据,每条数据以换行符分隔,通常包含事件类型(event
)和数据内容(data
)。 - 自动重连:如果连接中断,浏览器会自动尝试重新连接。
一个典型的 SSE 数据流可能如下:
retry: 3000
id: 12345
event: message
data: {"content": "This is a server-sent message"}
SSE vs WebSocket
特性 | SSE | WebSocket |
---|---|---|
通信模式 | 单向(服务器向客户端) | 双向(全双工通信) |
协议 | HTTP | 独立的 WebSocket 协议 |
数据格式 | 纯文本 | 二进制或文本 |
自动重连 | 内置支持 | 需要手动实现 |
复杂度 | 简单,浏览器支持开箱即用 | 较复杂,需要更多样板代码 |
使用场景 | 实时通知、事件流 | 聊天应用、实时游戏 |
SSE 通信
Android Client
两种方式:一是只利用 okhttp,二是利用 okhttp-sse。
依赖库
implementation(libs.okhttp)
implementation(libs.okhttp.sse)
okhttp = "4.10.0"
okhttpSse = "4.9.3"okhttp = { group = "com.squareup.okhttp3", name="okhttp", version.ref = "okhttp" }
okhttp-sse = { group = "com.squareup.okhttp3", name="okhttp-sse", version.ref = "okhttpSse" }
1️⃣ 方式 一:仅用 okhttp
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SSEScreen() {val sseClient = SSEClient()var serverUrl by remember { mutableStateOf("http://192.168.3.44:8080/api/sse/stream") }var isSubscribed by remember { mutableStateOf(false) }val messages = remember { mutableStateListOf<String>() }Scaffold(topBar = {TopAppBar(title = { Text("SSE Client") })}) { padding ->Column(modifier = Modifier.fillMaxSize().padding(padding).padding(16.dp)) {BasicTextField(value = serverUrl,onValueChange = { serverUrl = it },modifier = Modifier.fillMaxWidth().padding(end = 8.dp).border(1.dp, MaterialTheme.colorScheme.primary, MaterialTheme.shapes.small).padding(8.dp),keyboardOptions = KeyboardOptions.Default.copy(imeAction = ImeAction.Done),keyboardActions = KeyboardActions(onDone = { /* Handle Enter */ }))Spacer(Modifier.height(10.dp))Button(onClick = {if (isSubscribed) {sseClient.cancel()messages.add("断开连接")isSubscribed = false} else {