You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用C#解析KLV格式字节数组并构造KLV发送数据

C# 实现UART通信场景下KLV格式数据的解析与构造

KLV是嵌入式串行通信中非常通用的载荷格式,核心结构为键(Key)-长度(Length)-值(Value) 三元组,不同协议的差异仅在于Key的字节长度、Length的编码规则(定长/可变长BER)、字节序(大端/小端),以下实现默认采用工业场景最常用的配置:1字节Key、2字节大端无符号数表示Length,你可以对照自己的设备协议调整参数即可。


接收端:字节数组解析KLV

解析核心流程是顺序遍历字节流,按三元组结构逐段读取,核心要处理UART流式传输带来的半包、粘包问题:

  • 先跳过帧前导码、无效填充字节,定位到第一个有效KLV条目的起始偏移
  • 按协议定义长度读取Key,映射对应的业务字段类型
  • 按Length编码规则读取Value的字节长度
  • 按长度读取对应长度的Value字节,根据Key对应的类型(整数、浮点数、字符串、结构体)转成业务值
  • 偏移读取指针到下一个KLV条目起始位置,重复上述流程直到缓冲区剩余字节不足一个最小KLV单元,剩余字节缓存后和下次接收数据拼接再解析

解析代码示例

// 存储解析后的KLV条目
public struct KlvItem
{
    public byte Key;
    public byte[] Value;
}

/// <summary>
/// 解析KLV格式原始字节数组
/// </summary>
/// <param name="rawData">UART接收到的字节数组</param>
/// <param name="keyLen">Key字段字节长度,默认1</param>
/// <param name="lenFieldLen">Length字段字节长度,默认2</param>
/// <param name="isLittleEndian">Length/多字节Value是否为小端序,默认大端</param>
/// <returns>解析完成的KLV条目列表</returns>
public List<KlvItem> ParseKlv(byte[] rawData, int keyLen = 1, int lenFieldLen = 2, bool isLittleEndian = false)
{
    var result = new List<KlvItem>();
    int offset = 0;
    while (offset < rawData.Length)
    {
        // 剩余字节不足最小KLV单元,判定为半包,直接退出留待下次拼接解析
        if (offset + keyLen + lenFieldLen > rawData.Length) break;

        // 读取Key
        byte key = rawData[offset];
        offset += keyLen;

        // 读取Value长度
        byte[] lenBytes = new byte[lenFieldLen];
        Buffer.BlockCopy(rawData, offset, lenBytes, 0, lenFieldLen);
        offset += lenFieldLen;
        if (isLittleEndian) Array.Reverse(lenBytes);
        int valueLen = BitConverter.ToUInt16(lenBytes, 0);

        // 剩余字节不足Value长度,判定为半包
        if (offset + valueLen > rawData.Length) break;

        // 读取Value
        byte[] value = new byte[valueLen];
        Buffer.BlockCopy(rawData, offset, value, 0, valueLen);
        offset += valueLen;

        result.Add(new KlvItem { Key = key, Value = value });
    }
    return result;
}

Value转业务值示例

解析得到的Value是原始字节数组,根据Key对应的字段类型转换即可:

List<KlvItem> klvItems = ParseKlv(uartRecvBuffer);
foreach (var item in klvItems)
{
    switch (item.Key)
    {
        case 0x01:
            // Key=0x01 对应2字节无符号数,代表设备电压,单位mV
            if (item.Value.Length == 2)
            {
                ushort voltage = BitConverter.ToUInt16(item.Value, 0);
                // 业务逻辑处理
            }
            break;
        case 0x02:
            // Key=0x02 对应ASCII编码字符串,代表设备序列号
            string serial = Encoding.ASCII.GetString(item.Value).TrimEnd('\0');
            break;
        // 其余Key按协议定义依次处理
    }
}

发送端:构造KLV格式发送帧

构造流程和解析完全反向,逐个拼接三元组后,再按协议要求添加帧头、校验、帧尾即可:

/// <summary>
/// 构造单个KLV条目的字节数组
/// </summary>
public byte[] BuildSingleKlv(byte key, byte[] value, int keyLen = 1, int lenFieldLen = 2, bool isLittleEndian = false)
{
    List<byte> itemBuffer = new List<byte>();
    // 写入Key
    itemBuffer.Add(key);
    // 写入Value长度
    ushort valueLen = (ushort)value.Length;
    byte[] lenBytes = BitConverter.GetBytes(valueLen);
    if (!isLittleEndian) Array.Reverse(lenBytes);
    itemBuffer.AddRange(lenBytes);
    // 写入Value
    itemBuffer.AddRange(value);
    return itemBuffer.ToArray();
}

// 构造完整发送帧示例
List<byte> sendFrame = new List<byte>();
// 写入协议规定的帧头 0xAA 0x55
sendFrame.Add(0xAA);
sendFrame.Add(0x55);
// 添加KLV载荷1:Key=0x10 代表设置LED亮度,Value为0x64(100%亮度)
sendFrame.AddRange(BuildSingleKlv(0x10, new byte[] { 0x64 }));
// 添加KLV载荷2:Key=0x11 代表设置上报间隔,Value为1000ms(2字节大端无符号数)
byte[] intervalVal = BitConverter.GetBytes((ushort)1000);
Array.Reverse(intervalVal);
sendFrame.AddRange(BuildSingleKlv(0x11, intervalVal));
// 写入异或校验位
byte checksum = 0;
foreach (byte b in sendFrame) checksum ^= b;
sendFrame.Add(checksum);
// 写入帧尾 0x0D
sendFrame.Add(0x0D);
// sendFrame.ToArray()即可直接通过UART串口发送

踩坑提示

  • 调不通优先核对字节序、字段长度,90%的KLV通信异常都是大小端不匹配、Key/Length字段长度和设备端定义不一致导致的
  • 不要忽略半包处理,UART是流式传输,没有天然包边界,一次Receive拿到的可能是半个包也可能是多个包拼在一起
  • 如果协议采用可变长BER编码的Length字段(常见于MISB地理信息、智能卡类协议),只需要替换固定读取Length的逻辑为BER解码逻辑,整体流程不变

内容的提问来源于stack exchange,提问作者user1610609

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 19:03:29