[分享]一个用于处理“简单的自定义通信协议被拆分”的缓存类
定义一套较简单的自定义通信协议,用于在串口或者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