Skip to content

WebSocket 开发文档

目录


概述

WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议。新版 WebSocket 类库提供了分离的客户端和服务端实现,支持文本和二进制消息传输,具有高效的缓冲区管理和完善的错误处理机制。

主要特性

  • 分离架构 - 客户端 (cWebSocketClient) 和服务端 (cWebSocketServer) 独立实现
  • 协议支持 - 完整实现 WebSocket 协议 (RFC 6455)
  • 消息类型 - 支持文本消息和二进制消息
  • 帧解析器 - cWebSocketFrame 类专门处理帧解析和构建
  • 高效缓冲 - cByteBuffer 类提供预分配和自动增长策略
  • 事件驱动 - 通过事件机制处理所有网络事件
  • 广播功能 - 服务端支持向所有客户端广播消息

项目结构

Websocket/
├── Client/
│   ├── frmClient.frm      # 客户端窗体
│   ├── WsClient.vbp       # 客户端项目
│   └── WsClient.vbw
├── Server/
│   ├── frmServer.frm      # 服务端窗体
│   ├── WsServer.vbp       # 服务端项目
│   └── WsServer.vbw
└── README.md              # 示例说明

类库架构

newWebsocket/
├── mWebSocketUtils.bas           # 公共工具模块 (UTF-8, Base64, SHA1, 枚举定义)
├── cByteBuffer.cls               # 高效字节缓冲区:预分配、自动增长、最小内存操作
├── cWebSocketFrame.cls           # WebSocket 帧解析器:只读解析、分离的unmask操作
├── cWebSocketClient.cls          # WebSocket 客户端
├── cWebSocketServer.cls          # WebSocket 服务端
└── cWebSocketServerClient.cls    # 服务端客户端连接信息

快速开始

环境要求

  • Visual Basic 6.0 或更高版本
  • VBMAN.dll 库文件(位于 ..\..\..\vbman\dist\DLL\

引用库

  1. 打开项目 WsClient.vbpWsServer.vbp
  2. 确保已引用 VBMANLIB
  3. 检查引用路径:..\..\..\vbman\dist\DLL\VBMAN.dll

运行示例

  1. 启动服务端

    • 打开 Server/WsServer.vbp
    • 运行项目
    • 点击"启动服务"按钮
    • 默认监听端口 8080
  2. 启动客户端

    • 打开 Client/WsClient.vbp
    • 运行项目
    • 输入服务器地址 ws://127.0.0.1:8080
    • 点击"连接"按钮
    • 连接成功后可发送消息
  3. 测试通信

    • 在客户端输入消息并点击"发送"
    • 服务端会回显消息
    • 发送 "broadcast" 可触发服务端广播功能

WebSocket 客户端

客户端

创建客户端

vb
Private WithEvents m_WsClient As cWebSocketClient

Private Sub InitializeClient()
    If m_WsClient Is Nothing Then
        Set m_WsClient = New cWebSocketClient
    End If
End Sub

连接到服务器

vb
Private Sub ConnectToServer(ByVal sURL As String)
    On Error GoTo EH
    
    InitializeClient()
    m_WsClient.Connect sURL
    
    LogMessage "正在连接到: " & sURL
    Exit Sub

EH:
    LogMessage "连接失败: " & Err.Description
End Sub

参数说明:

  • sURL: WebSocket 服务器 URL
    • 格式: ws://host:portwss://host:port (加密)
    • 示例: ws://127.0.0.1:8080

发送文本消息

vb
Private Sub SendTextMessage(sMessage As String)
    On Error GoTo EH
    
    If Not m_WsClient Is Nothing And m_WsClient.State = WS_STATE_OPEN Then
        m_WsClient.SendText sMessage
        LogMessage "已发送: " & sMessage
    End If
    Exit Sub

EH:
    LogMessage "发送失败: " & Err.Description
End Sub

发送二进制消息

vb
Private Sub SendBinaryMessage(Data() As Byte)
    On Error GoTo EH
    
    If Not m_WsClient Is Nothing And m_WsClient.State = WS_STATE_OPEN Then
        m_WsClient.SendBinary Data
        LogMessage "已发送二进制数据: " & (UBound(Data) + 1) & " 字节"
    End If
    Exit Sub

EH:
    LogMessage "发送失败: " & Err.Description
End Sub

发送 Ping 帧

vb
Private Sub SendPing(Optional Data() As Byte)
    On Error GoTo EH
    
    If Not m_WsClient Is Nothing And m_WsClient.State = WS_STATE_OPEN Then
        m_WsClient.Ping Data
        LogMessage "已发送 Ping"
    End If
    Exit Sub

EH:
    LogMessage "发送 Ping 失败: " & Err.Description
End Sub

断开连接

vb
Private Sub Disconnect(Optional ByVal Code As WsCloseCode = WS_CLOSE_NORMAL, Optional ByVal Reason As String = "")
    If Not m_WsClient Is Nothing Then
        m_WsClient.CloseConnection Code, Reason
        LogMessage "已断开连接"
    End If
End Sub

关闭码说明:

  • WS_CLOSE_NORMAL (1000): 正常关闭
  • WS_CLOSE_GOING_AWAY (1001): 端点离开
  • WS_CLOSE_PROTOCOL_ERROR (1002): 协议错误
  • WS_CLOSE_UNSUPPORTED (1003): 不支持的数据类型
  • WS_CLOSE_NO_STATUS (1005): 无状态码
  • WS_CLOSE_ABNORMAL (1006): 异常关闭
  • WS_CLOSE_INVALID_PAYLOAD (1007): 无效数据
  • WS_CLOSE_POLICY_VIOLATION (1008): 策略违规
  • WS_CLOSE_MESSAGE_TOO_BIG (1009): 消息过大
  • WS_CLOSE_MANDATORY_EXT (1010): 缺少扩展
  • WS_CLOSE_INTERNAL_ERROR (1011): 内部错误
  • WS_CLOSE_TLS_HANDSHAKE (1015): TLS 握手失败

完整示例

参考 Client/frmClient.frm 文件:

vb
Private Sub cmdConnect_Click()
    On Error GoTo EH
    
    If m_WsClient Is Nothing Then
        Set m_WsClient = New cWebSocketClient
    End If
    
    m_WsClient.Connect txtURL.Text
    
    LogMessage "正在连接到: " & txtURL.Text
    cmdConnect.Enabled = False
    Exit Sub
EH:
    LogMessage "连接失败: " & Err.Description
End Sub

Private Sub cmdSend_Click()
    On Error GoTo EH

    If Not m_WsClient Is Nothing And m_WsClient.State = WS_STATE_OPEN Then
        m_WsClient.SendText txtMessage.Text
        LogMessage "已发送: " & txtMessage.Text
    End If
    Exit Sub
EH:
    LogMessage "发送失败: " & Err.Description
End Sub

Private Sub UpdateUI(bConnected As Boolean)
    cmdConnect.Enabled = Not bConnected
    cmdDisconnect.Enabled = bConnected
    cmdSend.Enabled = bConnected
End Sub

WebSocket 服务器

服务器

创建服务器

vb
Private WithEvents m_WsServer As cWebSocketServer

Private Sub InitializeServer()
    If m_WsServer Is Nothing Then
        Set m_WsServer = New cWebSocketServer
    End If
End Sub

启动监听

vb
Private Sub StartServer(ByVal lPort As Long)
    On Error GoTo EH
    
    InitializeServer()
    m_WsServer.Listen lPort
    
    LogMessage "WebSocket 服务器已启动,监听端口: " & lPort
    Exit Sub

EH:
    LogMessage "启动服务失败: " & Err.Description
End Sub

停止服务

vb
Private Sub StopServer()
    If Not m_WsServer Is Nothing Then
        m_WsServer.StopServer
        LogMessage "WebSocket 服务器已停止"
    End If
End Sub

发送消息给指定客户端

vb
Private Sub SendToClient(ByVal ClientID As String, sMessage As String)
    On Error GoTo EH
    
    m_WsServer.SendText ClientID, sMessage
    LogMessage "已向客户端 " & ClientID & " 发送消息: " & sMessage
    Exit Sub

EH:
    LogMessage "发送失败: " & Err.Description
End Sub

发送二进制消息给指定客户端

vb
Private Sub SendBinaryToClient(ByVal ClientID As String, Data() As Byte)
    On Error GoTo EH
    
    m_WsServer.SendBinary ClientID, Data
    LogMessage "已向客户端 " & ClientID & " 发送二进制数据: " & (UBound(Data) + 1) & " 字节"
    Exit Sub

EH:
    LogMessage "发送失败: " & Err.Description
End Sub

广播消息

vb
Private Sub BroadcastMessage(sMessage As String, Optional ByVal ExcludeClientID As String = "")
    On Error GoTo EH
    
    m_WsServer.BroadcastText sMessage, ExcludeClientID
    LogMessage "已广播消息: " & sMessage
    Exit Sub

EH:
    LogMessage "广播失败: " & Err.Description
End Sub

参数说明:

  • sMessage: 要广播的消息
  • ExcludeClientID: 可选,排除指定的客户端 ID(不接收广播)

广播二进制消息

vb
Private Sub BroadcastBinaryMessage(Data() As Byte, Optional ByVal ExcludeClientID As String = "")
    On Error GoTo EH
    
    m_WsServer.BroadcastBinary Data, ExcludeClientID
    LogMessage "已广播二进制数据: " & (UBound(Data) + 1) & " 字节"
    Exit Sub

EH:
    LogMessage "广播失败: " & Err.Description
End Sub

断开指定客户端

vb
Private Sub DisconnectClient(ByVal ClientID As String, Optional ByVal Code As WsCloseCode = WS_CLOSE_NORMAL, Optional ByVal Reason As String = "")
    On Error GoTo EH
    
    m_WsServer.DisconnectClient ClientID, Code, Reason
    LogMessage "已断开客户端 " & ClientID
    Exit Sub

EH:
    LogMessage "断开客户端失败: " & Err.Description
End Sub

获取客户端信息

vb
Private Sub GetClientInfo(ByVal ClientID As String)
    Dim oClient As cWebSocketServerClient
    
    Set oClient = m_WsServer.GetClient(ClientID)
    
    If Not oClient Is Nothing Then
        LogMessage "客户端 ID: " & oClient.ID
        LogMessage "远程地址: " & oClient.RemoteAddress
        LogMessage "远程端口: " & oClient.RemotePort
        LogMessage "连接时间: " & oClient.ConnectedTime
    End If
End Sub

完整示例

参考 Server/frmServer.frm 文件:

vb
Private Sub cmdStart_Click()
    On Error GoTo EH

    If m_WsServer Is Nothing Then
        Set m_WsServer = New cWebSocketServer
    End If

    m_WsServer.Listen CLng(txtPort.Text)

    LogMessage "WebSocket 服务器已启动,监听端口: " & txtPort.Text
    cmdStart.Enabled = False
    cmdStop.Enabled = True
    Exit Sub
EH:
    LogMessage "启动服务失败: " & Err.Description
End Sub

Private Sub m_WsServer_OnClientTextMessage(ByVal ClientID As String, ByVal Message As String)
    LogMessage "收到来自 " & ClientID & " 的文本消息: " & Message

    ' 回显消息
    m_WsServer.SendText ClientID, "服务器收到: " & Message

    ' 如果消息是 "broadcast",则广播给所有客户端
    If LCase$(Message) = "broadcast" Then
        m_WsServer.BroadcastText "这是一条广播消息,来自客户端 " & ClientID, ClientID
        LogMessage "已向所有客户端发送广播消息"
    End If
End Sub

API 参考

cWebSocketClient 属性

属性名类型说明
StateWsState连接状态(参见状态常量)
URLString连接的 WebSocket URL
ReadyStateWsStateReadyState 属性(同 State)

cWebSocketClient 方法

Connect

vb
m_WsClient.Connect URL

连接到 WebSocket 服务器。

参数:

  • URL: WebSocket 服务器 URL

SendText

vb
m_WsClient.SendText Message

发送文本消息。

参数:

  • Message: 文本消息内容

SendBinary

vb
m_WsClient.SendBinary Data()

发送二进制消息。

参数:

  • Data: 字节数组

Ping

vb
m_WsClient.Ping [Data()]

发送 Ping 帧。

参数:

  • Data: 可选,附加的数据

CloseConnection

vb
m_WsClient.CloseConnection [Code], [Reason]

关闭连接。

参数:

  • Code: 可选,关闭码(默认 WS_CLOSE_NORMAL)
  • Reason: 可选,关闭原因

cWebSocketServer 方法

Listen

vb
m_WsServer.Listen Port

开始监听指定端口。

参数:

  • Port: 监听端口号

StopServer

vb
m_WsServer.StopServer

停止服务并断开所有客户端。

SendText

vb
m_WsServer.SendText ClientID, Message

发送文本消息给指定客户端。

参数:

  • ClientID: 客户端 ID
  • Message: 文本消息内容

SendBinary

vb
m_WsServer.SendBinary ClientID, Data()

发送二进制消息给指定客户端。

参数:

  • ClientID: 客户端 ID
  • Data: 字节数组

BroadcastText

vb
m_WsServer.BroadcastText Message, [ExcludeClientID]

向所有客户端广播文本消息。

参数:

  • Message: 文本消息内容
  • ExcludeClientID: 可选,排除的客户端 ID

BroadcastBinary

vb
m_WsServer.BroadcastBinary Data(), [ExcludeClientID]

向所有客户端广播二进制消息。

参数:

  • Data: 字节数组
  • ExcludeClientID: 可选,排除的客户端 ID

DisconnectClient

vb
m_WsServer.DisconnectClient ClientID, [Code], [Reason]

断开指定客户端。

参数:

  • ClientID: 客户端 ID
  • Code: 可选,关闭码
  • Reason: 可选,关闭原因

GetClient

vb
Set oClient = m_WsServer.GetClient(ClientID)

获取客户端对象。

参数:

  • ClientID: 客户端 ID

返回: cWebSocketServerClient 对象

状态常量

常量说明
WS_STATE_CONNECTING0正在连接
WS_STATE_OPEN1已连接
WS_STATE_CLOSING2正在关闭
WS_STATE_CLOSED3已关闭

事件说明

客户端事件

OnOpen

vb
Private Sub m_WsClient_OnOpen()

连接成功建立时触发。

OnClose

vb
Private Sub m_WsClient_OnClose(ByVal Code As WsCloseCode, ByVal Reason As String)

连接关闭时触发。

参数:

  • Code: 关闭码
  • Reason: 关闭原因

OnTextMessage

vb
Private Sub m_WsClient_OnTextMessage(ByVal Message As String)

收到文本消息时触发。

参数:

  • Message: 文本消息内容

OnBinaryMessage

vb
Private Sub m_WsClient_OnBinaryMessage(Data() As Byte)

收到二进制消息时触发。

参数:

  • Data: 二进制数据数组

OnError

vb
Private Sub m_WsClient_OnError(ByVal Description As String)

发生错误时触发。

参数:

  • Description: 错误描述

OnPong

vb
Private Sub m_WsClient_OnPong(Data() As Byte)

收到 Pong 响应时触发。

参数:

  • Data: Pong 数据

服务端事件

OnStart

vb
Private Sub m_WsServer_OnStart(ByVal Port As Long)

服务启动时触发。

参数:

  • Port: 监听端口号

OnStop

vb
Private Sub m_WsServer_OnStop()

服务停止时触发。

OnClientConnect

vb
Private Sub m_WsServer_OnClientConnect(ByVal ClientID As String, ByVal RemoteAddress As String, ByVal RemotePort As Long)

新客户端连接时触发。

参数:

  • ClientID: 客户端 ID
  • RemoteAddress: 客户端 IP 地址
  • RemotePort: 客户端端口

OnClientDisconnect

vb
Private Sub m_WsServer_OnClientDisconnect(ByVal ClientID As String, ByVal Reason As String)

客户端断开连接时触发。

参数:

  • ClientID: 客户端 ID
  • Reason: 断开原因

OnClientTextMessage

vb
Private Sub m_WsServer_OnClientTextMessage(ByVal ClientID As String, ByVal Message As String)

收到客户端文本消息时触发。

参数:

  • ClientID: 客户端 ID
  • Message: 文本消息内容

OnClientBinaryMessage

vb
Private Sub m_WsServer_OnClientBinaryMessage(ByVal ClientID As String, Data() As Byte)

收到客户端二进制消息时触发。

参数:

  • ClientID: 客户端 ID
  • Data: 二进制数据数组

OnError

vb
Private Sub m_WsServer_OnError(ByVal Description As String)

发生错误时触发。

参数:

  • Description: 错误描述

高级特性

客户端自动重连

实现客户端自动重连机制:

vb
Private WithEvents tmrReconnect As Timer
Private lReconnectAttempts As Long

Private Sub m_WsClient_OnClose(ByVal Code As WsCloseCode, ByVal Reason As String)
    LogMessage "连接已关闭: " & Reason
    
    ' 非正常关闭时尝试重连
    If Code <> WS_CLOSE_NORMAL Then
        StartReconnectTimer
    End If
End Sub

Private Sub StartReconnectTimer()
    lReconnectAttempts = 0
    Set tmrReconnect = New Timer
    tmrReconnect.Interval = 3000  ' 3秒后重连
    tmrReconnect.Enabled = True
End Sub

Private Sub tmrReconnect_Timer()
    tmrReconnect.Enabled = False
    
    If lReconnectAttempts < 5 Then
        lReconnectAttempts = lReconnectAttempts + 1
        LogMessage "尝试重连 (" & lReconnectAttempts & "/5)..."
        m_WsClient.Connect txtURL.Text
    Else
        LogMessage "重连失败,已达到最大尝试次数"
    End If
End Sub

心跳保活

实现客户端和服务端的心跳保活:

vb
' 客户端端
Private WithEvents tmrHeartbeat As Timer

Private Sub m_WsClient_OnOpen()
    LogMessage "已连接,启动心跳"
    
    Set tmrHeartbeat = New Timer
    tmrHeartbeat.Interval = 30000  ' 30秒
    tmrHeartbeat.Enabled = True
End Sub

Private Sub tmrHeartbeat_Timer()
    If m_WsClient.State = WS_STATE_OPEN Then
        m_WsClient.Ping
        LogMessage "发送心跳 Ping"
    End If
End Sub

' 服务端端 - 记录最后活动时间
Private Sub m_WsServer_OnClientTextMessage(ByVal ClientID As String, ByVal Message As String)
    ' 记录客户端活动时间
    Dim oClient As cWebSocketServerClient
    Set oClient = m_WsServer.GetClient(ClientID)
    If Not oClient Is Nothing Then
        oClient.LastActivity = Now
    End If
End Sub

消息队列

实现消息队列确保消息顺序发送:

vb
Private colMessageQueue As Collection
Private bSending As Boolean

Private Sub InitializeQueue()
    Set colMessageQueue = New Collection
    bSending = False
End Sub

Private Sub EnqueueMessage(sMessage As String)
    colMessageQueue.Add sMessage
    
    If Not bSending Then
        ProcessQueue
    End If
End Sub

Private Sub ProcessQueue()
    If colMessageQueue.Count > 0 And m_WsClient.State = WS_STATE_OPEN Then
        bSending = True
        
        ' 获取队列中的第一条消息
        Dim sMessage As String
        sMessage = colMessageQueue(1)
        colMessageQueue.Remove 1
        
        ' 发送消息
        m_WsClient.SendText sMessage
    Else
        bSending = False
    End If
End Sub

Private Sub m_WsClient_OnTextMessage(ByVal Message As String)
    LogMessage "收到响应: " & Message
    
    ' 处理下一条消息
    If colMessageQueue.Count > 0 Then
        ProcessQueue
    Else
        bSending = False
    End If
End Sub

认证机制

实现简单的令牌认证:

vb
' 服务端 - 在连接时验证
Private Sub m_WsServer_OnClientConnect(ByVal ClientID As String, ByVal RemoteAddress As String, ByVal RemotePort As Long)
    Dim oClient As cWebSocketServerClient
    Set oClient = m_WsServer.GetClient(ClientID)
    
    ' 检查查询参数中的 token
    Dim sToken As String
    sToken = ExtractTokenFromClient(oClient)
    
    If Not IsValidToken(sToken) Then
        LogMessage "认证失败,断开客户端: " & ClientID
        m_WsServer.DisconnectClient ClientID, WS_CLOSE_POLICY_VIOLATION, "Invalid token"
        Exit Sub
    End If
    
    LogMessage "客户端认证成功: " & ClientID
End Sub

' 客户端 - 在 URL 中附加令牌
Private Sub ConnectWithToken(ByVal sURL As String, ByVal sToken As String)
    Dim sURLWithToken As String
    sURLWithToken = sURL & "?token=" & sToken
    
    m_WsClient.Connect sURLWithToken
End Sub

消息压缩

使用 Deflate 算法压缩消息:

vb
Private Sub SendCompressedMessage(sMessage As String)
    Dim byOriginal() As Byte
    Dim byCompressed() As Byte
    
    ' 转换为 UTF-8 字节数组
    byOriginal = UTF8.GetBytes(sMessage)
    
    ' 压缩数据(需要实现 Deflate 压缩函数)
    byCompressed = CompressData(byOriginal)
    
    ' 发送压缩后的数据
    m_WsClient.SendBinary byCompressed
    LogMessage "已发送压缩消息: " & (UBound(byOriginal) + 1) & " -> " & (UBound(byCompressed) + 1) & " 字节"
End Sub

常见问题

Q1: 如何处理连接超时?

A: 使用定时器监控连接状态:

vb
Private WithEvents tmrConnect As Timer

Private Sub StartConnectTimer()
    Set tmrConnect = New Timer
    tmrConnect.Interval = 10000  ' 10秒超时
    tmrConnect.Enabled = True
End Sub

Private Sub tmrConnect_Timer()
    tmrConnect.Enabled = False
    
    If m_WsClient.State = WS_STATE_CONNECTING Then
        LogMessage "连接超时"
        m_WsClient.CloseConnection WS_CLOSE_ABNORMAL, "Connection timeout"
        UpdateUI False
    End If
End Sub

Private Sub m_WsClient_OnOpen()
    tmrConnect.Enabled = False
    LogMessage "连接成功"
End Sub

Q2: 如何处理大消息?

A: 使用消息分块传输:

vb
Private Sub SendLargeMessage(sMessage As String)
    Const MAX_CHUNK_SIZE As Long = 1024  ' 1KB 分块
    Dim lLength As Long
    Dim lOffset As Long
    Dim sChunk As String
    
    lLength = Len(sMessage)
    lOffset = 1
    
    Do While lOffset <= lLength
        sChunk = Mid(sMessage, lOffset, MAX_CHUNK_SIZE)
        m_WsClient.SendText sChunk
        
        LogMessage "发送分块 " & (lOffset \ MAX_CHUNK_SIZE + 1) & ": " & Len(sChunk) & " 字节"
        lOffset = lOffset + MAX_CHUNK_SIZE
        
        ' 短暂延迟
        DoEvents
        Sleep 50
    Loop
End Sub

Q3: 如何限制客户端连接数?

A: 在服务端维护客户端计数:

vb
Private lMaxClients As Long
Private lCurrentClients As Long

Private Sub InitializeServer()
    lMaxClients = 100  ' 最大客户端数
    lCurrentClients = 0
End Sub

Private Sub m_WsServer_OnClientConnect(ByVal ClientID As String, ByVal RemoteAddress As String, ByVal RemotePort As Long)
    If lCurrentClients >= lMaxClients Then
        LogMessage "拒绝连接: 达到最大客户端数"
        m_WsServer.DisconnectClient ClientID, WS_CLOSE_TRY_AGAIN_LATER, "Server full"
        Exit Sub
    End If
    
    lCurrentClients = lCurrentClients + 1
    LogMessage "客户端连接: " & ClientID & " (当前: " & lCurrentClients & "/" & lMaxClients & ")"
End Sub

Private Sub m_WsServer_OnClientDisconnect(ByVal ClientID As String, ByVal Reason As String)
    lCurrentClients = lCurrentClients - 1
    LogMessage "客户端断开: " & ClientID & " (当前: " & lCurrentClients & ")"
End Sub

Q4: 如何实现房间功能?

A: 使用客户端分组管理:

vb
Private colRooms As Collection

Private Sub InitializeRooms()
    Set colRooms = New Collection
End Sub

' 加入房间
Private Sub JoinRoom(ByVal ClientID As String, ByVal sRoom As String)
    Dim colClients As Collection
    
    On Error Resume Next
    Set colClients = colRooms(sRoom)
    
    If colClients Is Nothing Then
        Set colClients = New Collection
        colRooms.Add colClients, sRoom
    End If
    
    On Error GoTo 0
    colClients.Add ClientID, ClientID
    LogMessage "客户端 " & ClientID & " 加入房间: " & sRoom
End Sub

' 向房间发送消息
Private Sub SendToRoom(ByVal sRoom As String, ByVal sMessage As String)
    Dim colClients As Collection
    Dim i As Long
    
    On Error Resume Next
    Set colClients = colRooms(sRoom)
    On Error GoTo 0
    
    If Not colClients Is Nothing Then
        For i = 1 To colClients.Count
            m_WsServer.SendText colClients(i), sMessage
        Next i
    End If
End Sub

Q5: 如何调试 WebSocket 通信?

A: 记录所有事件和详细日志:

vb
Private Sub LogWebSocketEvent(ByVal sEventType As String, ByVal sDetails As String)
    Dim sLog As String
    sLog = Format$(Now, "yyyy-mm-dd hh:mm:ss") & " [" & sEventType & "] " & sDetails
    
    ' 输出到日志窗口
    txtLog.Text = txtLog.Text & sLog & vbCrLf
    txtLog.SelStart = Len(txtLog.Text)
    
    ' 输出到调试窗口
    Debug.Print sLog
    
    ' 可选: 写入文件
    LogToFile sLog
End Sub

Private Sub LogToFile(sLog As String)
    Dim nFile As Integer
    nFile = FreeFile
    
    Open App.Path & "\websocket.log" For Append As #nFile
    Print #nFile, sLog
    Close #nFile
End Sub

最佳实践

  1. 资源清理:窗体卸载时调用 CloseConnectionStopServer 释放资源
  2. 错误处理:所有网络操作都应包含错误处理
  3. 状态检查:发送消息前检查 State = WS_STATE_OPEN
  4. 日志记录:记录关键事件便于调试和问题排查
  5. 超时处理:实现合理的连接和操作超时机制
  6. 心跳保活:定期发送 Ping 帧保持连接活跃
  7. 消息验证:接收消息时进行格式和长度验证
  8. 优雅关闭:使用正确的关闭码和原因信息
  9. 客户端限制:实现合理的客户端连接数限制
  10. 安全认证:在生产环境中实现认证机制

协议说明

WebSocket 握手流程

  1. 客户端请求
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
  1. 服务端响应
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=

帧结构

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| Payload len |    Extended payload length    |
|I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
|N|V|V|V|       |S|             |   (if payload len==126/127)   |
| |1|2|3|       |K|             |                               |
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
|     Extended payload length continued, if payload len == 127  |
+ - - - - - - - - - - - - - - - +-------------------------------+
|                               |Masking-key, if MASK set to 1  |
+-------------------------------+-------------------------------+
| Masking-key (continued)       |           Payload Data         |
+-------------------------------- - - - - - - - - - - - - - - - +
:                     Payload Data continued ...                :
+ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
|                     Payload Data continued ...                |
+---------------------------------------------------------------+

操作码

说明
0x0连续帧
0x1文本帧
0x2二进制帧
0x8关闭帧
0x9Ping 帧
0xAPong 帧

相关资源

  • 项目目录: Websocket/
  • 客户端示例: Client/frmClient.frm
  • 服务端示例: Server/frmServer.frm
  • 依赖库: VBMAN.dll
  • WebSocket 协议: RFC 6455

更新日志

  • v2.0 - 新版类库,分离客户端/服务端,优化架构
    • 分离的客户端 (cWebSocketClient) 和服务端 (cWebSocketServer) 类
    • 独立的帧解析器 (cWebSocketFrame)
    • 高效的缓冲区管理 (cByteBuffer)
    • 清晰的职责分离和错误处理
  • v1.0 - 初始版本

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