首页 > 其他分享 >手写协议报文 c语言手法

手写协议报文 c语言手法

时间:2024-04-23 10:55:46浏览次数:31  
标签:__ 报文 uint8 编译器 手法 手写 序列化 结构

鉴于绝大部分文件、网络通信协议、非网络通信协议都有类似的结构{类型,长度,校验,不定长数据,结束标志},再高级点的会包含多个单层TLV,甚至嵌套TLV,状态机流转标志等等。所以编程语言上也需要采用一定的手法。

建立结构

结构体和联合体

例如

//结构体对齐宏
#if defined(__GNUC__)
#define PACKED_STRUCT struct __attribute__((packed))
#endif
#include <stdint.h> //for uint8_t etc.

PACKED_STRUCT Frame {
          uint8_t     HEADER;
          uint8_t     ftype;
          uint16_t    len;
          PACKED_STRUCT Payload payload;
};

struct Data {
  union {
    PACKED_STRUCT Frame frame;
    uint8_t rawdata[];
  };
} data;

使用技巧

这样就可以通过结构体成员名直接操作

  • 写入:data.frame.ftype = 0x89 直接填充。
  • 读取:通过 data.rawdata[i] 直接取到完整的二进制数据。(i表示第i个字节),可以通过 %02X 将该rawdata打印出来

调试/单元测试:可以搞个读写回环,保证数据正确后再接入实际功能代码。

位域

//记得使用packed属性告诉编译器不要瞎优化我的结构体
struct __attribute__((packed))  DataBitm {
	uint8_t flag: 1;
	uint8_t type: 4;
	uint8_t len:  3;
	uint32_t data[];
}

冒号 :1代表该变量只占用1bit。 例如上面代码,type占用了4bit。我们序列化/反序列化的时候就可以直接取到内存映射上的4bit,而无需做位运算。

序列化(serialization)在计算机科学的资料处理中,是指将数据结构或对象状态转换成可取用格式(例如存成文件,存于缓冲,或经由网络中发送),以留待后续在相同或另一台计算机环境中,能恢复原先状态的过程。 依照序列化格式重新获取字节的结果时,可以利用它来产生与原始对象相同语义的副本。反序列化就是它的逆过程。

统一类型

记住这个C99标准库头文件 #include <stdint.h>,里面的类型 uint8_t到uint32_t足够使用,在所有支持C99编译器上均可使用,包括常见的单片机编译平台,不需要自己定义一些奇奇怪怪又不完全通用的什么u8 u32 U32 UINT32这样的类型。

需要注意大小端问题

这个过程需要注意CPU大小端问题,以下是一些转换宏可以用

//大小端转换,其实就是字节换位
#define BIG_TO_LITTLE_ENDIAN_16(x) ((uint16_t)((((x) & 0xFF00) >> 8) | (((x) & 0x00FF) << 8)))
#define BIG_TO_LITTLE_ENDIAN_32(x) ((uint32_t)((((x) & 0xFF000000) >> 24) | (((x) & 0x00FF0000) >> 8) | (((x) & 0x0000FF00) << 8) | (((x) & 0x000000FF) << 24)))

C99特性 - 柔性数组

另外还可以使用柔性数组来做不定长的协议报文,关键词: C99 柔性数组,但柔性数组只能用在结构体的尾部,所以如果有帧尾,一般不把帧尾放入结构体里,而是在函数里手动填充。

然后在填充报文的函数里再做switch分支即可完成协议的子类解析。
复制报文数据到发送区的时候一般使用 memcpy(dst,src,size);

状态机流转

最好画一个状态机的图便于分析和修改,这个有很多画流程图的软件都可以做到。

参考案例

接下来我们观赏一段英特尔处理媒体流协议的代码,是RTP协议下的子类。
GITHUB项目地址:https://github.com/OpenVisualCloud/Media-Transport-Library

数据填充

image

告诉编译器不优化结构体

attribute ((packed)) 的作用就是告诉编译器取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐,是GCC特有的语法。当然其他编译器也有类似的属性名

image

结构体和联合体。

image

标签:__,报文,uint8,编译器,手法,手写,序列化,结构
From: https://www.cnblogs.com/yucloud/p/18098167/protocols_with_c_struct_union

相关文章

  • Siemens 西门子 S7协议及报文格式详解
    一、简介S7Comm(S7Communication)是西门子专有的协议,是西门子S7通讯协议簇里的一种。S7通信协议是西门子S7系列PLC内部集成的一种通信协议,是S7系列PLC的精髓所在。它是一种运行在传输层之上的(会话层/表示层/应用层)、经过特殊优化的通信协议,其信息传输可以基于MPI网络、PRO......
  • vue3 优化ai生成的手写签名
    下面是baiduai生成的代码:在Vue3中实现手写签名功能,可以使用canvas元素来创建一个绘图区域,并监听鼠标事件来实现签名的记录。以下是一个简单的例子:vue<template><div><canvasref="signatureCanvas"@mousedown="startSigning"@mousemove="updat......
  • 手写简单的RPC
    手写简单的RPC1.何为RPCRPC(RemoteProcedureCall,远程过程调用)是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。在OSI网络通信模型中,RPC跨越了传输层和应用层。RPC使得开发包括......
  • 大报文之道:优化策略与实践
    写在前面在做正常的需求开发时,当我们提供了一个接口或是调用别人接口时,我们需要考虑接口除了正常的逻辑处理外,还需要考虑接口能接收报文的上限,性能,响应时间等一系列非功能性需求。如果不注意这些问题,就可能在某一天的某个时刻收到一系列系统告警,严重者甚至导致系统不可用,引发线上......
  • 06、配置设备忽略报文目的MAC地址
    配置设备忽略报文目的MAC地址通过配置设备忽略报文的目的MAC地址,可以使目的MAC地址为非本设备三层接口MAC地址的报文也能够查找路由表进行转发。背景信息在网络维护的过程中经常会镜像流量,然后将此流量引到特定设备进行分析。而一般情况下设备只会将目的MAC地址为本设备三......
  • 04、VXLAN报文格式
    VXLAN报文格式VXLAN是MACinUDP的网络虚拟化技术,所以其报文封装是在原始以太报文之前添加了一个UDP封装及VXLAN头封装。具体报文格式如图1所示。图1 VXLAN报文格式表1 VXLAN报文格式说明字段描述VXLANheaderVXLANFlags:8比特,取值为00001000。VNI:VXLAN......
  • 03、VRRP协议报文
    VRRP协议报文VRRP协议报文用来将Master设备的优先级和状态通告给同一备份组的所有Backup设备。VRRP协议报文封装在IP报文中,发送到分配给VRRP的IP组播地址。在IP报文头中,源地址为发送报文接口的主IP地址(不是虚拟IP地址),目的地址是224.0.0.18,TTL是255,协议号是112。主IP地址(Prima......
  • 从零手写实现 apache Tomcat-01-入门介绍
    自己如何实现?要实现一个简单版本的Tomcat,整体思路如下了解Tomcat的基本原理:Tomcat是一个开源的JavaServlet容器和Web服务器,它能够运行JavaServlet和JavaServerPages。Tomcat是基于Java的,它是用Java编写的。创建一个简单的HTTP服务器:创建一个Jav......
  • eBPF指定网口丢弃icmp报文
    安装eBPF依赖#安装编译工具aptinstall-yllvmclang#确认内核具有BTF支持,路径存在,内核没有BTF支持,使用vmlinux.h无法通过编译ls/sys/kernel/btf#生成vmlinux.h#aptinstall-ylinux-tools-genericaptinstall-ylinux-tools-6.5.0-26-genericbpftoolbtfdump......
  • 基于PyTorch框架的多层全连接神经网络实现MNIST手写数字分类
    基于PyTorch框架的多层全连接神经网络实现MNIST手写数字分类简单的三层全连接神经网络导入了PyTorch相关的库,定义了一个名为SimpleNet的类,继承自nn.Module,这个神经网络有三个全连接层,分别是layer1、layer2和layer3。在初始化函数__init__中,指定了输入维度in_dim、两个隐藏层的神......