网络数据协议包解析结构体定义问题

violet725 2017-03-15 11:59:50
协议有,看网络很多使用数组转结构体的方式解析网络数据包,项目中也想这样做,但遇到一个协议中内容数据个数不定的麻烦事,新手这个结构体搞不懂该如何定义了
协议其他省略,重点如下,请大家给予指导,不胜感激涕零
协议说明:
名称 字节数 说明
帧头 1 起始位
地址 1
命令 1
数据帧长度 4 int
提要 16 char
图像个数 1 不知道到底几个
图像1长度 4 int
图像1 n blob JPG格式
.......
图像N长度 4 int
图像N n blob JPG格式
停止位 1


前面定义如下

[StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
public struct MonitorCPSBStruct
{
/// <summary>
/// 帧头信息
/// </summary>
[MarshalAs(UnmanagedType.I1, SizeConst = 1)]
public byte Head;
/// <summary>
/// 地址
/// </summary>
[MarshalAs(UnmanagedType.I1, SizeConst = 1)]
public byte Addr;
/// <summary>
/// 命令
/// </summary>
[MarshalAs(UnmanagedType.I1, SizeConst = 1)]
public byte Cmd;
/// <summary>
/// 数据帧长度
/// </summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public byte[] DataFrameLength;
/// <summary>
/// 提要
/// </summary>
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)]
public byte[] Content;
/// <summary>
/// 图像个数
/// </summary>
[MarshalAs(UnmanagedType.I1, SizeConst = 1)]
public byte PictureNum;

//这里该怎么定义图片信息?
}

因为图片的个数不知道,及时提前约定好个数,比如2张图片
那么下面2个图片的结构该如何定义,因为每张图片的长度提前并不知道,只有收到数据后才知道
但数据这个时候还没解析出来,这直接是个矛盾啊,新手不要见笑,请给予指导,谢谢!
...全文
405 14 打赏 收藏 转发到动态 举报
写回复
用AI写文章
14 条回复
切换为时间正序
请发表友善的回复…
发表回复
violet725 2017-03-16
  • 打赏
  • 举报
回复
引用 1 楼 xuggzu 的回复:
不定长图片肯定不能放结构里定义,一般只要在结构里放图片信息即可,比如名字,长度,格式等。接收数据时根据此信息解析来的数据。
那你这没办法使用这种方式解析数据了吧,只能自己循环遍历分析了啊?
crystal_lz 2017-03-16
  • 打赏
  • 举报
回复
对于你这样的数据格式 我从来都不定义 结构体 直接拿着byte进行解析 比如写成一个XXX的类 然后里面差不多有两个静态方法GetXXXFormByte(byte[]); GetByteFormXXX(XXX);
xuggzu 2017-03-16
  • 打赏
  • 举报
回复
不定长图片肯定不能放结构里定义,一般只要在结构里放图片信息即可,比如名字,长度,格式等。接收数据时根据此信息解析来的数据。
by_封爱 版主 2017-03-16
  • 打赏
  • 举报
回复
硬件定了 改不了 也就是 你要解析 协议给了 那还不简单了?
引用
名称 字节数 说明 帧头 1 起始位 地址 1 命令 1 数据帧长度 4 int 提要 16 char 图像个数 1 不知道到底几个 图像1长度 4 int 图像1 n blob JPG格式 ....... 图像N长度 4 int 图像N n blob JPG格式 停止位 1
首先 我们拿一个完整的包byte[](这个你应该会吧)来说. 前面是固定的1+1+1+4+16 然后下一个byte 就是个数 其实没什么用. 接下来是4个字节(一个int)是图片1的长度 假设是一个数字33333 那么你从当前截取33333个 不就正好是图片1了? 然后继续是4个字节 然后急需截取这长度 不就是第二个图片了? 其实没必要做成"对象" 能解析就行了. 解析出来之后 如果你为了开发方便 你在封装成 也可以啊. 大概就是

var data=串口或其他硬件获取数据();
var 头=data[0];
var 地址=data[1];
var 命令=data[2];
var 数据总长度=getInt(data.take(3).skip(4));
var 摘要=getString(data.take(7),skip(16));
var 图像个数=data[22];
for 图像个数
 {
 var 当前图形长度=data[23];
 var 当前图形=getImage(data.skip(23).take(当前图形长度));
}
当然循环体里是要根据次数以及长度做增加,伪代码而已... 所以这样一来不就能解析了? 当然这只是你收到信息做解析 做显示. 如果你要是根据协议发送数据 其实也简单 无非就是构造一大堆byte[]而已啊.

var data=list<byte>();
data.add(头)
data.add......

var img=getbyte("d://1.jpg");
data.add(img.length);
data.addrange(img);
data,add(尾);
violet725 2017-03-16
  • 打赏
  • 举报
回复
我现在就是使用这种读取字节流,然后解析数据头,获取信息的方式去解析,感觉一直在执行socket的读取,入队,判定完整性,循环解析。 一直觉得应该有更好的方法,所以来这里请教下,果然收益颇多,大家各有各的思想,集思广益,希望路过的继续发表意见,限定于对网络数据的解析方式方法上,协议已经固定的模式下,不需要考虑发送的问题,只限于网络数据包的完整性,快速正确稳定长期运行的解析上
wanghui0380 2017-03-16
  • 打赏
  • 举报
回复
比如对于Avro,对于多张图片的IDL,你可以写成 array[bytes] 他翻译成对应的C#代码为 Ilist<byte[]> 封装解析交给Avro自己完成,我们实际需要的只是定义IDL
wanghui0380 2017-03-16
  • 打赏
  • 举报
回复
请百(谷)度(歌)“Avro” ,“Thrift”,“protocolbuffer”,“hprose” ,看看现代的程序员在用什么
violet725 2017-03-16
  • 打赏
  • 举报
回复
引用 6 楼 sp1234 的回复:
“这里该怎么定义图片信息?” 按照偏移位置去扫描一次“数据帧长度、图像1长度......图像N长度”,顺序挑这几个 int 数值出来,其实你就知道图像信息数组准确大小和每一个图像byte数组的准确大小了,可以构造了一个“空”的不含数据的结构。 不过其实按照你的说法而进行“自己循环遍历分析”也非常方便,使用 binaryReader.ReadXXXX() 方式来读取数据,在 c# 中经常用到。去构造一个空的但是大小刚刚好完全按一样的 struct 对象,这其实是 c++ 的写法。在 c# 程序中很少用到。 面向的领域是不同的,应用领域越高级,则“死抠底层”的编程设计情况越少。所以越是往高级的应用程序去设计,可能就不用 struct 而只用 class 了,并且可能就不用固定二进制思维方式解析而用现成的稳定和灵活的、流行的解析方式了。
感谢sp1234的指导,阅读后受益颇深,真是知识无极限,还得继续学习啊。 也望其他大仙发表自己的看法和意见,说说大家通常的处理方式。 协议无法改变,来自硬件设备。
by_封爱 版主 2017-03-16
  • 打赏
  • 举报
回复
没必要啊 直接做成实体对象 然后弄成json 传输就行了...
  • 打赏
  • 举报
回复
“这里该怎么定义图片信息?” 按照偏移位置去扫描一次“数据帧长度、图像1长度......图像N长度”,顺序挑这几个 int 数值出来,其实你就知道图像信息数组准确大小和每一个图像byte数组的准确大小了,可以构造了一个“空”的不含数据的结构。 不过其实按照你的说法而进行“自己循环遍历分析”也非常方便,使用 binaryReader.ReadXXXX() 方式来读取数据,在 c# 中经常用到。去构造一个空的但是大小刚刚好完全按一样的 struct 对象,这其实是 c++ 的写法。在 c# 程序中很少用到。 面向的领域是不同的,应用领域越高级,则“死抠底层”的编程设计情况越少。所以越是往高级的应用程序去设计,可能就不用 struct 而只用 class 了,并且可能就不用固定二进制思维方式解析而用现成的稳定和灵活的、流行的解析方式了。
Forty2 2017-03-16
  • 打赏
  • 举报
回复
没有必要胡子眉毛一把抓啊。 比如你可以定义一个固定大小的数据头

struct 数据头
{
  public byte 帧头; //        1              起始位
  public byte 地址; //        1             
  public byte 命令; //        1               
  public int 数据帧长度; //    4            int

  [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
  public byte[] 提要; //      16               char
  public byte 图像个数; //     1              不知道到底几个
}
先读数据头,再根据 图像个数,接收数据。
  • 打赏
  • 举报
回复
引用 2 楼 violet725 的回复:
[quote=引用 1 楼 xuggzu 的回复:] 不定长图片肯定不能放结构里定义,一般只要在结构里放图片信息即可,比如名字,长度,格式等。接收数据时根据此信息解析来的数据。
那你这没办法使用这种方式解析数据了吧,只能自己循环遍历分析了啊?[/quote] 当你知道“大小”,就能初始化一个固定长度的数组了。因此实际上也不是什么“循环遍历”,而是首先从特定的偏移位置来取得“大小”这一个byte或者int之类的简单数值,然后才来整体反序列化一次。 不过,至少10年前,世界上流行的方式就是xml了。后来流行了 json,以及各种改进型。当通讯两端的版本不同,或者增加、删除字段,或者字段的顺序并不相同,通讯都不受影响,程序都不会报出 bug。(其中对于二进制数据使用 base64编码,或者单独走一个跟命令解析通道不同的专门进行二进制bytes传输通道) 使用xml或者json会更加稳定灵活。
  • 打赏
  • 举报
回复
你应该见过在结构里的字段类型为数组的吧?

110,529

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧