由于字节流协议具有共同的特点,因此解析也就很简单,主要涉及两种设计模式:模板方法,命令模式。
解析的基本步骤:
1判断一个完整帧的开始,根据协议规定判断帧的开始字节
2获取一个完整的帧。主要有两种,一种是根据根据帧的开始字节结束字节判断一个完整的帧,一种是根据开始帧和帧长度判断
3得到一个完整的帧后,根据命令字创建不同的命令,根据不同的命令字分别处理。
通过模板方法解析帧,由于不同的命令具体组成不一样,所以根据命令字创建不同的命令,使用命令模式,具体处理每一个解析出来的帧。
以下为一个具体的解析例子,只需要简单修改,就可以在实际中使用
[csharp] view plaincopy
public class DataProcess
{
protected List fragment = new List();//保存上次处理后,剩余的字节
///
/// 对外调用的方法
///
/// 新接收到的字节数据
public void ProcessData(byte[] newReceivedData)
{
try
{
fragment.AddRange(newReceivedData);//把新收到的数据和上次处理的数据合并
var data = new List();
data.AddRange(fragment);
List frames = ParseFrames(data);//解析帧
if (frames.Count > 0)
{
try
{
for (int i = 0; i < frames.Count; i++)
{
log(string.Format("处理数据:{0}", frames[i]));
ProcessFrame(frames[i]);//处理帧
}
}
catch (Exception ex)
{
throw ex;
}
}
SaveFragment(data);//保存此次处理后剩余的片段
}
catch (Exception ex)
{
log("处理设备数据出错。");
}
}
///
/// 循环解析帧
///
///
///
protected List ParseFrames(List data)
{
List Frames = new List();
int frameStartIndex = GetFrameStartIndex(data);//判断帧的开始
while (frameStartIndex >= 0)
{
int frameEndIndex = GetFrameEndIndex(data, frameStartIndex);//判断帧的结束
if (frameEndIndex < 0)//帧不完整
{
return Frames;
}
byte[] OneFramebyte = GetOneFrame(data, frameStartIndex, frameEndIndex);
Frames.Add(OneFramebyte);
//data.RemoveRange(0, frameStartIndex);//可以有这一句,避免不完整的帧,对后续解析造成影响
data.RemoveRange(frameStartIndex, frameEndIndex - frameStartIndex);//移除已经处理的数据
frameStartIndex = GetFrameStartIndex(data);
}
return Frames;
}
///
/// 需要根据实际情况重写
///
///
protected void ProcessFrame(byte[] frame)
{
int commandTypeIndex = 2;//第三个字节规定命令类型
int commandByte = frame[commandTypeIndex];
switch (commandByte) //根据命令的不同,生成的command分别处理,使用命令模式
{
case 1:
//
break;
case 2:
break;
case 3:
break;
}
}
///
/// 处理解析帧后,剩余的数据
///
///
protected void SaveFragment(List frag)
{
int maxFragmentLength = 1000; //未处理的帧的最大长度
//遗留数据片段过长,有问题。未防止内存压力过大,需要清空fragment
if (frag.Count > maxFragmentLength)
{
frag = new List();
}
fragment.Clear();
fragment.AddRange(frag);
if (frag.Count > 0)
{
log(string.Format("剩余数据片段:{0}", frag));
}
}
///
/// 需要根据实际情况重写
///
///
///
private static int GetFrameStartIndex(List data)
{
byte FrameStartByte1 = 0x08;
byte FrameStartByte2 = 0x04;
//x8,0x04
for (int i = 0; i < data.Count - 1; i++)
{
if (data[i] == FrameStartByte1 && data[i + 1] == FrameStartByte2) //帧头是0x08,0x04两个字节开始
{
return i;
}
}
return -1;//默认值,没有找到帧头
}
///
/// 需要根据实际情况重写
///
///
///
private static int GetFrameEndIndex(List data, int frameStartIndex)
{
int FrameStartBytesLegnth = 2;//帧头的字节个数
if (frameStartIndex + FrameStartBytesLegnth < data.Count - 1)
{
int length = data[frameStartIndex + 2];//第四个字节规定整个帧的长度
if (length + frameStartIndex < data.Count)
{
return length + frameStartIndex;
}
return -1;//虽然包含了帧头,但不完整
}
/* 规定了帧的结束字符的情况
for (int i = frameStartIndex + 1; i < data.Count - 1; i++)
{
if (data[i] == 0x08 && data[i + 1] == 0x03) //帧头是0x08,0x03两个字节结束一个完整的帧
{
return i + 1;
}
} */
return -1;//帧不完整
}
///
/// 获取一个完整的帧的所有字节
///
///
/// 帧的开始位置
/// 帧的结束位置
///
private static byte[] GetOneFrame(List data, int frameStartIndex, int frameEndIndex)
{
var OneFramebyte = new byte[frameEndIndex - frameStartIndex];
Array.Copy(data.ToArray(), frameStartIndex, OneFramebyte, 0, frameEndIndex - frameStartIndex);
return OneFramebyte;
}
private static void log(string info)
{
}
}
阅读(620) | 评论(0) | 转发(0) |