| 网站首页 | 业界新闻 | 小组 | 威客 | 人才 | 下载频道 | 博客 | 代码贴 | 在线编程 | 编程论坛
欢迎加入我们,一同切磋技术
用户名:   
 
密 码:  
共有 976 人关注过本帖
标题:[分享]一个用于处理“简单的自定义通信协议被拆分”的缓存类
只看楼主 加入收藏
jiashie
Rank: 8Rank: 8
等 级:贵宾
威 望:10
帖 子:237
专家分:999
注 册:2009-4-30
结帖率:100%
收藏
已结贴  问题点数:20 回复次数:3 
[分享]一个用于处理“简单的自定义通信协议被拆分”的缓存类
定义一套较简单的自定义通信协议,用于在串口或者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
搜索更多相关主题的帖子: 缓存 通信协议 定义 拆分 分享 
2010-07-14 11:24
风吹过b
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:364
帖 子:4940
专家分:30047
注 册:2008-10-15
收藏
得分:20 
我采用的方法是:

把数据依次存到缓存里.也是像你这样的连接起来.

但我使用 split 命令进行分割.
然后判断最后一段,如果是完整的,也纳入处理,否则就放到缓存里,等待下一次的数据.

不过,我没有去使用类.我是在 winsock 里使用的.所以直接缓存就建在 winsock 的静态变量里.

 winsock.getdata j
缓存=缓存 & j

fj=split(缓存,分隔符)
  for i=0 to ubound(Fj)-1         
    '处理所有数据
  next i
if len( fj(ubound(fj))) = len(分隔符) +  命令长度 then
    '处理最后一段数据
    缓存=""
else
    缓存=fj(ubound(fj))
end if

-------------------
   我用到的 命令封装是:
   # + 单位数字编号 + 命令及数据(长度为 16+N) + !
   # + 命令及数据(长度为 16+N) + !
二种.
   不过分别位于不同的连接动作中,不会在一个连接动用中出现混用.
   我发第一种包,收第二种包.
   分隔符为 #
   判断包是否结束就是用 ! 是否为最后一个字符 来判断,
  
   然后取前 16 字节送去解密 , 产生一个结构 数据
   然后根据结构里 的 cmd 字度 (数字) 来进行对应的操作.

   

授人于鱼,不如授人于渔
早已停用QQ了
2010-07-14 13:00
jiashie
Rank: 8Rank: 8
等 级:贵宾
威 望:10
帖 子:237
专家分:999
注 册:2009-4-30
收藏
得分:0 
回复 2楼 风吹过b
起初我也是直接把接收的数据保存在某个变量里的,但是后来下位机又增加了一些命令,而且和以前的命令格式还有差别,就给整混乱了。
看来罪魁祸首还是没有事先定义好可扩展的协议的格式,需求又老是变动。
2010-07-14 15:09
风吹过b
Rank: 20Rank: 20Rank: 20Rank: 20Rank: 20
等 级:贵宾
威 望:364
帖 子:4940
专家分:30047
注 册:2008-10-15
收藏
得分:0 
我自己写的程序之间使用 winsock 通讯时。我的命令编码 一般都是使用 三位。

如 100 是开始问好, 101  就是回答问好。
2XX 是一类动作 ,   29X 就是错误标志。
3XX 又是一类动作,  39X 也就是错误标志。

如果是 com 口通讯的话。  我认为可以使用  00 - 7F 为命令符,然后后面再带其它数值。


每节定长有定长的好处, 容易发现错误。好处理。
每节不定长也有不定长的好处,灵活。




授人于鱼,不如授人于渔
早已停用QQ了
2010-07-14 20:27
快速回复:[分享]一个用于处理“简单的自定义通信协议被拆分”的缓存类
数据加载中...
 
   



关于我们 | 广告合作 | 编程中国 | 清除Cookies | TOP | 手机版

编程中国 版权所有,并保留所有权利。
Powered by Discuz, Processed in 0.033760 second(s), 8 queries.
Copyright©2004-2024, BCCN.NET, All Rights Reserved