SSE和WebSocket协议本质互斥,无法互相模拟:SSE是基于HTTP的单向文本流,依赖text/event-stream头和EventSource;WebSocket是独立TCP全双工协议,需HTTP升级握手,二者协议层、客户端API及消息格式完全不兼容。

SSE 和 WebSocket 是两种互斥的协议方案,Go 无法基于 WebSocket 实现 SSE——这不是技术限制问题,而是协议本质冲突。
SSE(Server-Sent Events)是 HTTP 协议之上的单向流式文本推送机制,依赖 Content-Type: text/event-stream、Connection: keep-alive 和客户端 EventSource;而 WebSocket 是独立于 HTTP 的全双工 TCP 连接,握手后即脱离 HTTP 语义,不再识别 data: 行、event: 字段或自动重连逻辑。
你真正需要的,是下面两种之一:
为什么不能用 WebSocket 模拟 SSE 格式?
即使你在 WebSocket 连接里手动拼 data: hello\n\n 字符串发给前端,EventSource 也完全收不到——它只监听 HTTP 响应流,不连接 WebSocket。反过来,用 WebSocket 客户端(new WebSocket())去连一个标着 text/event-stream 的接口,也会在握手阶段失败(HTTP 101 Switching Protocols 不会返回)。
常见错误现象:
- 前端
new EventSource("/stream")报错Failed to load resource: net::ERR_INVALID_HTTP_RESPONSE - 前端
new WebSocket("http://.../stream")报错Error during WebSocket handshake: Unexpected response code: 200 - 服务端日志显示连接被立即关闭,但没报错——其实是浏览器根本没完成协议协商
Go 中正确选择:SSE 用标准 HTTP handler,WebSocket 用 gorilla/websocket
不要混用。该选哪个,取决于你的交互需求:
Go语言(Golang)1.26.0版本提供 Go 官方 Windows amd64 MSI 安装包下载入口,版本号 1.26.0,可用于旧项目维护、兼容性测试和指定版本开发环境配置。
- 只需要「模型吐 token → 前端逐条渲染」:用
net/http+ 正确 header +Flush(),别碰 WebSocket 库 - 需要「用户中途取消请求 / 传文件 / 多轮对话上下文控制 / 客户端主动中断生成」:必须上
gorilla/websocket,自己定义消息结构(如 JSON 封装{"type":"token","data":"..."})
性能影响明显:gorilla/websocket 连接比 HTTP 长连接更省资源(无 HTTP 解析开销),但开发成本高一倍;而 SSE 在 Nginx 等反代下容易被静默断连(默认 proxy_read_timeout 60),需显式调大。
最容易踩的坑:Flush 不生效导致前端卡住
这是 Go 实现 SSE 最高频失败点。仅写 fmt.Fprintf(w, "data: ...\n\n") 不够,必须强制刷新缓冲区:
- 用
net/http原生 handler:检查w是否实现了http.Flusher,再调w.(http.Flusher).Flush() - 用 Gin:必须用
c.SSEvent()或手动c.Writer.(http.Flusher).Flush(),c.String()会缓存并终结响应 - 漏掉
Flush()的后果:前端等满 60 秒超时,或直到服务端协程结束才一次性收到全部数据
示例关键片段:
func sseHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "text/event-stream")
w.Header().Set("Cache-Control", "no-cache")
w.Header().Set("Connection", "keep-alive")
w.Header().Set("X-Accel-Buffering", "no") // Nginx 专用
<pre class="brush:php;toolbar:false;">flusher, ok := w.(http.Flusher)
if !ok {
http.Error(w, "streaming unsupported", http.StatusInternalServerError)
return
}
for _, chunk := range []string{"hello", " world", "!"} {
fmt.Fprintf(w, "data: %s\n\n", chunk)
flusher.Flush() // 必须!
time.Sleep(300 * time.Millisecond)
}}
SSE 和 WebSocket 不是“升级关系”,而是不同场景下的工具。强行把 WebSocket 当 SSE 用,只会让前后端都陷入不可调试的状态。真正要关注的,是你的业务是否真的需要双向能力——90% 的本地大模型聊天界面,text/event-stream 就已足够。










