SSE Server-Side Real-time Push
Overview
HttpServer has built-in SSE (Server-Sent Events) support, enabling server-to-client real-time message push. Suitable for:
- Real-time notifications
- Online chat
- Data monitoring
- Progress push
Client Request
Client connects using cSSEClient:
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 "Received: " & EventName & " = " & Data
End SubServer Implementation
Basic Push
' Push message in controller
Public Sub Events(ctx As cHttpServerContext)
' Use SendPack for quick send
ctx.SSE.SendPack "message", "Hello World", ctx.ClientInfo.hSocket
' Send JSON data
Dim data As New Dictionary
data("time") = Now
data("status") = "ok"
ctx.SSE.SendPack "data", Json.Encode(data), ctx.ClientInfo.hSocket
' Use Data + Send for custom data structure
ctx.SSE.Data.Add "type", "alert"
ctx.SSE.Data.Add "message", "System maintenance notice"
ctx.SSE.Send ctx.ClientInfo.hSocket
End SubBroadcast Message
' cNotificationController.cls
' Send to all connections (omit hSocket for broadcast)
Public Sub Broadcast(ctx As cHttpServerContext)
Dim msg As String
msg = ctx.Request.Form("message")
' Broadcast to all SSE clients
ctx.SSE.SendPack "notification", msg
ctx.Response.Json Nothing, 0, "Broadcast sent"
End Sub
' Send to specific user
Public Sub SendToUser(ctx As cHttpServerContext)
Dim userId As String, msg As String
userId = ctx.Request.Form("user_id")
msg = ctx.Request.Form("message")
' Chain call to send to specific user
ctx.SSE.ToUser(userId).SendPack "private", msg
ctx.Response.Json Nothing, 0, "Sent"
End SubReal-time Data Stream
Note: SSE is an event-driven model, not suitable for
Do Whilepolling loops in controllers. Use a timer (e.g., cTimer) to periodically push data instead of blocking loops.
' cMonitorController.cls
' Push triggered by timer (recommended approach)
Public Sub PushStats(ctx As cHttpServerContext)
' Collect system data
Dim data As New Dictionary
data("cpu") = GetCPUUsage()
data("memory") = GetMemoryUsage()
data("time") = Now
' Push to all online SSE clients
ctx.SSE.SendPack "stats", Json.Encode(data)
End SubProgress Push
' cTaskController.cls
' Push progress triggered by timer or callback
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
' Push to requesting client
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 SubComplete Chat Example
' ========== Server ==========
' cChatController.cls
Option Explicit
' GET /chat/stream
Public Sub Stream(ctx As cHttpServerContext)
' Check login
If Not ctx.Session.Exists("user_id") Then
ctx.Response.State401 "Please login first"
Exit Sub
End If
' Register to chat room (SSE connection managed by framework, no manual headers needed)
Dim userId As String, username As String
userId = ctx.Session("user_id")
username = ctx.Session("username")
' Bind user (can push by username later)
ctx.SSE.BindUser ctx.ClientInfo.hSocket, userId
' Send welcome message
ctx.SSE.SendPack "system", "{\"msg\":\"Welcome " & username & " to the chat room\"}", 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")
' Broadcast to all users (omit hSocket for broadcast)
Dim data As String
data = "{\"user\":\"" & username & "\",\"msg\":\"" & msg & "\"}"
ctx.SSE.SendPack "message", data
ctx.Response.Json Nothing, 0, "Sent"
End Sub
' ========== Client ==========
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"
' Show system message
ShowSystemMessage Data
Case "message"
' Show chat message
ShowChatMessage Data
End Select
End SubServer API Reference
Start
Start SSE service, bind path and event handler.
Note:
Startis a method of thecSSEclass, only called during service startup. It cannot be called viactx.SSE(cSSEContext) in controllers.
Public Function Start(Optional MatchPath As String, Optional EventsHandler As Object) As BooleanParameters:
MatchPath- Request path to match (empty matches all)EventsHandler- Event handler object, must implementOnConnect(Context)andOnClose(Context)methods
Example:
' Start SSE, listen on /events path
Set SSE = New cSSE
SSE.Start "/events", Me ' Me implements OnConnect/OnClose
' In event handler
Public Sub OnConnect(ctx As cHttpServerContext)
Debug.Print "SSE client connected: " & ctx.ClientInfo.IP
End Sub
Public Sub OnClose(ctx As cHttpServerContext)
Debug.Print "SSE client disconnected: " & ctx.ClientInfo.IP
End SubSend / SendPack
Send message to SSE client.
' Custom data send
Public Function Send(Optional ByVal hSocket As Long) As Boolean
' Quick pack send (action + content format)
Public Function SendPack(Action As String, Content As Variant, Optional hSocket As Long) As BooleanDescription: Send uses the shared Data dictionary to build message body, SendPack auto-wraps {"action":..., "content":...} format. Both support specifying hSocket to send to a specific client, or omit (target set by ToUser/ToGroup chain).
Send Reentry Safety: The Send method snapshots the shared Data to a local variable before encoding, so even if reentry calls are triggered during sending, data corruption will not occur.
Example:
' Use Send for custom data
SSE.Data.Add "type", "alert"
SSE.Data.Add "message", "System maintenance notice"
SSE.Send hSocket
' Use SendPack for quick send
SSE.SendPack "notification", "You have a new message", hSocket
' Chain call to send to specific user
SSE.ToUser("admin").SendPack "private", "Welcome back"CloseClient
Close specified SSE client connection.
Public Function CloseClient(Optional ByVal hSocket As Long) As BooleanDescription: Whether called via parameter or ToUser chain, performs complete cleanup:
- Trigger
OnCloseuser event - Clear SSE context references
- Remove from Clients dictionary
- Clean up user/group mappings
- Close Socket connection, triggering cClientCallback's complete cleanup flow
Example:
' Close specified client
SSE.CloseClient hSocket
' Chain close specified user
SSE.ToUser("admin").CloseClientBindUser / GetUser / GetClientByUser
Manage user-to-Socket mapping.
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 LongExample:
' Bind user
SSE.BindUser hSocket, "user_123"
' Find socket by username
Dim sock As Long
sock = SSE.GetClientByUser("user_123")
' Find username by socket
Dim user As String
user = SSE.GetUser(hSocket)ToUser / ToGroup
Chain call to specify send target.
Public Function ToUser(ByVal User As String) As cSSEContext
Public Function ToGroup(ByVal Name As String) As cSSEContextDescription: Returns cSSEContext itself while setting the internal target hSocket. Subsequent calls to Send/SendPack/CloseClient don't need to pass hSocket.
Example:
' Send to specific user
ctx.SSE.ToUser("admin").SendPack "msg", "Hello Admin"
' Close specific user connection
ctx.SSE.ToUser("admin").CloseClientClientCount
Current online SSE client count (read-only).
Public Property Get ClientCount() As LongExample:
Debug.Print "Online SSE clients: " & SSE.ClientCountLast Updated: 2026-06-13