iT邦幫忙

0

SSE 与 WebSocket 通信原理及 Go 实现

  • 分享至 

  • xImage
  •  

一、为什么需要 SSE 和 WebSocket?

传统 HTTP 通信通常采用:

客户端发送请求
      ↓
服务器处理请求
      ↓
服务器返回响应
      ↓
请求结束

例如:

GET /api/user HTTP/1.1
Host: example.com

服务器:

HTTP/1.1 200 OK
Content-Type: application/json

{
    "name": "Tom"
}

这种模式本质上是:

Client Request
      ↓
Server Response
      ↓
连接/请求结束

但是很多实时业务需要服务器主动向客户端发送数据,例如:

  • AI 大模型流式输出
  • 实时通知
  • 股票行情
  • 日志实时输出
  • 聊天系统
  • 在线游戏
  • 协同编辑
  • 实时监控大屏

如果使用普通 HTTP,客户端可能需要不断轮询:

Client → Server:有新消息吗?
Client ← Server:没有

Client → Server:有新消息吗?
Client ← Server:没有

Client → Server:有新消息吗?
Client ← Server:有

Client → Server:有新消息吗?
...

这种方式称为 Polling(轮询)

轮询会产生大量无意义 HTTP 请求,因此出现了更加适合实时通信的技术:

SSE
WebSocket

二、SSE

1. 什么是 SSE?

SSE 全称:

Server-Sent Events

即:

服务器发送事件

SSE 是一种基于 HTTP 的服务器向客户端持续推送数据的通信机制。

通信方向:

Client ───── HTTP Request ─────> Server

Client <──── SSE Event ───────── Server
Client <──── SSE Event ───────── Server
Client <──── SSE Event ───────── Server
Client <──── SSE Event ───────── Server

建立连接以后,服务器可以不断向客户端写入数据。

但 SSE 本身只负责:

Server → Client

因此它是单向通信

如果客户端需要向服务器发送数据,仍然可以使用:

fetch
axios
普通 HTTP API

例如:

                 HTTP POST
Client ─────────────────────────> Server

                 SSE
Client <───────────────────────── Server

这也是很多 AI 对话系统常见的通信模式。


三、SSE 的核心特点

1. 单向通信

SSE 的数据流向为:

Server → Client

服务器可以主动向客户端推送消息。

客户端如果需要发送数据:

Client → Server

一般通过普通 HTTP 请求完成。


2. 基于 HTTP

SSE 并没有设计一个新的传输协议。

它本质上仍然是一个 HTTP 请求,只不过服务器:

不立即结束 Response

而是持续向 Response Body 中写数据。

例如:

HTTP Response
│
├── data: message1
│
├── data: message2
│
├── data: message3
│
└── ...

因此 SSE 通常比较容易兼容现有:

HTTP Server
HTTPS
反向代理
负载均衡

不过代理服务器是否会缓存或缓冲响应仍然需要正确配置。


3. 持久 HTTP 连接

客户端建立 SSE:

const source = new EventSource("/events");

浏览器发送 HTTP 请求:

GET /events HTTP/1.1
Host: localhost:8080
Accept: text/event-stream

服务器不会立即结束响应。

而是返回:

HTTP/1.1 200 OK
Content-Type: text/event-stream
Cache-Control: no-cache

随后持续发送:

data: hello

data: world

data: test

连接一直保持:

HTTP Response
      │
      ├── Event
      ├── Event
      ├── Event
      ├── Event
      └── ...

4. 浏览器支持自动重连

SSE 的一个重要特点是:

EventSource 内置自动重连

例如网络突然断开:

Client ←X→ Server

浏览器的 EventSource 通常会自动尝试重新建立连接。

服务器还可以指定建议的重连时间:

retry: 3000

表示:

建议客户端大约 3 秒后重新连接

四、SSE 通信完整流程

第一步:客户端建立连接

浏览器:

const eventSource = new EventSource("/events");

监听服务器消息:

eventSource.onmessage = function(event) {
    console.log("收到消息:", event.data);
};

监听错误:

eventSource.onerror = function(event) {
    console.error("SSE 连接异常:", event);
};

五、服务器返回 SSE 响应

服务器需要返回:

Content-Type: text/event-stream
Cache-Control: no-cache

通常还可能看到:

Connection: keep-alive

但需要注意:

Connection 属于逐跳(hop-by-hop)HTTP 头,而且 HTTP/2 不使用这种方式表达连接持久化,因此它不是 SSE 协议成立的必要条件。

SSE 真正关键的是:

Content-Type: text/event-stream

六、SSE 数据格式

SSE 使用:

text/event-stream

服务器发送的每个事件通常使用空行分隔。

最简单的数据:

data: hello

实际字节形式相当于:

data: hello\n\n

多个事件:

data: hello

data: world

data: SSE

1. data

最常用字段:

data: hello

例如 JSON:

data: {"username":"Tom","message":"hello"}

前端:

eventSource.onmessage = function(event) {
    const data = JSON.parse(event.data);
    console.log(data);
};

2. event

SSE 可以定义事件名称:

event: user-login
data: {"username":"Tom"}

前端监听:

eventSource.addEventListener("user-login", function(event) {
    console.log(event.data);
});

因此服务器可以发送不同类型的事件:

event: login
data: Tom

event: logout
data: Jack

event: message
data: Hello

3. id

SSE 可以给事件指定 ID:

id: 1001
data: hello

浏览器重新连接时,可以通过:

Last-Event-ID

告诉服务器:

我最后收到的是哪个事件

服务器就有机会继续发送后续事件。

例如:

id: 1001
data: A

id: 1002
data: B

id: 1003
data: C

客户端收到:

1001
1002

随后断线。

重新连接时可能发送:

Last-Event-ID: 1002

服务器可以根据业务逻辑从:

1003

继续推送。

注意:

EventSource 提供 Last-Event-ID 机制,但服务端是否真正保存历史事件并实现“断点续传”,仍然需要开发者自己设计。


4. retry

服务器可以发送:

retry: 3000

告诉客户端建议的重连等待时间。

单位:

毫秒

七、完整 SSE Event

一个完整事件可以是:

id: 1001
event: message
retry: 3000
data: {"username":"Tom","message":"Hello"}

结构:

id
event
retry
data
空行

八、Go 实现 SSE 服务端

下面实现一个简单 SSE 服务:

GET /events

服务器每秒发送一次当前时间。

main.go

package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"time"
)

type Message struct {
	Time    string `json:"time"`
	Message string `json:"message"`
}

func sseHandler(w http.ResponseWriter, r *http.Request) {
	// SSE 必需/常用响应头
	w.Header().Set("Content-Type", "text/event-stream")
	w.Header().Set("Cache-Control", "no-cache")
	w.Header().Set("X-Accel-Buffering", "no")

	// 确保当前 ResponseWriter 支持立即刷新数据
	flusher, ok := w.(http.Flusher)
	if !ok {
		http.Error(w, "Streaming unsupported", http.StatusInternalServerError)
		return
	}

	log.Println("SSE client connected")

	ticker := time.NewTicker(time.Second)
	defer ticker.Stop()

	messageID := 1

	for {
		select {

		case <-r.Context().Done():
			// 浏览器关闭连接、刷新页面或网络断开时,
			// Request Context 会被取消。
			log.Println("SSE client disconnected")
			return

		case t := <-ticker.C:

			message := Message{
				Time:    t.Format(time.RFC3339),
				Message: "Hello from Go SSE server",
			}

			data, err := json.Marshal(message)
			if err != nil {
				log.Println("JSON marshal error:", err)
				continue
			}

			// SSE 数据格式
			fmt.Fprintf(w, "id: %d\n", messageID)
			fmt.Fprintln(w, "event: message")
			fmt.Fprintf(w, "data: %s\n\n", data)

			// 非常重要:
			// 将缓冲区中的内容立即发送给客户端
			flusher.Flush()

			messageID++
		}
	}
}

func main() {

	http.HandleFunc("/events", sseHandler)

	log.Println("SSE server running:")
	log.Println("http://localhost:8080/events")

	err := http.ListenAndServe(":8080", nil)
	if err != nil {
		log.Fatal(err)
	}
}

运行:

go run main.go

服务器:

http://localhost:8080

九、SSE 前端代码

创建:

index.html

代码:

<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <title>SSE Demo</title>
</head>

<body>

<h1>SSE Demo</h1>

<button onclick="connect()">连接</button>
<button onclick="disconnect()">断开</button>

<pre id="output"></pre>

<script>

let eventSource = null;

function connect() {

    if (eventSource) {
        return;
    }

    eventSource = new EventSource("http://localhost:8080/events");

    eventSource.onopen = function () {
        console.log("SSE 已连接");
    };

    eventSource.addEventListener("message", function (event) {

        console.log("Event ID:", event.lastEventId);
        console.log("Data:", event.data);

        const data = JSON.parse(event.data);

        document.getElementById("output").textContent +=
            data.time + " -> " + data.message + "\n";
    });

    eventSource.onerror = function (error) {
        console.error("SSE error:", error);

        // 不要在这里直接 close,
        // 否则 EventSource 的自动重连能力会被关闭。
    };
}

function disconnect() {

    if (eventSource) {
        eventSource.close();
        eventSource = null;

        console.log("SSE 已主动关闭");
    }
}

</script>

</body>
</html>

十、SSE 中 Flush 为什么重要?

Go HTTP Server 会进行缓冲。

假设服务器执行:

fmt.Fprintf(w, "data: hello\n\n")

数据不一定马上到达浏览器。

因此流式响应通常需要:

flusher.Flush()

整体过程:

Go Handler
    │
    │ Write()
    ↓
HTTP Buffer
    │
    │ Flush()
    ↓
TCP
    │
    ↓
Browser

如果没有及时 Flush,就可能出现:

服务器已经生成很多数据
        ↓
数据仍然停留在缓冲区
        ↓
浏览器迟迟看不到

这对于:

AI Token Streaming
日志 Streaming
实时通知

尤其重要。


十一、SSE 常见架构

例如 AI 对话:

Browser
   │
   │ POST /chat
   │ {"message":"解释TCP"}
   ↓
Go Server
   │
   │ 调用 AI
   ↓
LLM
   │
   │ Token
   ↓
Go Server
   │
   │ SSE
   ↓
Browser

浏览器看到:

TCP
TCP 是
TCP 是一种
TCP 是一种可靠的
TCP 是一种可靠的传输层
...

这就是典型的流式输出。


十二、WebSocket

1. 什么是 WebSocket?

WebSocket 是一种建立在 TCP 连接之上的全双工通信协议

通信方向:

Client ←────────────→ Server

双方都可以主动发送消息。

例如:

Client ─── Hello ─────────> Server
Client <── Hi ───────────── Server
Client <── New Message ──── Server
Client ─── Message ────────> Server

因此非常适合:

聊天室
在线游戏
协同编辑
实时控制
实时交易界面

十三、一个容易混淆的概念

经常有人说:

WebSocket 建立以后就从 HTTP 变成 TCP。

这种说法不够准确。

正确理解应该是:

TCP
 │
 ├── HTTP
 │
 └── WebSocket

WebSocket 和 HTTP 都可以运行在 TCP 之上。

典型 WebSocket 建连过程是:

TCP Connection
      ↓
HTTP Upgrade Handshake
      ↓
101 Switching Protocols
      ↓
WebSocket Protocol
      ↓
WebSocket Frames

也就是说:

TCP 连接没有变,只是应用层协议从 HTTP Upgrade 握手切换成了 WebSocket。

加密连接则通常是:

TCP
 ↓
TLS
 ↓
HTTP Upgrade
 ↓
WebSocket

对应:

wss://

十四、WebSocket 通信流程

完整过程:

Client                                      Server

   |                                           |
   |------ TCP Connection -------------------->|
   |                                           |
   |------ HTTP Upgrade Request -------------->|
   |                                           |
   |<----- 101 Switching Protocols ------------|
   |                                           |
   |========== WebSocket Connection ===========|
   |                                           |
   |------ Text/Binary Frame ----------------->|
   |<----- Text/Binary Frame ------------------|
   |<----- Ping -------------------------------|
   |------ Pong ------------------------------>|
   |                                           |
   |------ Close ------------------------------>|
   |<----- Close ------------------------------|
   |                                           |
   X                                           X

十五、WebSocket 握手

客户端:

const socket = new WebSocket("ws://localhost:8080/ws");

浏览器首先发送类似:

GET /ws HTTP/1.1
Host: localhost:8080
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: xxxxxxxxxxxxxxxxx
Sec-WebSocket-Version: 13

其中最重要的是:

Upgrade: websocket
Connection: Upgrade

表示:

客户端希望把当前连接升级为 WebSocket。

十六、Sec-WebSocket-Key

客户端会产生随机值:

Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==

服务器会把 Key 和 WebSocket 标准规定的 GUID:

258EAFA5-E914-47DA-95CA-C5AB0DC85B11

拼接:

Key + GUID

然后:

SHA-1
  ↓
Base64

最终生成:

Sec-WebSocket-Accept

例如:

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

它主要用于验证服务器确实理解 WebSocket 握手,而不是普通 HTTP 服务误处理了这个请求。

它本身并不是用户身份认证或消息加密机制。

真正需要加密通信时应该使用:

wss://

即:

WebSocket over TLS

十七、WebSocket Frame

握手完成以后:

HTTP Header

不会跟在每一条业务消息前面。

数据通过:

WebSocket Frame

传输。

常见 Frame 类型包括:

Text
Binary
Ping
Pong
Close
Continuation

因此 WebSocket 不仅可以发送文本:

Hello

还可以发送二进制:

[]byte
图片数据
音频数据
自定义二进制协议

十八、WebSocket Ping / Pong

WebSocket 协议定义:

Ping
Pong

控制帧。

例如:

Server ---- Ping ----> Client

Server <--- Pong ----- Client

它可以帮助应用检测:

连接是否仍然存活

需要注意:

浏览器 WebSocket API 并没有向 JavaScript 暴露直接发送 Ping/Pong 控制帧的接口。

通常 Ping/Pong 由服务器端 WebSocket 库和浏览器协议栈处理。

有些应用也会额外设计业务层心跳:

{
    "type": "ping"
}

服务器:

{
    "type": "pong"
}

这种 JSON 心跳属于:

应用层心跳

与 WebSocket 协议自己的 Ping/Pong 控制帧不是同一个东西。


十九、Go 实现 WebSocket

Go 标准库 net/http 本身没有提供完整的高级 WebSocket API,因此实际项目通常使用第三方库。

例如:

github.com/coder/websocket

初始化项目:

mkdir websocket-demo
cd websocket-demo

go mod init websocket-demo

安装:

go get github.com/coder/websocket

二十、Go WebSocket 服务端

创建:

main.go

代码:

package main

import (
	"context"
	"log"
	"net/http"
	"time"

	"github.com/coder/websocket"
	"github.com/coder/websocket/wsjson"
)

type Message struct {
	Type    string `json:"type"`
	Content string `json:"content"`
	Time    string `json:"time"`
}

func websocketHandler(w http.ResponseWriter, r *http.Request) {

	conn, err := websocket.Accept(w, r, &websocket.AcceptOptions{
		// Demo 使用。
		// 生产环境应严格检查 Origin。
		InsecureSkipVerify: true,
	})

	if err != nil {
		log.Println("WebSocket accept error:", err)
		return
	}

	defer conn.Close(websocket.StatusNormalClosure, "connection closed")

	log.Println("WebSocket client connected")

	ctx := r.Context()

	for {

		var clientMessage Message

		err := wsjson.Read(ctx, conn, &clientMessage)

		if err != nil {

			status := websocket.CloseStatus(err)

			if status == websocket.StatusNormalClosure ||
				status == websocket.StatusGoingAway {

				log.Println("WebSocket client disconnected")
				return
			}

			log.Println("WebSocket read error:", err)
			return
		}

		log.Printf(
			"收到客户端消息: type=%s content=%s",
			clientMessage.Type,
			clientMessage.Content,
		)

		response := Message{
			Type:    "response",
			Content: "Server received: " + clientMessage.Content,
			Time:    time.Now().Format(time.RFC3339),
		}

		writeCtx, cancel := context.WithTimeout(ctx, 5*time.Second)

		err = wsjson.Write(writeCtx, conn, response)

		cancel()

		if err != nil {
			log.Println("WebSocket write error:", err)
			return
		}
	}
}

func main() {

	http.HandleFunc("/ws", websocketHandler)

	log.Println("WebSocket server running:")
	log.Println("ws://localhost:8080/ws")

	err := http.ListenAndServe(":8080", nil)

	if err != nil {
		log.Fatal(err)
	}
}

二十一、WebSocket 前端

创建:

index.html

代码:

<!DOCTYPE html>
<html lang="zh-CN">

<head>
    <meta charset="UTF-8">
    <title>WebSocket Demo</title>
</head>

<body>

<h1>WebSocket Demo</h1>

<button onclick="connect()">连接</button>

<input
    id="message"
    type="text"
    placeholder="请输入消息"
/>

<button onclick="sendMessage()">发送</button>

<button onclick="disconnect()">断开</button>

<pre id="output"></pre>

<script>

let socket = null;

function connect() {

    if (socket &&
        (socket.readyState === WebSocket.OPEN ||
         socket.readyState === WebSocket.CONNECTING)) {
        return;
    }

    socket = new WebSocket("ws://localhost:8080/ws");

    socket.onopen = function () {

        console.log("WebSocket 已连接");

        document.getElementById("output").textContent +=
            "WebSocket 已连接\n";
    };

    socket.onmessage = function (event) {

        console.log("收到服务器消息:", event.data);

        const data = JSON.parse(event.data);

        document.getElementById("output").textContent +=
            data.time + " -> " + data.content + "\n";
    };

    socket.onerror = function (error) {

        console.error("WebSocket error:", error);

    };

    socket.onclose = function (event) {

        console.log(
            "WebSocket 已关闭",
            event.code,
            event.reason
        );

        document.getElementById("output").textContent +=
            "WebSocket 已关闭\n";
    };
}

function sendMessage() {

    if (!socket || socket.readyState !== WebSocket.OPEN) {

        console.log("WebSocket 尚未连接");
        return;
    }

    const input =
        document.getElementById("message");

    const message = {

        type: "message",

        content: input.value,

        time: new Date().toISOString()

    };

    socket.send(
        JSON.stringify(message)
    );

    input.value = "";
}

function disconnect() {

    if (socket) {

        socket.close(
            1000,
            "Client closed connection"
        );

    }
}

</script>

</body>
</html>

二十二、WebSocket 数据流

客户端发送:

socket.send(JSON.stringify({
    type: "message",
    content: "Hello Server"
}));

数据流:

Browser
   │
   │ WebSocket Frame
   │
   │ {
   │   "type":"message",
   │   "content":"Hello Server"
   │ }
   ↓
Go Server

Go:

wsjson.Read(ctx, conn, &clientMessage)

服务器处理以后:

wsjson.Write(ctx, conn, response)

返回:

Go Server
   │
   │ WebSocket Frame
   ↓
Browser

前端:

socket.onmessage = function(event) {
    console.log(event.data);
};

二十三、WebSocket 与 SSE 最大区别

最重要的区别不是:

谁更先进

而是:

通信模型不同

SSE:

        HTTP Request
Client ----------------> Server

        Event Stream
Client <================ Server

核心方向:

Server
  ↓
Client

WebSocket:

Client <===============> Server

核心方向:

Client
  ↕
Server

二十四、SSE vs WebSocket 对比

特性 SSE WebSocket
全称 Server-Sent Events WebSocket
通信方向 Server → Client Client ↔ Server
通信模型 单向 全双工
建连方式 HTTP 请求 HTTP Upgrade 握手
应用层协议 HTTP Event Stream WebSocket
浏览器 API EventSource WebSocket
自动重连 浏览器原生支持 通常需要应用自己实现
文本 支持 支持
二进制 不直接支持 原生支持
JSON 支持 支持
Ping/Pong 无 WebSocket 式协议控制帧 协议原生支持
消息 ID SSE 原生支持 id: 应用层自己设计
断点恢复 可利用 Last-Event-ID 应用层自己设计
HTTP/HTTPS 基础设施兼容 较好 通常需要代理显式支持 Upgrade/WS
实现复杂度 较低 相对较高
AI 流式输出 非常适合 可以
实时通知 非常适合 可以
实时日志 非常适合 可以
聊天系统 可以,但客户端发送仍需 HTTP 非常适合
在线游戏 不适合主要双向链路 非常适合
协同编辑 通常不是首选 非常适合

二十五、什么时候选择 SSE?

如果业务基本是:

服务器不断产生数据
        ↓
浏览器不断接收

优先考虑 SSE。

例如:

AI 流式输出
实时日志
服务器状态
任务执行进度
消息通知
实时监控
新闻/行情推送

典型架构:

        POST /chat
Browser -------------> Go Server
                           │
                           ↓
                          AI
                           │
        SSE Stream         │
Browser <==================

客户端发送请求:

POST /chat

服务器持续返回:

data: 你

data: 你好

data: 你好,

data: 你好,我

data: 你好,我是

二十六、什么时候选择 WebSocket?

如果双方都需要频繁主动发送数据:

Client → Server
Server → Client

优先考虑 WebSocket。

例如:

聊天室
在线游戏
协同编辑
实时白板
远程控制
多人房间
实时交易终端

例如聊天:

                  WebSocket Server

                     ┌───────┐
                     │  Go   │
                     └───┬───┘
                         │
            ┌────────────┼────────────┐
            │            │            │
            ↓            ↓            ↓

         Client A     Client B     Client C

A:

大家好

发送给服务器:

A → Server

服务器广播:

Server → A
Server → B
Server → C

这就是典型的 WebSocket 聊天室模型。


二十七、SSE 与 WebSocket 的底层关系

可以从协议栈理解。

SSE

使用 HTTP 时:

┌─────────────────────┐
│ SSE / Event Stream  │
├─────────────────────┤
│ HTTP                │
├─────────────────────┤
│ TCP                 │
├─────────────────────┤
│ IP                  │
└─────────────────────┘

HTTPS:

SSE
 ↓
HTTP
 ↓
TLS
 ↓
TCP
 ↓
IP

WebSocket

经典 HTTP/1.1 Upgrade 场景:

WebSocket
    ↓
TCP
    ↓
IP

加密:

WebSocket
    ↓
TLS
    ↓
TCP
    ↓
IP

但连接最开始通常经历:

HTTP Upgrade

因此生命周期可以理解为:

TCP
 │
 ├─ HTTP Handshake
 │
 └─ WebSocket Frames

二十八、生产环境还需要考虑什么?

上面的代码主要用于理解通信机制。

真正生产环境还需要处理:

身份认证
Origin 校验
TLS
连接数量限制
超时
心跳
重连
消息大小限制
慢客户端
背压
限流
日志
监控
异常连接清理
负载均衡
反向代理配置

特别是 WebSocket:

一个客户端
      =
一个长期连接

如果:

100000 用户在线

服务器就可能需要维护:

100000 个长连接

因此需要重点考虑:

连接管理
文件描述符限制
内存消耗
goroutine 数量
负载均衡
连接迁移
广播机制

二十九、Nginx 代理 SSE 时需要注意

SSE 最常见的问题之一不是 Go 代码,而是:

代理缓冲

例如:

Go
 ↓
Nginx
 ↓
Browser

如果 Nginx 缓冲响应:

Go 一直 Write + Flush
        ↓
Nginx Buffer
        ↓
        ↓
        ↓
积累到一定程度
        ↓
Browser 一次收到大量数据

这样就失去了实时流式效果。

Go 示例中:

w.Header().Set("X-Accel-Buffering", "no")

可以帮助 Nginx 场景关闭当前响应的代理缓冲。

也可以根据实际部署配置 Nginx。


三十、WebSocket 生产环境不要随意允许所有 Origin

示例为了方便本地学习使用:

websocket.Accept(w, r, &websocket.AcceptOptions{
    InsecureSkipVerify: true,
})

生产环境不建议这样做。

应该验证:

Origin
Host
用户身份
Token / Session

否则可能产生跨站 WebSocket 劫持等安全问题。


三十一、SSE 和 WebSocket 与普通 HTTP 的关系总结

可以把三种方式理解为:

普通 HTTP

Client ---- Request ----> Server
Client <--- Response ---- Server

结束

特点:

一问一答

SSE

Client ---- Request ----> Server

Client <--- Event ------- Server
Client <--- Event ------- Server
Client <--- Event ------- Server
Client <--- Event ------- Server
                 ...

特点:

客户端建立 HTTP 请求
服务器持续输出 Response

核心:

Server → Client

WebSocket

Client ---- Handshake ---> Server
Client <--- 101 ---------- Server

Client ===== Message ====> Server
Client <==== Message ===== Server
Client ===== Message ====> Server
Client <==== Message ===== Server

特点:

建立一次连接
双方长期双向通信

核心:

Client ↔ Server

三十二、一句话记忆

可以直接记住:

HTTP:
客户端问一次,服务器答一次。

SSE:
客户端建立一条 HTTP 流,
服务器可以沿着这条流不断往客户端推数据。

WebSocket:
先通过握手建立 WebSocket 连接,
之后客户端和服务器都可以随时给对方发送消息。

技术选型可以进一步简化成:

只需要服务器持续推送
        ↓
       SSE

需要客户端和服务器高频双向通信
        ↓
    WebSocket

典型场景:

AI 流式输出      → SSE
任务进度         → SSE
实时日志         → SSE
服务器通知       → SSE

聊天室           → WebSocket
在线游戏         → WebSocket
协同编辑         → WebSocket
实时双向控制     → WebSocket

最终理解这两种技术时,最重要的不是死记 API,而是记住通信模型:

SSE

Client
  ↑
  │
Server


WebSocket

Client
  ↕
Server

一旦理解了这个区别,SSE 和 WebSocket 的绝大多数设计选择都会变得非常清楚。


圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言