Packet Protocol and Heartbeat Mechanism
📦💓 Built-in protocol to solve TCP sticky packet/fragmentation issues, and intelligent heartbeat to keep connections alive
📖 Table of Contents
- Overview
- TCP Sticky Packet and Fragmentation Issues
- Packet Protocol
- Security Limit Properties
- Event Model
- Intelligent Heartbeat Mechanism
- UDP Protocol Support
- Complete Examples
- Best Practices
- FAQ
Overview
TCP is a stream protocol without message boundaries — data sent continuously by the sender may be received multiple times by the receiver (fragmentation), or multiple messages may be received at once as concatenated data (sticky packets). cWinsock's built-in packet protocol cPacketProtocol and intelligent heartbeat cHeartbeat automatically solve these problems:
- Packet Protocol: Three built-in protocols, automatic packet/unpacket, ensuring each receive is a complete message
- Heartbeat Mechanism: Embedded timer automatic drive, server timeout detection + client intelligent keepalive
TCP Sticky Packet and Fragmentation Issues
What are Sticky Packets/Fragmentation?
| Phenomenon | Term | Description |
|---|---|---|
| Data from one Send arrives in multiple Receives | Fragmentation | Sent 1000 bytes, first receive 300, then 700 |
| Data from multiple Sends arrives in one Receive | Sticky Packet | Sent 3 messages continuously, received as concatenated data |
| Both mixed | Most Common | Received data is both incomplete and mixed with next message's start |
Visual Diagram
Application layer sends: [Msg1][Msg2][Msg3]
↓ TCP stream transmission (no boundaries)
Receiver may receive:
Case 1 (Fragmentation): [Msg1 part1] [Msg1 part2+Msg2 part1] [Msg2 part2+Msg3]
Case 2 (Sticky Packet): [Msg1+Msg2] [Msg3]
Case 3 (Mixed): [Msg1 part1] [Msg1 part2+Msg2] [Msg3 part1] [Msg3 part2]
Ideal case (Rare): [Msg1] [Msg2] [Msg3]cPacketProtocol's Solution
Define clear boundaries in data, restoring boundary-less byte streams into bounded messages:
Raw TCP byte stream (no boundaries):
[Msg1 part1][Msg1 part2+Msg2 part1][Msg2 part2]
↓ cPacketProtocol.Decode()
Complete messages:
[Msg1 Complete] → Trigger MessageArrival
[Msg2 Complete] → Trigger MessageArrivalPacket Protocol
Three Protocol Types Comparison
| Protocol Type | Principle | Fragmentation Handling | Sticky Packet Handling | Pros/Cons |
|---|---|---|---|---|
ppLengthHeader | Header indicates message body length | Cache if length insufficient, extract when data arrives | Cut one when length sufficient, continue parsing remainder | Recommended. Not dependent on data content, no message length limit |
ppDelimiter | Delimiter marks message end | Cache if delimiter not found | Cut one when delimiter found, continue searching | Simple, but delimiter cannot appear in message body |
ppFixedLength | Fixed message length | Cache if less than fixed length | Cut one when fixed length reached | Only for fixed-length message scenarios |
Recommended: ppLengthHeader
4-byte little-endian length header protocol is the most universal choice:
- Not dependent on special characters appearing in data content (delimiter protocol's weakness)
- No single message length limit (fixed-length protocol's weakness)
- Each message has its own length, receiver knows exactly how many bytes to read
ppDelimiter - Delimiter Protocol
Uses specified character/string as message boundary marker.
Use Case: Text protocols, line-based command protocols (e.g., chat, HTTP headers)
' Configuration
m_oServer.PacketProtocol = ppDelimiter
m_oServer.Delimiter = vbCrLf ' Newline delimiter
' Send automatically appends delimiter
Client.SendData "Hello" ' Actually sends: "Hello" + vbCrLf
' Receive automatically removes delimiter
Private Sub m_oServer_MessageArrival(Client As cWinsock, ByVal bytesTotal As Long)
Debug.Print "Complete message: " & Client.GetDataText() ' "Hello"
End SubCommon Delimiters:
| Delimiter | Constant | Use Case |
|---|---|---|
\r\n | vbCrLf | Line-based text protocol |
\n | vbLf | Unix-style line protocol |
\0 | vbNullChar | C string style |
| Custom string | "<EOF>" | Custom protocol |
Note: Delimiter cannot appear in message body, otherwise messages will be incorrectly split.
ppFixedLength - Fixed Length Protocol
Each message has fixed length, suitable for structured data with known length.
Use Case: Status packets, sensor data, fixed-format messages
' Configuration
m_oServer.PacketProtocol = ppFixedLength
m_oServer.FixedLength = 256 ' Each message fixed 256 bytes
' Send: Less than 256 bytes will be zero-padded, more than 256 bytes will error
Client.SendData myDataNote: Data exceeding length will directly error (won't silently truncate).
ppLengthHeader - Length Header Protocol (Recommended)
Add length information in message header, most universal and recommended protocol.
Use Case: Binary protocols, variable-length messages, any scenario needing reliable transmission
' Configuration
m_oServer.PacketProtocol = ppLengthHeader
m_oServer.HeaderBytes = 4 ' 4-byte length header (supports max ~4GB)
m_oServer.HeaderEndian = eeLittleEndian ' Little-endian
' Send automatically adds length header
Client.SendData "Hello" ' Actually sends: [4-byte length=5] + "Hello"
' Receive automatically strips length header
Private Sub m_oServer_MessageArrival(Client As cWinsock, ByVal bytesTotal As Long)
Dim baData() As Byte
baData = Client.GetDataByteArray() ' 100% is one complete message
End SubHeaderBytes Options:
| Value | Type | Max Message Length | Description |
|---|---|---|---|
| 2 | Unsigned Integer | 65,535 bytes (~64KB) | Small message scenarios, saves bandwidth |
| 4 | Unsigned Long | 2,147,483,647 bytes (~2GB) | General scenarios, recommended |
HeaderEndian Byte Order:
| Value | Description | Use Case |
|---|---|---|
eeLittleEndian | Little-endian (default) | x86/x64 platform internal communication |
eeBigEndian | Big-endian (network byte order) | Communication with Java/C server |
Security Limit Properties
Prevent malicious packets from exhausting memory, added 2026-06-09:
MaxPacketSize
Single packet maximum size limit, prevents malicious oversized packet declarations from exhausting memory.
Property Get MaxPacketSize() As Long
Property Let MaxPacketSize(ByVal Value As Long)- Default: 1MB (1048576 bytes)
- Function: During length header protocol parsing, if declared message length exceeds this value, directly error and discard
- Applicable Protocol:
ppLengthHeader
' Adjust max packet limit
m_oServer.MaxPacketSize = 524288 ' 512KB
' New clients automatically inherit this configurationMaxBufferSize
Buffer accumulation upper limit, prevents large numbers of incomplete packets slowly consuming memory.
Property Get MaxBufferSize() As Long
Property Let MaxBufferSize(ByVal Value As Long)- Default: 4MB (4194304 bytes)
- Function: Check before Decode merge buffer, error if exceeded
- Applicable Protocol: All protocols
' Adjust buffer upper limit
m_oServer.MaxBufferSize = 8388608 ' 8MBOverflow Behavior
| Overflow Type | Behavior |
|---|---|
Single packet exceeds MaxPacketSize | Throw explicit error message, discard buffer |
Accumulation exceeds MaxBufferSize | Throw explicit error message, discard buffer |
| Data too long (FixedLength) | Throw error (won't silently truncate) |
Event Model
Protocol Mode vs No-Protocol Mode Difference
| Mode | Trigger Event | Description |
|---|---|---|
No Protocol (ppNone) | DataArrival | Raw byte stream, may be incomplete or sticky |
| With Protocol | MessageArrival | Each time guaranteed to be one complete message |
Key Rule: In protocol mode only MessageArrival is triggered, DataArrival is not triggered, avoiding duplicate reads of same data by two events.
MessageArrival Event
Private Sub object_MessageArrival(Client As cWinsock, ByVal bytesTotal As Long)| Parameter | Type | Description |
|---|---|---|
Client | cWinsock | Client object receiving message |
bytesTotal | Long | Complete message byte count |
Usage Example
' Set length header protocol
m_oServer.PacketProtocol = ppLengthHeader
m_oServer.HeaderBytes = 4
m_oServer.HeaderEndian = eeLittleEndian
' Receive complete message
Private Sub m_oServer_MessageArrival(Client As cWinsock, ByVal bytesTotal As Long)
' Buffer now contains complete message, 100% complete
Dim sData As String
sData = Client.GetDataText()
Debug.Print "Complete message: " & sData
End SubDifference from DataArrival
| Event | Trigger Timing | Data Integrity | Use Case |
|---|---|---|---|
DataArrival | Each time raw data received | May be fragmented or sticky data | No-protocol mode |
MessageArrival | After protocol parses complete message | Guaranteed to be one complete message | Protocol mode |
Intelligent Heartbeat Mechanism
cWinsock's built-in cHeartbeat heartbeat manager, embedded cTimer automatic drive, no external timer needed.
Server: Timeout Detection
Server periodically checks all clients' idle time, automatically disconnects zombie connections after timeout.
' Configuration
m_oServer.AutoHeartbeat = True
m_oServer.HeartbeatTimeout = 120 ' 2 minutes no activity then timeout
' Timeout event
Private Sub m_oServer_ClientTimeout(Client As cWinsock)
Debug.Print "Client " & Client.Tag & " timeout, automatically disconnected"
End SubClient: Heartbeat Keepalive
Client periodically sends heartbeat packets to keep connection alive. Has intelligent skip mechanism — skips heartbeat when data is being sent/received, saving bandwidth.
' Configuration
m_oClient.AutoHeartbeat = True
m_oClient.HeartbeatInterval = 50 ' 50 seconds no activity then send heartbeat
' Heartbeat event
Private Sub m_oClient_HeartbeatSent(Client As cWinsock)
Debug.Print "Heartbeat sent, idle: " & Client.IdleSeconds & " seconds"
End SubCustom Heartbeat Packet
' Default heartbeat packet is single byte &H00, can be customized
Dim baHB(0 To 3) As Byte
baHB(0) = &H50 ' P
baHB(1) = &H49 ' I
baHB(2) = &H4E ' N
baHB(3) = &H47 ' G
m_oClient.HeartbeatData = baHBHeartbeat and Protocol Consistency
Heartbeat data is sent through protocol encoding, won't pollute protocol state machine. That is:
- Heartbeat packets sent through
SendData, go through protocolEncode - Receiver heartbeat data goes through protocol
Decode - Heartbeat won't cause sticky packet/fragmentation state confusion
Working Principle
- Heartbeat manager embedded
cTimer, triggers Tick every 10 seconds - Server: Checks all clients'
IdleSeconds, triggersClientTimeoutand auto-disconnects if timeout - Client: If idle exceeds
HeartbeatIntervalsends heartbeat packet, intelligently skips when data is sent/received - Each send/receive automatically resets
LastActivityTime
Heartbeat Related Properties
| Property | Type | Read/Write | Description |
|---|---|---|---|
AutoHeartbeat | Boolean | RW | Enable/Disable automatic heartbeat |
HeartbeatTimeout | Long | RW | Server timeout seconds (default 120) |
HeartbeatInterval | Long | RW | Client heartbeat interval seconds (default 50) |
HeartbeatData | Byte() | RW | Heartbeat packet content (default single byte 0) |
IdleSeconds | Long | RO | Current idle seconds |
UDP Protocol Support
UDP clients also support packet protocols. UDP is a datagram protocol (naturally has boundaries), but protocol mode can still be used for:
- Custom message format processing
- Sharing protocol logic with TCP side
- Utilizing security limit properties
' UDP server set protocol
m_oUdp.PacketProtocol = ppLengthHeader
m_oUdp.HeaderBytes = 4
m_oUdp.MaxPacketSize = 65536 ' UDP single packet usually doesn't exceed 64KB
' UDP virtual client automatically inherits protocol configuration
Private Sub m_oUdp_MessageArrival(Client As cWinsock, ByVal bytesTotal As Long)
Dim sData As String
sData = Client.GetDataText()
Debug.Print "UDP complete message: " & sData
End SubComplete Examples
TCP Server + Length Header Protocol + Heartbeat
Private WithEvents m_oServer As cWinsock
Private Sub Form_Load()
Set m_oServer = New cWinsock
' Set packet protocol (Recommended: length header protocol)
m_oServer.PacketProtocol = ppLengthHeader
m_oServer.HeaderBytes = 4
m_oServer.HeaderEndian = eeLittleEndian
m_oServer.MaxPacketSize = 1048576 ' 1MB
m_oServer.MaxBufferSize = 4194304 ' 4MB
' Set heartbeat
m_oServer.AutoHeartbeat = True
m_oServer.HeartbeatTimeout = 120 ' 2 minutes timeout
' Start server
m_oServer.Listen 8080
Debug.Print "Server started"
End Sub
Private Sub m_oServer_ConnectionRequest(Client As cWinsock, ByRef DisConnect As Boolean)
Debug.Print "New client: " & Client.RemoteHostIP & ":" & Client.RemotePort
' New clients automatically inherit protocol and heartbeat configuration
End Sub
Private Sub m_oServer_MessageArrival(Client As cWinsock, ByVal bytesTotal As Long)
' 100% is complete message
Dim sData As String
sData = Client.GetDataText()
Debug.Print "[" & Client.Tag & "] " & sData
' Echo
Client.SendData "Echo: " & sData
End Sub
Private Sub m_oServer_ClientTimeout(Client As cWinsock)
Debug.Print "Client timeout: " & Client.Tag
End Sub
Private Sub m_oServer_CloseEvent(Client As cWinsock)
Debug.Print "Client disconnected: " & Client.Tag
End Sub
Private Sub Form_Unload(Cancel As Integer)
On Error Resume Next
m_oServer.Close_
End SubTCP Client + Length Header Protocol + Heartbeat
Private WithEvents m_oClient As cWinsock
Private Sub Form_Load()
Set m_oClient = New cWinsock
' Set packet protocol (Must be consistent with server)
m_oClient.PacketProtocol = ppLengthHeader
m_oClient.HeaderBytes = 4
m_oClient.HeaderEndian = eeLittleEndian
' Set heartbeat
m_oClient.AutoHeartbeat = True
m_oClient.HeartbeatInterval = 50 ' 50 seconds interval
' Connect
m_oClient.Connect "127.0.0.1", 8080
End Sub
Private Sub m_oClient_Connect(Client As cWinsock)
Debug.Print "Connected"
Client.SendData "Hello, Server!"
End Sub
Private Sub m_oClient_MessageArrival(Client As cWinsock, ByVal bytesTotal As Long)
Dim sData As String
sData = Client.GetDataText()
Debug.Print "Received: " & sData
End Sub
Private Sub m_oClient_HeartbeatSent(Client As cWinsock)
Debug.Print "Heartbeat sent"
End Sub
Private Sub Form_Unload(Cancel As Integer)
On Error Resume Next
m_oClient.Close_
End SubChat Server + Delimiter Protocol
Private WithEvents m_oServer As cWinsock
Private Sub Form_Load()
Set m_oServer = New cWinsock
' Use newline as message delimiter
m_oServer.PacketProtocol = ppDelimiter
m_oServer.Delimiter = vbCrLf
' Heartbeat keepalive
m_oServer.AutoHeartbeat = True
m_oServer.HeartbeatTimeout = 180
m_oServer.Listen 9090
End Sub
Private Sub m_oServer_MessageArrival(Client As cWinsock, ByVal bytesTotal As Long)
Dim sMsg As String
sMsg = Client.GetDataText()
' Broadcast to all clients
Dim oClient As cWinsock
For Each oClient In m_oServer.Clients
If Not oClient Is Client Then
oClient.SendData "[" & Client.Tag & "] " & sMsg
End If
Next
End SubBest Practices
1. Choose Appropriate Protocol Type
| Protocol Type | Use Case | Pros/Cons |
|---|---|---|
ppLengthHeader | Binary protocol, variable-length messages | Most universal, recommended |
ppDelimiter | Text protocol (chat, command-line style) | Simple and intuitive, but data cannot contain delimiter |
ppFixedLength | Fixed-format messages (status packets, sensor data) | Fastest parsing, but not flexible |
2. Use MessageArrival in Protocol Mode
' ✅ Correct: Use MessageArrival in protocol mode
Private Sub m_oServer_MessageArrival(Client As cWinsock, ByVal bytesTotal As Long)
Dim sData As String
sData = Client.GetDataText() ' Guaranteed to be complete message
End Sub
' ❌ Wrong: Use DataArrival in protocol mode
' DataArrival won't trigger in protocol mode3. Length Header Protocol Byte Order
' Use big-endian when communicating with C/Java server
m_oServer.HeaderEndian = eeBigEndian
' Can use little-endian for pure VB6 internal communication (default)
m_oServer.HeaderEndian = eeLittleEndian4. Configure Security Limits
' Adjust security limits based on business requirements
m_oServer.MaxPacketSize = 524288 ' 512KB, max single packet
m_oServer.MaxBufferSize = 8388608 ' 8MB, max buffer5. Configure Server Protocol Before Listen
' ✅ Correct: Set protocol before Listen
m_oServer.PacketProtocol = ppLengthHeader
m_oServer.HeaderBytes = 4
m_oServer.Listen 8080
' New clients automatically inherit server configuration, create independent protocol instances6. Heartbeat Cooperates with Protocol
Heartbeat packets go through protocol encoding, won't pollute protocol state machine, no need to manually filter heartbeats.
FAQ
❓ Why use packet protocol?
TCP is a stream protocol without message boundaries. Without using protocol, DataArrival may receive incomplete or sticky data, requiring manual stitching/splitting, prone to errors. After using packet protocol, MessageArrival triggers with one complete message each time, developer doesn't need to care about underlying byte stream merging/splitting.
❓ What's the difference between MaxPacketSize and MaxBufferSize?
MaxPacketSize: Maximum length of single message, for declared message body length in length header protocolMaxBufferSize: Receive buffer accumulation upper limit, prevents large numbers of incomplete packets slowly consuming memory
❓ Will DataArrival still trigger in protocol mode?
No. In protocol mode only MessageArrival is triggered, avoiding same data being read twice by two events. No-protocol mode still triggers DataArrival.
❓ Will heartbeat packets affect protocol parsing?
No. Heartbeat data is sent and received through protocol encoding, won't pollute protocol state machine.
❓ Does UDP need packet protocol?
UDP naturally has message boundaries, doesn't need packet protocol to solve sticky packet issues. But UDP clients also support protocol mode, can be used for unified message format, utilizing security limit properties, etc.
❓ How large a message can 2-byte length header transmit?
2-byte header can represent 0~65535 (max about 64KB). Exceeding 65535 bytes will error. Recommend using 4-byte header for large data.
Related Documentation
| Document | Description |
|---|---|
| Properties Reference | Detailed description of protocol and heartbeat related properties |
| Events Reference | MessageArrival, ClientTimeout and other events |
| Methods Reference | GetDataText, GetDataByteArray and other methods |
| TCP Programming | TCP client and server programming guide |
| Best Practices | Solutions for common scenarios and performance optimization recommendations |
Last Updated: 2026-06-09