Skip to content

cWinsock 开发文档

目录


概述

cWinsock 是一个基于 Windows Socket API 的高级封装类,提供了简单易用的 VB6 网络编程接口。它支持 TCP 和 UDP 协议,可以轻松创建客户端和服务器端应用程序。

主要特性

  • 多协议支持: 支持 TCP 和 UDP 协议
  • 异步非阻塞: 使用异步 I/O 模型,不阻塞 UI 线程
  • 多客户端管理: 服务器端自动管理多个客户端连接
  • 事件驱动: 通过事件机制处理网络事件
  • 错误处理: 完善的错误处理机制

项目结构

Winsock/
├── Form1.frm          # 主窗体(服务器 + UDP 示例)
├── Client.frm         # 客户端窗体
├── Module1.bas        # 模块文件
├── Project1.vbp       # 项目文件
└── README.md          # 示例说明

快速开始

环境要求

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

引用库

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

基础代码结构

vb
' 声明 cWinsock 对象(带事件)
Private WithEvents m_oSocket As cWinsock

' 初始化对象
Set m_oSocket = New cWinsock

' 设置协议类型
m_oSocket.Protocol = sckTCPProtocol  ' 或 sckUDPProtocol

TCP 客户端

TCP 客户端用于连接到远程服务器,建立可靠的连接后进行双向通信。

客户端

创建 TCP 客户端

vb
Private WithEvents m_oClient As cWinsock

Private Sub InitializeClient()
    If m_oClient Is Nothing Then
        Set m_oClient = New cWinsock
        m_oClient.Protocol = sckTCPProtocol
    End If
End Sub

连接到服务器

vb
Private Sub ConnectToServer(ByVal sHost As String, ByVal lPort As Long)
    On Error GoTo EH
    
    InitializeClient()
    m_oClient.Connect sHost, lPort
    
    LogMessage "正在连接到 " & sHost & ":" & lPort & "..."
    Exit Sub

EH:
    LogMessage "连接错误: " & Err.Description
End Sub

参数说明:

  • sHost: 服务器 IP 地址或主机名(如 "127.0.0.1" 或 "example.com")
  • lPort: 服务器端口号(如 8080)

发送数据

vb
Private Sub SendData(sData As String)
    On Error GoTo EH
    
    If Not m_oClient Is Nothing And m_oClient.State = sckConnected Then
        m_oClient.SendData sData
        LogMessage "已发送数据: " & sData
    Else
        LogMessage "未连接到服务器"
    End If
    Exit Sub

EH:
    LogMessage "发送错误: " & Err.Description
End Sub

断开连接

vb
Private Sub Disconnect()
    If Not m_oClient Is Nothing Then
        m_oClient.Close_
        LogMessage "客户端已断开连接"
    End If
End Sub

完整示例

参考 Client.frm 文件,这是一个完整的 TCP 客户端实现:

vb
Private Sub cmdClientConnect_Click()
    On Error GoTo EH
    
    If m_oClient Is Nothing Then
        Set m_oClient = New cWinsock
        m_oClient.Protocol = sckTCPProtocol
    End If
    
    m_oClient.Connect txtClientHost.Text, CLng(txtClientPort.Text)
    
    LogMessage "正在连接到 " & txtClientHost.Text & ":" & txtClientPort.Text & "..."
    Exit Sub
EH:
    LogMessage "连接错误: " & Err.Description
End Sub

Private Sub cmdClientDisconnect_Click()
    If Not m_oClient Is Nothing Then
        m_oClient.Close_
        LogMessage "客户端已断开连接"
    End If
    cmdClientConnect.Enabled = True
    cmdClientDisconnect.Enabled = False
    cmdClientSend.Enabled = False
End Sub

Private Sub cmdClientSend_Click()
    On Error GoTo EH
    
    If Not m_oClient Is Nothing And m_oClient.State = sckConnected Then
        m_oClient.SendData txtClientData.Text
        LogMessage "已发送数据: " & txtClientData.Text
    End If
    Exit Sub
EH:
    LogMessage "发送错误: " & Err.Description
End Sub

TCP 服务器

TCP 服务器监听指定端口,接受多个客户端连接,并可以与每个客户端进行独立通信。

服务器

创建 TCP 服务器

vb
Private WithEvents m_oServer As cWinsock

Private Sub InitializeServer()
    If m_oServer Is Nothing Then
        Set m_oServer = New cWinsock
    End If
    
    m_oServer.Protocol = sckTCPProtocol
End Sub

开始监听

vb
Private Sub StartListening(ByVal lPort As Long)
    On Error GoTo EH
    
    InitializeServer()
    m_oServer.Listen lPort
    
    LogMessage "服务器开始监听端口 " & lPort
    Exit Sub

EH:
    LogMessage "监听错误: " & Err.Description
End Sub

停止监听

vb
Private Sub StopListening()
    If Not m_oServer Is Nothing Then
        m_oServer.Close_
        LogMessage "服务器已停止监听"
    End If
End Sub

处理客户端连接

vb
Private Sub m_oServer_ConnectionRequest(Client As VBMANLIB.cWinsock, DisConnect As Boolean)
    ' 压测模式下不显示连接日志
    If Not m_bStressTestMode Then
        LogMessage "新客户端连接: " & Client.RemoteHostIP & ":" & Client.RemotePort & " (Tag: " & Client.Tag & ")"
    End If

    ' 将 Tag 内容写入 listbox
    lstClients.AddItem Client.Tag & " " & Client.RemoteHostIP & ":" & Client.RemotePort
End Sub

压测模式

压测模式用于性能测试,在高并发场景下只统计数据而不显示详细日志:

vb
' 压测模式变量
Private m_bStressTestMode As Boolean
Private m_lMsgCount As Long          ' 当前周期消息数
Private m_lBytesCount As Long         ' 当前周期字节数
Private m_lTotalMsgCount As Long      ' 累计消息数
Private m_lTotalBytesCount As Long     ' 累计字节数
Private m_lCurrentClientCount As Long  ' 当前客户端数量
Private m_dLastStatsTime As Double    ' 上次统计时间
Private WithEvents tmrStats As VB.Timer

' 切换压测模式
Private Sub chkStressTest_Click()
    m_bStressTestMode = (chkStressTest.Value = vbChecked)

    If m_bStressTestMode Then
        LogMessage "压测模式已启用 - 仅统计,不显示消息内容"
        ResetStats
    Else
        LogMessage "压测模式已关闭 - 显示所有消息内容"
    End If
End Sub

' 接收数据时(压测模式)
Private Sub m_oServer_DataArrival(Client As cWinsock, ByVal bytesTotal As Long)
    Dim sData As String
    Dim sResponse As String

    On Error GoTo EH

    If m_bStressTestMode Then
        ' 压测模式:只统计,不获取内容
        Client.GetData sData, vbString, bytesTotal

        ' 累计统计
        m_lMsgCount = m_lMsgCount + 1
        m_lBytesCount = m_lBytesCount + bytesTotal
        m_lTotalMsgCount = m_lTotalMsgCount + 1
        m_lTotalBytesCount = m_lTotalBytesCount + bytesTotal

        ' 回显数据
        sResponse = "OK"
        Client.SendData sResponse
    Else
        ' 普通模式:显示内容
        Client.GetData sData
        LogMessage "从客户端 " & Client.Tag & " 收到数据 (" & bytesTotal & " 字节): " & sData
        sResponse = "Echo: " & sData
        Client.SendData sResponse
        LogMessage "已向客户端 " & Client.Tag & " 发送回显: " & sResponse
    End If
    Exit Sub
EH:
    LogMessage "服务器接收数据错误: " & Err.Description
End Sub

' 统计定时器(每秒更新一次)
Private Sub tmrStats_Timer()
    Dim dCurrentTime As Double
    Dim dElapsedTime As Double
    Dim lMsgPerSec As Long
    Dim lBytesPerSec As Long
    Dim sStats As String

    If m_bStressTestMode Then
        dCurrentTime = Timer
        dElapsedTime = dCurrentTime - m_dLastStatsTime

        If dElapsedTime > 0 Then
            lMsgPerSec = CLng(m_lMsgCount / dElapsedTime)
            lBytesPerSec = CLng(m_lBytesCount / dElapsedTime)

            ' 构建统计信息(每项一行)
            sStats = vbCrLf & _
                     "========================================" & vbCrLf & _
                     Format$(Now, "hh:mm:ss") & " - [统计信息]" & vbCrLf & _
                     "----------------------------------------" & vbCrLf & _
                     "当前周期消息数: " & m_lMsgCount & vbCrLf & _
                     "当前周期字节数: " & m_lBytesCount & vbCrLf & _
                     "消息速率: " & lMsgPerSec & " msg/s" & vbCrLf & _
                     "数据速率: " & Format$(lBytesPerSec / 1024, "0.00") & " KB/s" & vbCrLf & _
                     "----------------------------------------" & vbCrLf & _
                     "当前客户端数量: " & m_lCurrentClientCount & vbCrLf & _
                     "累计消息数: " & m_lTotalMsgCount & vbCrLf & _
                     "累计字节数: " & Format$(m_lTotalBytesCount / 1024 / 1024, "0.00") & " MB" & vbCrLf & _
                     "========================================" & vbCrLf

            ' 直接替换文本框内容
            txtLog.Text = sStats
            txtLog.SelStart = Len(txtLog.Text)

            ' 重置当前周期统计
            m_lMsgCount = 0
            m_lBytesCount = 0
            m_dLastStatsTime = dCurrentTime
        End If
    End If
End Sub

接收和发送数据

vb
Private Sub m_oServer_DataArrival(Client As cWinsock, ByVal bytesTotal As Long)
    Dim sData As String
    Dim sResponse As String
    
    On Error GoTo EH
    
    ' 获取数据
    Client.GetData sData
    LogMessage "从客户端 " & Client.Tag & " 收到数据 (" & bytesTotal & " 字节): " & sData
    
    ' 处理数据并返回
    sResponse = "Echo: " & sData
    Client.SendData sResponse
    LogMessage "已向客户端 " & Client.Tag & " 发送回显: " & sResponse
    Exit Sub

EH:
    LogMessage "服务器接收数据错误: " & Err.Description
End Sub

处理客户端断开

vb
Private Sub m_oServer_CloseEvent(Client As cWinsock)
    Dim i As Long

    ' 压测模式下不显示日志
    If Not m_bStressTestMode Then
        LogMessage "客户端 " & Client.RemoteHostIP & ":" & Client.RemotePort & " 已断开连接"
    End If

    ' 用 Tag 去 listbox 遍历删除匹配项
    For i = 0 To lstClients.ListCount - 1
        If InStr(lstClients.List(i), Client.Tag) > 0 Then
            lstClients.RemoveItem i
            Exit For
        End If
    Next
End Sub

完整示例

参考 Form1.frm 中的服务器实现:

vb
Private Sub cmdServerListen_Click()
    On Error GoTo EH
    
    If m_oServer Is Nothing Then
        Set m_oServer = New cWinsock
    End If
    
    m_oServer.Protocol = sckTCPProtocol
    m_oServer.Listen CLng(txtServerPort.Text)
    
    LogMessage "服务器开始监听端口 " & txtServerPort.Text
    cmdServerListen.Enabled = False
    cmdServerStop.Enabled = True
    Exit Sub
EH:
    LogMessage "监听错误: " & Err.Description
End Sub

Private Sub m_oServer_ConnectionRequest(Client As VBMANLIB.cWinsock, DisConnect As Boolean)
    LogMessage "新客户端连接: " & Client.RemoteHostIP & ":" & Client.RemotePort & " (Tag: " & Client.Tag & ")"
    lstClients.AddItem Client.Tag & " - " & Client.RemoteHostIP & ":" & Client.RemotePort
End Sub

UDP 通信

UDP(User Datagram Protocol)是无连接的协议,适合发送少量数据或对可靠性要求不高的场景。

创建 UDP Socket

vb
Private WithEvents m_oUdp As cWinsock

Private Sub InitializeUdp()
    If m_oUdp Is Nothing Then
        Set m_oUdp = New cWinsock
        m_oUdp.Protocol = sckUDPProtocol
    End If
End Sub

绑定本地端口

vb
Private Sub BindUdpPort(ByVal lPort As Long)
    On Error GoTo EH
    
    InitializeUdp()
    m_oUdp.Bind lPort
    
    LogMessage "UDP Socket 已绑定到端口 " & lPort
    Exit Sub

EH:
    LogMessage "UDP 绑定错误: " & Err.Description
End Sub

发送 UDP 数据

vb
Private Sub SendUdpData(sHost As String, lPort As Long, sData As String)
    On Error GoTo EH
    
    With m_oUdp
        .RemoteHost = sHost
        .RemotePort = lPort
        .SendData sData
        LogMessage "UDP 已发送数据到 " & sHost & ":" & lPort & ": " & sData
    End With
    Exit Sub

EH:
    LogMessage "UDP 发送错误: " & Err.Description
End Sub

接收 UDP 数据

vb
Private Sub m_oUdp_DataArrival(Client As cWinsock, ByVal bytesTotal As Long)
    Dim sData As String
    
    On Error GoTo EH
    
    Client.GetData sData
    LogMessage "UDP 收到数据 (" & bytesTotal & " 字节) 来自 " & Client.RemoteHostIP & ":" & Client.RemotePort & ": " & sData
    Exit Sub

EH:
    LogMessage "UDP 接收数据错误: " & Err.Description
End Sub

完整示例

参考 Form1.frm 中的 UDP 实现:

vb
Private Sub cmdUdpBind_Click()
    If m_oUdp Is Nothing Then
        Set m_oUdp = New cWinsock
        m_oUdp.Protocol = sckUDPProtocol
    End If
    
    m_oUdp.Bind CLng(txtUdpPort.Text)
    
    LogMessage "UDP Socket 已绑定到端口 " & txtUdpPort.Text
End Sub

Private Sub cmdUdpSend_Click()
    On Error GoTo EH
    
    With m_oUdp
        .RemoteHost = txtUdpHost.Text
        .RemotePort = CLng(txtUdpPort.Text)
        .SendData txtUdpData.Text
        LogMessage "UDP 已发送数据到 " & txtUdpHost.Text & ":" & txtUdpPort.Text & ": " & txtUdpData.Text
    End With
    Exit Sub
EH:
    LogMessage "UDP 发送错误: " & Err.Description
End Sub

API 参考

属性

属性名类型说明
ProtocolInteger协议类型:sckTCPProtocol (0) 或 sckUDPProtocol (1)
StateInteger连接状态,参见下方状态常量
RemoteHostString远程主机地址
RemoteHostIPString远程主机 IP 地址(只读)
RemotePortLong远程端口号
LocalPortLong本地端口号(只读)
TagVariant用于存储自定义数据的标签

方法

Connect

vb
oSocket.Connect RemoteHost, RemotePort

连接到指定的服务器。

参数:

  • RemoteHost: 服务器地址(IP 或主机名)
  • RemotePort: 服务器端口

Listen

vb
oSocket.Listen Port

开始监听指定端口(仅 TCP 服务器模式)。

参数:

  • Port: 监听端口号

Bind

vb
oSocket.Bind Port

绑定本地端口(仅 UDP 模式)。

参数:

  • Port: 绑定端口号

SendData

vb
oSocket.SendData Data

发送数据到远程端点。

参数:

  • Data: 要发送的数据(字符串或字节数组)

GetData

vb
oSocket.GetData Data, [Type], [MaxLen]

从缓冲区获取接收到的数据。

参数:

  • Data: 存储接收数据的变量
  • Type: 可选,数据类型(默认为字符串)
  • MaxLen: 可选,最大读取长度

Close_

vb
oSocket.Close_

关闭连接或停止监听。

状态常量

常量说明
sckClosed0连接已关闭
sckOpen1Socket 已打开
sckListening2正在监听
sckConnectionPending3连接正在建立
sckResolvingHost4正在解析主机
sckHostResolved5主机已解析
sckConnecting6正在连接
sckConnected7已连接
sckClosing8正在关闭
sckError9发生错误

事件说明

Connect

vb
Private Sub oSocket_Connect(Client As cWinsock)

客户端成功连接到服务器时触发。

参数:

  • Client: 触发事件的 cWinsock 对象

CloseEvent

vb
Private Sub oSocket_CloseEvent(Client As cWinsock)

连接关闭时触发。

参数:

  • Client: 触发事件的 cWinsock 对象

ConnectionRequest

vb
Private Sub oSocket_ConnectionRequest(Client As cWinsock, DisConnect As Boolean)

服务器收到新客户端连接请求时触发(仅 TCP 服务器)。

参数:

  • Client: 新连接的客户端对象
  • DisConnect: 设置为 True 拒绝连接,False 接受连接

DataArrival

vb
Private Sub oSocket_DataArrival(Client As cWinsock, ByVal bytesTotal As Long)

收到数据时触发。

参数:

  • Client: 接收数据的客户端对象
  • bytesTotal: 接收到的数据字节数

Error

vb
Private Sub oSocket_Error(Client As cWinsock, ByVal Number As Long, Description As String, ByVal Scode As Long)

发生错误时触发。

参数:

  • Client: 发生错误的客户端对象
  • Number: 错误代码
  • Description: 错误描述
  • Scode: 系统 Scode 错误码

高级特性

多客户端管理

服务器端会自动为每个连接创建独立的客户端对象,通过 Tag 属性可以识别不同的客户端:

vb
Private Sub m_oServer_ConnectionRequest(Client As cWinsock, DisConnect As Boolean)
    ' 分配唯一标识
    Client.Tag = "Client_" & GetNextId()
    
    ' 存储到集合中管理
    colClients.Add Client, Client.Tag
    
    LogMessage "新客户端: " & Client.Tag
End Sub

客户端认证

ConnectionRequest 事件中实现简单的认证:

vb
Private Sub m_oServer_ConnectionRequest(Client As cWinsock, DisConnect As Boolean)
    ' 检查 IP 白名单
    If Not IsAllowedIP(Client.RemoteHostIP) Then
        DisConnect = True
        LogMessage "拒绝连接: " & Client.RemoteHostIP
        Exit Sub
    End If
    
    LogMessage "接受连接: " & Client.RemoteHostIP
End Sub

数据分包处理

处理大量数据时的分包传输:

vb
Private Sub m_oServer_DataArrival(Client As cWinsock, ByVal bytesTotal As Long)
    Static sBuffer As String
    Dim sData As String
    Dim lPos As Long
    
    Client.GetData sData
    sBuffer = sBuffer & sData
    
    ' 查找消息结束标记(如换行符)
    Do
        lPos = InStr(sBuffer, vbCrLf)
        If lPos > 0 Then
            ProcessMessage Client, Left(sBuffer, lPos - 1)
            sBuffer = Mid(sBuffer, lPos + 2)
        Else
            Exit Do
        End If
    Loop
End Sub

错误重连机制

实现自动重连:

vb
Private Sub m_oClient_CloseEvent(Client As cWinsock)
    LogMessage "连接已断开,尝试重连..."
    
    ' 延迟重连
    Dim i As Integer
    For i = 1 To 3
        If TryReconnect() Then
            Exit Sub
        End If
        Sleep 2000
    Next i
    
    LogMessage "重连失败"
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()
    If m_oClient.State <> sckConnected Then
        m_oClient.Close_
        LogMessage "连接超时"
        tmrConnect.Enabled = False
    End If
End Sub

Q2: 如何发送二进制数据?

A: 使用字节数组:

vb
Dim byData() As Byte
byData = StrConv("Hello", vbFromUnicode)
m_oClient.SendData byData

Q3: UDP 模式下如何区分不同的发送者?

A: 通过 RemoteHostIPRemotePort 属性:

vb
Private Sub m_oUdp_DataArrival(Client As cWinsock, ByVal bytesTotal As Long)
    Dim sSender As String
    sSender = Client.RemoteHostIP & ":" & Client.RemotePort
    
    LogMessage "收到来自 " & sSender & " 的数据"
End Sub

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

A: 使用计数器管理:

vb
Private lClientCount As Long

Private Sub m_oServer_ConnectionRequest(Client As cWinsock, DisConnect As Boolean)
    If lClientCount >= 100 Then
        DisConnect = True
        LogMessage "拒绝连接:达到最大客户端数"
        Exit Sub
    End If
    
    lClientCount = lClientCount + 1
    LogMessage "客户端连接数: " & lClientCount
End Sub

Private Sub m_oServer_CloseEvent(Client As cWinsock)
    lClientCount = lClientCount - 1
End Sub

Q5: 如何调试网络通信问题?

A: 使用日志记录所有关键事件:

vb
Private Sub LogMessage(sMessage As String)
    txtLog.Text = txtLog.Text & Format$(Now, "hh:mm:ss") & " - " & sMessage & vbCrLf
    txtLog.SelStart = Len(txtLog.Text)
    Debug.Print sMessage  ' 输出到立即窗口
End Sub

最佳实践

  1. 始终检查连接状态:在发送数据前检查 State = sckConnected
  2. 错误处理:所有网络操作都应包含错误处理
  3. 资源清理:窗体卸载时调用 Close_ 释放资源
  4. 日志记录:记录关键事件便于调试
  5. 超时处理:设置合理的连接和操作超时
  6. 数据校验:接收数据时进行格式和长度验证

相关资源

  • 项目目录: Winsock/
  • 示例代码: Client.frm, Form1.frm
  • 依赖库: VBMAN.dll

更新日志

  • v1.0 - 初始版本,包含 TCP 客户端、TCP 服务器和 UDP 示例

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