Skip to content

SSE 服务器端实时推送

简介

HttpServer 内置 SSE (Server-Sent Events) 支持,可实现服务器向客户端实时推送消息。适用于:

  • 实时通知
  • 在线聊天
  • 数据监控
  • 进度推送

客户端请求

客户端使用 cSSEClient 连接:

vb
Private WithEvents SSE As cSSEClient

Private Sub Connect()
    Set SSE = New cSSEClient
    SSE.Connect "http://localhost:8080/events"
End Sub

Private Sub SSE_OnMessage(EventName As String, Data As String, Id As String)
    Debug.Print "收到: " & EventName & " = " & Data
End Sub

服务端实现

基础推送

vb
' 在控制器中推送消息
Public Sub Events(ctx As cHttpServerContext)
    ' 使用 SendPack 快捷发送
    ctx.SSE.SendPack "message", "Hello World", ctx.ClientInfo.hSocket

    ' 发送 JSON 数据
    Dim data As New Dictionary
    data("time") = Now
    data("status") = "ok"
    ctx.SSE.SendPack "data", Json.Encode(data), ctx.ClientInfo.hSocket

    ' 使用 Data + Send 自定义数据结构
    ctx.SSE.Data.Add "type", "alert"
    ctx.SSE.Data.Add "message", "系统维护通知"
    ctx.SSE.Send ctx.ClientInfo.hSocket
End Sub

广播消息

vb
' cNotificationController.cls

' 发送给所有连接(不传 hSocket 即广播)
Public Sub Broadcast(ctx As cHttpServerContext)
    Dim msg As String
    msg = ctx.Request.Form("message")

    ' 广播到所有 SSE 客户端
    ctx.SSE.SendPack "notification", msg

    ctx.Response.Json Nothing, 0, "已广播"
End Sub

' 发送给指定用户
Public Sub SendToUser(ctx As cHttpServerContext)
    Dim userId As String, msg As String
    userId = ctx.Request.Form("user_id")
    msg = ctx.Request.Form("message")

    ' 通过链式调用发送给指定用户
    ctx.SSE.ToUser(userId).SendPack "private", msg
    ctx.Response.Json Nothing, 0, "已发送"
End Sub

实时数据流

注意: SSE 是事件驱动模式,不适合在控制器中使用 Do While 循环轮询。 应使用定时器(如 cTimer)定期推送数据,而非阻塞式循环。

vb
' cMonitorController.cls
' 由定时器触发推送(推荐方式)
Public Sub PushStats(ctx As cHttpServerContext)
    ' 收集系统数据
    Dim data As New Dictionary
    data("cpu") = GetCPUUsage()
    data("memory") = GetMemoryUsage()
    data("time") = Now

    ' 推送给所有在线 SSE 客户端
    ctx.SSE.SendPack "stats", Json.Encode(data)
End Sub

进度推送

vb
' cTaskController.cls
' 由定时器或回调触发推送进度
Public Sub PushProgress(ctx As cHttpServerContext)
    Dim taskId As String
    taskId = ctx.Request.Form("task_id")
    Dim percent As Long
    percent = CLng(ctx.Request.Form("percent"))

    Dim progress As New Dictionary
    progress("percent") = percent
    progress("task_id") = taskId

    ' 推送给请求的客户端
    ctx.SSE.SendPack "progress", Json.Encode(progress), ctx.ClientInfo.hSocket
End Sub

Public Sub PushComplete(ctx As cHttpServerContext)
    ctx.SSE.SendPack "complete", "{\"status\":\"done\"}", ctx.ClientInfo.hSocket
End Sub

完整聊天示例

vb
' ========== 服务端 ==========

' cChatController.cls
Option Explicit

' GET /chat/stream
Public Sub Stream(ctx As cHttpServerContext)
    ' 检查登录
    If Not ctx.Session.Exists("user_id") Then
        ctx.Response.State401 "请先登录"
        Exit Sub
    End If

    ' 注册到聊天室(SSE 连接由框架自动管理,无需手动设置头)
    Dim userId As String, username As String
    userId = ctx.Session("user_id")
    username = ctx.Session("username")

    ' 绑定用户(后续可按用户名推送)
    ctx.SSE.BindUser ctx.ClientInfo.hSocket, userId

    ' 发送欢迎消息
    ctx.SSE.SendPack "system", "{\"msg\":\"欢迎 " & username & " 加入聊天室\"}", ctx.ClientInfo.hSocket
End Sub

' POST /chat/send
Public Sub SendMsg(ctx As cHttpServerContext)
    Dim msg As String
    msg = ctx.Request.Form("message")

    Dim username As String
    username = ctx.Session("username")

    ' 广播给所有用户(不传 hSocket 即广播)
    Dim data As String
    data = "{\"user\":\"" & username & "\",\"msg\":\"" & msg & "\"}"

    ctx.SSE.SendPack "message", data

    ctx.Response.Json Nothing, 0, "已发送"
End Sub

' ========== 客户端 ==========

Private WithEvents SSE As cSSEClient

Private Sub JoinChat()
    Set SSE = New cSSEClient
    SSE.Connect "http://localhost:8080/chat/stream"
End Sub

Private Sub SendMessage(msg As String)
    Dim http As New cHttpClient
    http.RequestDataForm("message") = msg
    http.SendPost "http://localhost:8080/chat/send"
End Sub

Private Sub SSE_OnMessage(EventName As String, Data As String, Id As String)
    Select Case EventName
        Case "system"
            ' 显示系统消息
            ShowSystemMessage Data
        Case "message"
            ' 显示聊天消息
            ShowChatMessage Data
    End Select
End Sub

服务端 API 参考

Start

启动 SSE 服务,绑定路径和事件处理器。

注意: StartcSSE 类的方法,仅在服务启动时调用。控制器中通过 ctx.SSE(cSSEContext)无法调用此方法。

vb
Public Function Start(Optional MatchPath As String, Optional EventsHandler As Object) As Boolean

参数:

  • MatchPath - 匹配的请求路径(为空则匹配所有)
  • EventsHandler - 事件处理器对象,需实现 OnConnect(Context)OnClose(Context) 方法

示例:

vb
' 启动 SSE,监听 /events 路径
Set SSE = New cSSE
SSE.Start "/events", Me  ' Me 实现了 OnConnect/OnClose

' 在事件处理器中
Public Sub OnConnect(ctx As cHttpServerContext)
    Debug.Print "SSE 客户端连接: " & ctx.ClientInfo.IP
End Sub

Public Sub OnClose(ctx As cHttpServerContext)
    Debug.Print "SSE 客户端断开: " & ctx.ClientInfo.IP
End Sub

Send / SendPack

发送消息给 SSE 客户端。

vb
' 自定义数据发送
Public Function Send(Optional ByVal hSocket As Long) As Boolean

' 快捷打包发送(action + content 格式)
Public Function SendPack(Action As String, Content As Variant, Optional hSocket As Long) As Boolean

说明: Send 使用共享 Data 字典构建消息体,SendPack 自动封装 {"action":..., "content":...} 格式。两者都支持指定 hSocket 发送给特定客户端,或省略(由 ToUser/ToGroup 链式设定目标)。

Send 重入安全: Send 方法在编码前会将共享 Data 快照到局部变量,发送过程中即使触发重入调用也不会导致数据错乱。

示例:

vb
' 使用 Send 发送自定义数据
SSE.Data.Add "type", "alert"
SSE.Data.Add "message", "系统维护通知"
SSE.Send hSocket

' 使用 SendPack 快捷发送
SSE.SendPack "notification", "您有新消息", hSocket

' 链式调用发送给指定用户
SSE.ToUser("admin").SendPack "private", "欢迎回来"

CloseClient

关闭指定 SSE 客户端连接。

vb
Public Function CloseClient(Optional ByVal hSocket As Long) As Boolean

说明: 无论通过参数还是 ToUser 链式调用,都会执行完整清理流程:

  1. 触发 OnClose 用户事件
  2. 清除 SSE 上下文引用
  3. 从 Clients 字典移除
  4. 清理用户/分组映射
  5. 关闭 Socket 连接,触发 cClientCallback 的完整清理流程

示例:

vb
' 关闭指定客户端
SSE.CloseClient hSocket

' 链式关闭指定用户
SSE.ToUser("admin").CloseClient

BindUser / GetUser / GetClientByUser

管理用户与 Socket 的映射关系。

vb
Public Function BindUser(ByVal hSocket As Long, ByVal User As String) As Boolean
Public Function GetUser(ByVal hSocket As Long) As String
Public Function GetClientByUser(ByVal User As String) As Long

示例:

vb
' 绑定用户
SSE.BindUser hSocket, "user_123"

' 根据用户名查找 Socket
Dim sock As Long
sock = SSE.GetClientByUser("user_123")

' 根据 Socket 查找用户名
Dim user As String
user = SSE.GetUser(hSocket)

ToUser / ToGroup

链式调用指定发送目标。

vb
Public Function ToUser(ByVal User As String) As cSSEContext
Public Function ToGroup(ByVal Name As String) As cSSEContext

说明: 返回 cSSEContext 自身,同时设置内部目标 hSocket。后续调用 Send/SendPack/CloseClient 时无需再传 hSocket。

示例:

vb
' 发送给指定用户
ctx.SSE.ToUser("admin").SendPack "msg", "Hello Admin"

' 关闭指定用户连接
ctx.SSE.ToUser("admin").CloseClient

ClientCount

当前在线 SSE 客户端数量(只读)。

vb
Public Property Get ClientCount() As Long

示例:

vb
Debug.Print "在线 SSE 客户端: " & SSE.ClientCount

最后更新: 2026-06-13

VB6及其LOGO版权为微软公司所有