[分享]一个用于处理“简单的自定义通信协议被拆分”的缓存类
定义一套较简单的自定义通信协议,用于在串口或者winsock通信中。比如,发送方:“@12L05@ + 固定长度的数据 ”表示 某个意思
接收方检测到(从串口或winsock)接收的字符串中包含了特定的命令标志“@12L05@”,就把之后的固定长度的数据保存下来(或者其它处理)。
正常情况下,如果“@12L05@ + 固定长度的数据 ”一次性接收完成,在接收方检测命令和提取数据是不会出问题的。但是,当一个完整的“数据包”被拆分到达之后,在接收方就有可能检测不到或者取数据不正常。
比如:“@12L05@ + 固定长度的数据 ”被拆分成“上一次未发送完的n个字节的数据 + @12” ,到达接收方后,检测不到“@12L05@”,于是认为是无关的数据,丢弃,下次接收到剩下的“L05@ + m长度的数据 ”,仍然检测不到“@12L05@”,再次丢弃。如此循环,有可能永远都收不到数据了。
为了解决这种类型的问题,先把接收到的数据放到缓存,再检测当前的缓存中是否包含指定的命令,且命令后的数据长度符合要求。如果符合,则提取出一个完整的“数据包”,并从缓存中清除。从而防止了数据包被拆分到达接收端后无法识别的问题。
代码如下:
程序代码:Option Explicit
Private m_Cache As String
Private m_Count As Long
Private Type CMD_TYPE
cmd_Start As String
cmd_Len As Long
End Type
Private m_Cmd() As CMD_TYPE
'Private m_Cmd As Dictionary
Private cmdCount As Long
Private Const ERROR_USER As Long = 531
Public Function SetData(ByVal strValue As String) As Long
m_Cache = m_Cache & strValue
m_Count = m_Count + Len(strValue)
End Function
Public Function GetData(Optional ByVal bGetAll As Boolean = False) As String
On Error GoTo errHandler
Dim i As Long
Dim j As Long
Dim nPos As Long
Dim nIndex As Long
Dim strRet As String
If bGetAll Then
GetData = m_Cache
Exit Function
End If
'从头开始查找第一个符合命令的位置,避免由于cmd的添加顺序不同,先取出后面的数据,而丢弃前面的数据
nPos = m_Count
For i = 0 To cmdCount - 1
j = InStr(1, m_Cache, m_Cmd(i).cmd_Start)
If j = 1 Then
nPos = j
nIndex = i
Exit For
Else
If j < nPos And j > 0 Then
nPos = j
nIndex = i
End If
End If
Next
If nPos > 0 And nPos < m_Count And nPos + m_Cmd(nIndex).cmd_Len - 1 <= m_Count Then
strRet = Mid$(m_Cache, nPos, m_Cmd(nIndex).cmd_Len)
m_Cache = Mid$(m_Cache, nPos + m_Cmd(nIndex).cmd_Len)
m_Count = m_Count - nPos - m_Cmd(nIndex).cmd_Len + 1
End If
GetData = strRet
Exit Function
errHandler:
Debug.Print Err.Number, Err.Description
#If debug_mode Then
Stop: Resume
#End If
GetData = ""
End Function
Public Function ClearData() As Long
m_Cache = ""
m_Count = 0
End Function
Public Function AddCmd(ByVal cmdStart As String, ByVal cmdLen As Long) As Long
On Error GoTo errHandler
'TODO:检查是否已有重复的cmd
If cmdStart = "" Or cmdLen < Len(cmdStart) Then
Err.Raise ERROR_USER, , "命令格式错误"
End If
cmdCount = cmdCount + 1
ReDim Preserve m_Cmd(cmdCount - 1) As CMD_TYPE
m_Cmd(cmdCount - 1).cmd_Start = cmdStart
m_Cmd(cmdCount - 1).cmd_Len = cmdLen
AddCmd = 0
Exit Function
errHandler:
Debug.Print Err.Number, Err.Description
#If debug_mode Then
Stop: Resume
#End If
AddCmd = -1
End Function
Private Sub Class_Initialize()
m_Cache = ""
m_Count = 0
cmdCount = 0
ReDim m_Cmd(cmdCount) As CMD_TYPE
End Sub
Private Sub Class_Terminate()
Erase m_Cmd
m_Cache = ""
End Sub
测试代码如下:
程序代码:Private Sub Command1_Click() Dim a As Cache Set a = New Cache a.SetData "@12L05@helloworld" a.SetData "1234567890" Debug.Print a.GetData, a.GetData(True) a.AddCmd "@12L05@", 27 Debug.Print a.GetData a.SetData "@12L05@helloworld" Debug.Print a.GetData, a.GetData(True) a.ClearData Debug.Print "拆分的命令" a.SetData "abcdefg@12L" Debug.Print a.GetData, a.GetData(True) a.SetData "05@1234567890" Debug.Print "数据长度不足" Debug.Print a.GetData, a.GetData(True) a.SetData "1234567890abcdef" Debug.Print "长度已足够,能够识别" Debug.Print a.GetData, a.GetData(True) a.AddCmd "fml", 10 a.SetData "mlzhangwj@12L05@helloworld1234567890" Debug.Print a.GetData, a.GetData(True) Set a = Nothing End Sub







