C# TLV格式数据解析

2 篇文章 0 订阅

1.什么是TLV格式?

TLV即Tag-Length-Value,常在IC卡与POS终端设备中通过这样的一个应用通信协议进行数据交换。 金融系统中的TLV是BER-TLV编码的一个特例编码规范,而BER-TLV是ISO定义中的规范。在TLV的定义中,可以知道它包括三个域,分别为:标签域(Tag),长度域(Length),内容域(Value)。这里的长度域的值实际上就是内容域的长度。 其实,在BER编码的方式有两种情况,一种是确定长度的方式,一种是不确定长度的方式,而金融TLV选择了确定长度的方式,这样在设备之间的数据传输量上就可以减少。
在这里插入图片描述

2.创建实体类

    /// <summary>
    /// TLV格式报文实体类
    /// </summary>
    public class TLVEntity
    {
        /// <summary>
        /// 标记
        /// </summary>
        public byte[] Tag { get; set; }
        /// <summary>
        /// 数据长度
        /// </summary>
        public byte[] Length { get; set; }
        /// <summary>
        /// 数据
        /// </summary>
        public byte[] Value { get; set; }
        /// <summary>
        /// 标记占用字节数
        /// </summary>
        public int TagSize { get { return this.Tag.Length; } }
        /// <summary>
        /// 数据长度占用字节数
        /// </summary>
        public int LengthSize { get { return this.Length.Length; } }
        /// <summary>
        /// 子嵌套TLV实体列表
        /// </summary>
        public List<TLVEntity> SubTLVEntity { get; set; }
    }

3.报文的打包与解析

    /// <summary>
    /// TLV格式报文打包解析
    /// </summary>
    public class PackageTLV
    {
        /// <summary>
        /// 根据tag获取tlv的值
        /// </summary>
        /// <param name="entities"></param>
        /// <param name="tag"></param>
        /// <returns></returns>
        public static TLVEntity GetValueByTag(List<TLVEntity> entities, string tag)
        {
            TLVEntity resultEntity = null;
            var query = entities.SingleOrDefault(e => e.Tag.ToHexString().ToUpper() == tag);
            if (query == null)
            {
                foreach (var tlv in entities)
                {
                    if (tlv.SubTLVEntity != null)
                    {
                        TLVEntity result = GetValueByTag(tlv.SubTLVEntity, tag);
                        if (result != null && result.Length.Length > 0)
                            return result;
                    }
                }
            }
            else
            {
                resultEntity = query;
            }
            return resultEntity;
        }
        /// <summary>
        /// 16进制数据转化为TVL实体
        /// </summary>
        /// <param name="resultData"></param>
        /// <returns></returns>
        public static List<TLVEntity> ToTLVEntityList(string data)
        {
            byte[] dataBytes = data.HexStringToByteArray();
            var tlvList = Construct(dataBytes);
            return tlvList;
        }
        #region TLV 实体打包
        /// <summary>
        /// 字节数组转 TLV报文打包
        /// </summary>
        /// <param name="buffer">字节数据</param>
        /// <returns></returns>
        public static List<TLVEntity> Construct(byte[] buffer)
        {
            List<TLVEntity> resultList = new List<TLVEntity>();
            int currentIndex = 0;
            while (currentIndex < buffer.Length)
            {
                TLVEntity entity = new TLVEntity();
                //1. 根据Tag判断数据是否是嵌套的TLV
                bool hasSubEntity = HasSubEntity(buffer, currentIndex);

                #region Tag解析
                entity.Tag = GetTag(buffer, currentIndex);
                currentIndex += entity.Tag.Length;
                #endregion

                #region Length解析
                entity.Length = GetLength(buffer, currentIndex);
                currentIndex += entity.Length.Length;
                #endregion
                
                #region Value解析
                int valueLength = GetValueLengthByLengthByteValue(entity.Length);
                entity.Value = buffer.Take(currentIndex + valueLength).Skip(currentIndex).ToArray();
                if (hasSubEntity)//判断是否是嵌套结构
                {
                    entity.SubTLVEntity = Construct(entity.Value);//嵌套结构递归解析
                }
                currentIndex += entity.Value.Length;
                #endregion
                resultList.Add(entity);
            }
            return resultList;
        }
        /// <summary>
        /// 是否存在嵌套实体
        /// </summary>
        /// <returns></returns>
        private static bool HasSubEntity(byte[] bytes, int index)
        {
            if (bytes.Length < index + 1)
                throw new ArgumentException("无效的索引值");
            return (bytes[index] & 0x20) == 0x20;
        }
        /// <summary>
        /// 获取Tag字节数据
        /// </summary>
        /// <param name="bytes">长度</param>
        /// <param name="index">索引位置</param>
        /// <returns></returns>
        private static byte[] GetTag(byte[] bytes, int index)
        {
            if (bytes.Length < index + 1)
            { throw new ArgumentException("无效的索引值"); }
            //判断Tag所占字节长度
            if ((bytes[index] & 0x1f) == 0x1f)
            {
            	//占2字节
                return new byte[] { bytes[index], bytes[index + 1] };
            }
            else
            {
            	//占1字节
                return new byte[] { bytes[index] };
            }
        }
        /// <summary>
        /// 获取长度
        /// </summary>
        /// <param name="bytes">长度</param>
        /// <param name="index">索引位置</param>
        /// <returns></returns>
        private static byte[] GetLength(byte[] bytes, int index)
        {
            if (bytes.Length < index + 1)
            { throw new ArgumentException("无效的索引值"); }
            //判断Length部分所占字节 是1个字节还是多个字节
            if ((bytes[index] & 0x80) == 0x80)
            {
            	//占多个字节
                int lengthSize = (bytes[index] & 0x7f) + 1;//获取Length所占字节数
                return bytes.Take(index + lengthSize).Skip(index).ToArray();
            }
            else
            {    
            	//占单个字节
                return new byte[] { bytes[index] };
            }
        }
        /// <summary>
        /// 根据Length部分的值获取到value部分的值
        /// </summary>
        /// <param name="bytes">Length部分的值</param>
        /// <returns></returns>
        private static int GetValueLengthByLengthByteValue(byte[] bytes)
        {
            int length = 0;
            if (bytes.Length == 1)
            {
            	length = bytes[0];
            }
            else
            {
                //从下一个字节开始算Length域
                for (int index = 1; index < bytes.Length; index++)
                {
                    length += bytes[index] << ((index - 1) * 8); //计算Length域的长度
                }
            }
            return length;
        }
        #endregion

        #region TLV实体 解析
        /// <summary>
        /// 解析TLV
        /// </summary>
        /// <param name="list">
        /// <returns></returns>
        public static byte[] Parse(List<TLVEntity> list)
        {
            byte[] buffer = new byte[4096];
            int currentIndex = 0;
            int currentTLVIndex = 0;
            int valueSize = 0;
            while (currentTLVIndex < list.Count())
            {
                valueSize = 0;
                TLVEntity entity = list[currentTLVIndex];
                Array.Copy(entity.Tag, 0, buffer, currentIndex, entity.TagSize);    //解析Tag
                currentIndex += entity.TagSize;
                for (int index = 0; index < entity.LengthSize; index++)
                {
                    valueSize += entity.Length[index] << (index * 8); //计算Length域的长度
                }
                if (valueSize > 127)
                {
                    buffer[currentIndex] = Convert.ToByte(0x80 | entity.LengthSize);
                    currentIndex += 1;
                }
                Array.Copy(entity.Length, 0, buffer, currentIndex, entity.LengthSize);  //解析Length
                currentIndex += entity.LengthSize;
                //判断是否包含子嵌套TLV
                if (entity.SubTLVEntity == null)
                {
                    Array.Copy(entity.Value, 0, buffer, currentIndex, valueSize);   //解析Value
                    currentIndex += valueSize;
                }
                else
                {
                    byte[] tempBuffer = Parse(entity.SubTLVEntity);
                    Array.Copy(tempBuffer, 0, buffer, currentIndex, tempBuffer.Length); //解析子嵌套TLV
                    currentIndex += tempBuffer.Length;
                }
                currentTLVIndex++;
            }
            byte[] resultBuffer = new byte[currentIndex];
            Array.Copy(buffer, 0, resultBuffer, 0, currentIndex);
            return resultBuffer;
        }
        #endregion
    }

4.辅助方法

由于使用上述解析方法解析出来的数据为byte[]格式,所以在实际应用当中可能需要转换为字符串

    public static class MethodExtension
    {
        /// <summary>
        /// 将字节数组转换为十六进制字符字符串。
        /// </summary>
        /// <param name="bytes">要转为字符串的字节。</param>
        /// <returns>作为十六进制字符字符串的字节数组。</returns>
        public static string ToHexString(this byte[] bytes)
        {
            var c = new char[bytes.Length * 2];
            for (var i = 0; i < bytes.Length; ++i)
            {
                var b = ((byte)(bytes[i] >> 4));
                c[i * 2] = (char)(b > 9 ? b + 0x37 : b + 0x30);
                b = ((byte)(bytes[i] & 0xF));
                c[i * 2 + 1] = (char)(b > 9 ? b + 0x37 : b + 0x30);
            }
            return new string(c);
        }
        /// <summary>
        /// 将十六进制字符字符串转换为字节数组。
        /// </summary>
        /// <param name="hexString">要转换为字节数组的十六进制字符串。</param>
        /// <returns>十六进制字符串的字节数组。</returns>
        public static byte[] HexStringToByteArray(this string hexString)
        {
            var hexStringLength = hexString.Length;
            if (hexStringLength % 2 == 1) throw new ArgumentException("hexString无效。必须包含偶数个字符,其范围为[0123456789ABCDEF]。");
            var b = new byte[hexStringLength / 2];
            for (var i = 0; i < hexStringLength; i += 2)
            {
                var topChar = (hexString[i] > 0x40 ? hexString[i] - 0x37 : hexString[i] - 0x30) << 4;
                var bottomChar = hexString[i + 1] > 0x40 ? hexString[i + 1] - 0x37 : hexString[i + 1] - 0x30;
                b[i / 2] = Convert.ToByte(topChar + bottomChar);
            }
            return b;
        }
    }

5.应用

 	static void Main(string[] args)
	{
		var ValueHex = "01080000000100000000";
  	    var tlvList = PackageTLV.ToTLVEntityList(ValueHex);
		var tlvValueList = new List<object>();
		foreach (var item in tlvList)
		{
			tlvValueList.Add(new
			{
				tag = item?.Tag?.ToHexString(),
				type = item?.Tag?.ToHexString(),
				len = item?.Length?.ToHexString(),
				value = item?.Value?.ToHexString()
			});
		}
		var Value = JsonConvert.SerializeObject(tlvValueList);
		Console.WriteLine(Value);
		Console.ReadLine();
	}

在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值