在现代 Web 应用开发中,实时数据交互已成为核心需求。WebSocket 和 Server-Sent Events(SSE)作为两种主流的实时通信技术方案,分别适用于双向交互与单向推送场景。本文将基于 Go 语言的 Gin 框架,通过前后端代码示例,演示这两种技术的基本用法。
一、WebSocket 通信实现解析
1. 核心概念与优势
WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议,允许服务器主动向客户端推送数据,解决了 HTTP 协议的单向通信限制,适用于实时聊天、在线协作等场景。
2. Go 语言 Gin 框架实现代码
package controllers
import (
"go_cli/utils"
"log"
"net/http"
"strconv"
"sync"
"time"
"github.com/gin-gonic/gin"
"github.com/gorilla/websocket"
)
// Connection 表示一个客户端连接
type Connection struct {
ID string
WebSocket *websocket.Conn
LastSeen time.Time
IP string
}
// 全局连接管理器,直接使用sync.Map
var connectionManager = sync.Map{}
func HandleWebSocket(ctx *gin.Context) {
// 配置WebSocket upgrader
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
// 允许所有CORS请求
CheckOrigin: func(r *http.Request) bool {
return true
},
}
// 将HTTP连接升级为WebSocket连接
conn, err := upgrader.Upgrade(ctx.Writer, ctx.Request, nil)
if err != nil {
// utils.Fail(ctx, 500, "升级连接失败", err)
return
}
defer conn.Close()
log.Printf("客户端已连接: %s", ctx.Request.RemoteAddr)
// 生成唯一客户端ID
clientID := "websocket-" + strconv.FormatInt(time.Now().UnixNano(), 10)
// 创建新连接对象
connection := &Connection{
ID: clientID,
WebSocket: conn,
LastSeen: time.Now(),
IP: ctx.ClientIP(),
}
// 添加到连接管理器
connectionManager.Store(clientID, connection)
log.Printf("新客户端已连接 (ID: %s, IP: %s)", clientID, connection.IP)
// 向客户端发送欢迎消息,包含其ID
if err := conn.WriteMessage(websocket.TextMessage, []byte("欢迎连接到WebSocket服务器! 您的ID: "+clientID)); err != nil {
log.Println("写入欢迎消息失败:", err)
return
}
// 持续读取客户端消息
for {
// 更新最后活跃时间
connection.LastSeen = time.Now()
// 读取消息
messageType, message, err := conn.ReadMessage()
if err != nil {
log.Printf("客户端 (ID: %s) 读取消息错误: %v", clientID, err)
break
}
// 记录收到的消息
log.Printf("从客户端 (ID: %s) 收到消息: %s", clientID, message)
// 将消息发送回客户端(回显),添加ID前缀
response := []byte("[" + clientID + "] " + string(message))
if err := conn.WriteMessage(messageType, response); err != nil {
log.Printf("向客户端 (ID: %s) 写入消息失败: %v", clientID, err)
break
}
}
// 从连接管理器中移除
connectionManager.Delete(clientID)
log.Printf("客户端断开连接 (ID: %s, IP: %s)", clientID, connection.IP)
}
// ListConnections 获取所有连接列表
func ListConnections() []*Connection {
var connections []*Connection
connectionManager.Range(func(key, value any) bool {
if conn, ok := value.(*Connection); ok {
connections = append(connections, conn)
}
return true
})
return connections
}
// GetConnection 获取指定ID的连接
func GetConnection(clientID string) (*Connection, bool) {
value, exists := connectionManager.Load(clientID)
if !exists {
return nil, false
}
conn, ok := value.(*Connection)
return conn, ok
}
3. 前端Vue实现WebSocket客户端
onMounted(() => {
startWebSocket()
})
onUnmounted(() => {
stopWebSocket()
})
let ws:WebSocket
const startWebSocket = () => {
ws = new WebSocket('ws://127.0.0.1:9000/ws/connect')
ws.onopen = () => {
console.log('WebSocket连接成功')
// 在连接建立后,可以发送消息到服务器
sendWebSocketMessage('Hello, WebSocket!')
}
ws.onmessage = (event) => {
console.log('接收到消息:', event.data)
}
ws.onclose = () => {
console.log('WebSocket连接关闭')
// 连接关闭后,重新连接
setTimeout(startWebSocket, 1000)
}
ws.onerror = (error) => {
console.error('WebSocket错误:', error)
}
}
const stopWebSocket = () => {
// 主动关闭WebSocket连接
ws && ws.close()
}
const sendWebSocketMessage = (message:string | ArrayBufferLike | Blob | ArrayBufferView) => {
// 向服务端发送消息
ws && ws.send(message)
}
4. 关键逻辑说明
服务端
连接升级:通过gorilla/websocket库的Upgrader将 HTTP 请求升级为 WebSocket 协议,CheckOrigin允许跨域访问。
连接管理:使用sync.Map存储客户端连接,保证并发安全,包含 ID、连接对象、最后活跃时间和 IP 信息。
消息处理:循环读取客户端消息并回显,同时更新连接的活跃时间,断开时自动从连接池中移除。
客户端
创建WebSocket连接:使用new WebSocket(url)构造函数创建一个新的WebSocket对象,其中url是WebSocket服务器的地址。
设置事件处理程序:为WebSocket对象设置各种事件处理程序,如onopen、onmessage、onerror和onclose。
发送消息:当WebSocket连接成功建立后(即onopen事件触发时),客户端可以通过调用send方法发送消息。
接收消息:当服务器发送消息时(即onmessage事件触发时),客户端可以接收消息。
关闭连接:当不再需要WebSocket连接时,可以调用close方法关闭连接。
二、SSE(Server-Sent Events)通信实现解析
1. 核心概念与优势
SSE 是基于 HTTP 的单向通信协议,由服务器主动向客户端推送数据,适合新闻推送、实时通知等场景。其优势在于简单易用,无需复杂的连接管理,且支持自动重连。
2. Go 语言 Gin 框架实现代码
package controllers
import (
"log"
"net/http"
"strconv"
"sync"
"time"
"github.com/gin-gonic/gin"
)
// SSEClient 表示一个SSE客户端连接
type SSEClient struct {
ID string
Writer http.ResponseWriter
IP string
Connected time.Time
}
// 全局客户端连接管理器
var sseClients = sync.Map{}
func HandleSSE(ctx *gin.Context) {
// 设置响应头
ctx.Writer.Header().Set("Content-Type", "text/event-stream")
ctx.Writer.Header().Set("Cache-Control", "no-cache")
ctx.Writer.Header().Set("Connection", "keep-alive")
// 生成唯一客户端ID
clientID := "sse-" + strconv.FormatInt(time.Now().UnixNano(), 10)
// 创建新客户端连接
client := &SSEClient{
ID: clientID,
Writer: ctx.Writer,
IP: ctx.ClientIP(),
Connected: time.Now(),
}
// 添加到连接管理器
sseClients.Store(clientID, client)
log.Printf("新SSE客户端已连接 (ID: %s, IP: %s)", clientID, client.IP)
// 发送欢迎消息
ctx.SSEvent("welcome", gin.H{"clientID": clientID})
// ctx.Writer.Write([]byte("data: 欢迎使用SSE服务!\n\n"))
ctx.Writer.Flush()
// 监听客户端断开事件
notify := ctx.Writer.CloseNotify()
// 定时发送当前时间
ticker := time.NewTicker(2 * time.Second)
defer ticker.Stop()
for {
select {
case <-notify:
// 客户端断开连接,从管理器中移除
sseClients.Delete(clientID)
log.Printf("SSE客户端断开连接 (ID: %s, IP: %s)", clientID, client.IP)
case <-ticker.C:
// 发送当前时间
ctx.SSEvent("message", gin.H{"time": time.Now().Format("2006-01-02 15:04:05")})
// ctx.Writer.Write([]byte("data: " + time.Now().Format("2006-01-02 15:04:05") + "\n\n"))
ctx.Writer.Flush()
}
}
}
// 获取当前所有连接的客户端
func GetAllClients() []*SSEClient {
var clients []*SSEClient
sseClients.Range(func(key, value interface{}) bool {
if client, ok := value.(*SSEClient); ok {
clients = append(clients, client)
}
return true
})
return clients
}
// 获取指定ID的连接
func GetSSEClient(clientID string) (*SSEClient, bool) {
value, exists := sseClients.Load(clientID)
if !exists {
return nil, false
}
conn, ok := value.(*SSEClient)
return conn, ok
}
3. 前端Vue实现SSE客户端
onMounted(() => {
startSSE()
})
onUnmounted(() => {
stopSSE()
})
let sse:EventSource
const startSSE = () => {
sse = new EventSource('http://127.0.0.1:9000/sse/connect')
sse.onopen = () => {
console.log('SSE连接成功')
}
sse.onmessage = (event) => {
console.log('接收到消息:', event.data)
}
sse.onerror = (error) => {
console.error('SSE错误:', error)
}
}
const stopSSE = () => {
// 主动关闭SSE连接
sse && sse.close()
}
4. 关键逻辑说明
响应头设置:必须设置Content-Type: text/event-stream,禁用缓存并保持长连接。
连接管理:使用sync.Map存储客户端的ResponseWriter,定时任务每秒推送当前时间。
断开处理:通过CloseNotify监听客户端断开事件,自动移除连接。
三、技术对比与适用场景
| 特性 | WebSocket | SSE |
|---|---|---|
| 通信方向 | 全双工 (双向) | 单工 (服务器→客户端) |
| 协议基础 | 基于 TCP,独立协议(ws/wss) | 基于 HTTP |
| 典型场景 | 实时聊天、在线协作、游戏 | 新闻推送、系统通知、日志监控 |
| 连接管理 | 需要手动处理心跳、重连 | 自动重连,服务端单向维护 |
| 数据格式 | 支持文本 / 二进制,灵活性高 | 仅限文本格式(JSON / 纯文本) |
| 跨域处理 | 需要服务端显式配置CheckOrigin | 依赖 HTTP 响应头Access-Control |







文章有(9)条网友点评
generic cialis images
generic cialis images
minoxidil extra strength
minoxidil extra strength
terbinafine safety information
terbinafine safety information
ivermectin interactions FAQ
ivermectin interactions FAQ
minoxidil 2% vs 5% results
minoxidil 2% vs 5% results
minoxidil foam vs liquid results
minoxidil foam vs liquid results
minoxidil mechanism deep dive
minoxidil mechanism deep dive
orlistat food impact
orlistat food impact
sinusitis ent overview
sinusitis ent overview