通信协议的解析:字节流协议的解析

本文介绍了一种解析字节流协议的基本步骤,包括判断帧的开始、获取完整帧、根据命令字创建不同命令并进行处理。通过模板方法解析帧,并使用命令模式具体处理每一个解析出来的帧。提供了具体的解析例子,易于实际应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

由于字节流协议具有共同的特点,因此解析也就很简单,主要涉及两种设计模式:模板方法,命令模式。

解析的基本步骤:

1判断一个完整帧的开始,根据协议规定判断帧的开始字节

2获取一个完整的帧。主要有两种,一种是根据根据帧的开始字节结束字节判断一个完整的帧,一种是根据开始帧和帧长度判断

3得到一个完整的帧后,根据命令字创建不同的命令,根据不同的命令字分别处理。

通过模板方法解析帧,由于不同的命令具体组成不一样,所以根据命令字创建不同的命令,使用命令模式,具体处理每一个解析出来的帧。

以下为一个具体的解析例子,只需要简单修改,就可以在实际中使用

public class DataProcess
{
  protected List<byte> fragment = new List<byte>();//保存上次处理后,剩余的字节


  /// <summary>
  /// 对外调用的方法
  /// </summary>
  /// <param name="newReceivedData">新接收到的字节数据</param>
  public void ProcessData(byte[] newReceivedData)
  {
    try
    {
      fragment.AddRange(newReceivedData);//把新收到的数据和上次处理的数据合并
      var data = new List<byte>();
      data.AddRange(fragment);
      List<byte[]> 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("处理设备数据出错。");
    }
  }


  /// <summary>
  /// 循环解析帧
  /// </summary>
  /// <param name="data"></param>
  /// <returns></returns>
  protected List<byte[]> ParseFrames(List<byte> data)
  {
    List<byte[]> Frames = new List<byte[]>();
    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;
  }


  /// <summary>
  /// 需要根据实际情况重写
  /// </summary>
  /// <param name="frame"></param>
  protected void ProcessFrame(byte[] frame)
  {
    int commandTypeIndex = 2;//第三个字节规定命令类型
    int commandByte = frame[commandTypeIndex];
    switch (commandByte) //根据命令的不同,生成的command分别处理,使用命令模式
    {
      case 1:
        //
        break;
      case 2:
        break;
      case 3:
        break;
    }


  }


  /// <summary>
  /// 处理解析帧后,剩余的数据
  /// </summary>
  /// <param name="frag"></param>
  protected void SaveFragment(List<byte> frag)
  {
    int maxFragmentLength = 1000; //未处理的帧的最大长度
    //遗留数据片段过长,有问题。未防止内存压力过大,需要清空fragment
    if (frag.Count > maxFragmentLength)
    {
      frag = new List<byte>();
    }
    fragment.Clear();
    fragment.AddRange(frag);
    if (frag.Count > 0)
    {
      log(string.Format("剩余数据片段:{0}", frag));
    }


  }


  /// <summary>
  /// 需要根据实际情况重写
  /// </summary>
  /// <param name="data"></param>
  /// <returns></returns>
  private static int GetFrameStartIndex(List<byte> 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;//默认值,没有找到帧头
  }


  /// <summary>
  /// 需要根据实际情况重写
  /// </summary>
  /// <param name="datas"></param>
  /// <returns></returns>
  private static int GetFrameEndIndex(List<byte> 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;//帧不完整
  }
  /// <summary>
  /// 获取一个完整的帧的所有字节
  /// </summary>
  /// <param name="data"></param>
  /// <param name="frameStartIndex">帧的开始位置</param>
  /// <param name="frameEndIndex">帧的结束位置</param>
  /// <returns></returns>
  private static byte[] GetOneFrame(List<byte> 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)
  {


  }


}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值