什么是 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 {