C++ TLV格式解析

一、什么是TLV格式?

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

1)Tag域

2)Length域

   当b8为0时,该字节的b7-b1作为value域的长度;当b8为1时,b7-b1作为后续字节的长度。例:10000011,代表后续还有3个字节作为value域的长度(本字节不算,本字节变为作为一个Length的索引)。3个字节代表value的长度,意味着什么呢,意味着内容的长度当需要很大的时候,字节的位数就会跟着越高,3个字节就代表最大可以有256*256*256的长度。

3)Value域

   分成两种情况考虑,就是前面说到的Tag分成两个数据元结构,一种是简单数据元结构,一种是复合数据元架构:

   先来看看简单数据元结构:

   复合数据元结构:

 

  后面的Value说明:Primitive or constructed BER-TLV data object number,包含一个简单数据元结构或者也可以是一个符合数据元结构。这样可以看出,算法中必须要实现Tag的嵌套功能,递归算法不可少。

4)TLV格式数据实例

数据: 5F2D027A68

Tag域

5F → 01011111 → 5F 2D → 00101101 → 5F2D

Length域

02 → 00000010 → 02(2字节)

Value域

7A68

5)算法实现( C++)

       根据以上的说明现在来实现它的打包解包的算法(打包的目的是将一个从终端上发的请求数据——字节数组,构造成一系列的TLV结构实体;解包的目的刚好相反,就是将TLV结构实体解析成字节数组,然后通过IC卡发送到终端上)。

      首先定义一个TLV结构实体:

// TLV结构体

struct TLVEntity {

    unsigned char* Tag;         //标记

    unsigned char* Length;      //数据长度

    unsigned char* Value;       //数据

    unsigned int TagSize;       //标记占用字节数

    unsigned int LengthSize;    //数据长度占用字节数

    TLVEntity* Sub_TLVEntity;   //子嵌套TLV实体

};

其中TagSize代表Tag字段的字节长度,LengthSize代表Length的字节长度,这里的Length记住要使用char*,由于前面说过,Length可能包含

多个字节,通过多个字节确定Value域的长度,Sub_TLVEntity作为子嵌套的TLV结构体。

    定义一个TLVPackage的打包类:

TLVPackage.h:

// TLV打包类

class TLVPackage

{

public:

    TLVPackage();

    virtual ~TLVPackage();

    //构造TLV实体

    static void Construct(unsigned char* buffer, unsigned int bufferLength, TLVEntity* tlvEntity, unsigned int& entityLength, unsigned int status=0);

    //解析TLV字节数组

    static void Parse(TLVEntity* tlvEntity, unsigned int entityLength, unsigned char* buffer, unsigned int& bufferLength);

};

具体实现 方法 :

// 构造TLV

void TLVPackage:: Construct(

    unsigned char* buffer,

    unsigned int bufferLength,

    TLVEntity* tlvEntity,

    unsigned int& entityLength,

    unsigned int status

    )

{

    int currentTLVIndex = 0;

    int currentIndex = 0;

    int currentStatus = 'T'; //状态字符

    unsigned long valueSize = 0;

 
    while(currentIndex < bufferLength)

    {

        switch(currentStatus)

        {

        case 'T':

            valueSize = 0;

            //判断是否单一结构

            if((status == 1 && buffer[currentIndex] & 0x20) != 0x20)

            {

                tlvEntity[currentTLVIndex].Sub_TLVEntity = NULL; //单一结构时将子Tag置空

                //判断是否多字节Tag

                if((buffer[currentIndex] & 0x1f) == 0x1f)

                {

                    int endTagIndex = currentIndex;

                    while((buffer[++endTagIndex] & 0x80) == 0x80); //判断第二个字节的最高位是否为1
                    int tagSize = endTagIndex - currentIndex + 1; //计算Tag包含多少字节

                    tlvEntity[currentTLVIndex].Tag = new unsigned char[tagSize];

                    memcpy(tlvEntity[currentTLVIndex].Tag, buffer + currentIndex, tagSize);
                    tlvEntity[currentTLVIndex].Tag[tagSize] = 0;

                    tlvEntity[currentTLVIndex].TagSize = tagSize;

                    currentIndex += tagSize;

                }

                else

                {

                    tlvEntity[currentTLVIndex].Tag = new unsigned char[1];

                    memcpy(tlvEntity[currentTLVIndex].Tag, buffer + currentIndex, 1);

                    tlvEntity[currentTLVIndex].Tag[1] = 0;

                    tlvEntity[currentTLVIndex].TagSize = 1;

                    currentIndex += 1;

                }

            }

            else

            {

                //判断是否多字节Tag

                if((buffer[currentIndex] & 0x1f) == 0x1f)

                {

                    int endTagIndex = currentIndex;

                    while((buffer[++endTagIndex] & 0x80) == 0x80); //判断第二个字节的最高位是否为1
                    int tagSize = endTagIndex - currentIndex + 1; //计算Tag包含多少字节

                    tlvEntity[currentTLVIndex].Tag = new unsigned char[tagSize];

                    memcpy(tlvEntity[currentTLVIndex].Tag, buffer + currentIndex, tagSize);
                    tlvEntity[currentTLVIndex].Tag[tagSize] = 0;

                    tlvEntity[currentTLVIndex].TagSize = tagSize;

                    currentIndex += tagSize;

                }

                else

                {

                    tlvEntity[currentTLVIndex].Tag = new unsigned char[1];

                    memcpy(tlvEntity[currentTLVIndex].Tag, buffer + currentIndex, 1);

                    tlvEntity[currentTLVIndex].Tag[1] = 0;

                    tlvEntity[currentTLVIndex].TagSize = 1;

                    currentIndex += 1;             

                }

                //分析SubTag

                int subLength = 0; 

                unsigned char* temp;

                if((buffer[currentIndex] & 0x80) == 0x80)

                {

                    for (int index = 0; index < 2; index++)

                    {
                        subLength += buffer[currentIndex + 1 + index] << (index * 8); //计算Length域的长度

                    }

                    temp = new unsigned char[subLength];

                    memcpy(temp, buffer + currentIndex + 3, subLength);

                }

                else

                {

                    subLength = buffer[currentIndex];

                    temp = new unsigned char[subLength];

                    memcpy(temp, buffer + currentIndex + 1, subLength);

                }

                temp[subLength] = 0;

                //memcpy(temp, buffer + currentIndex + 1, subLength);

                unsigned int oLength;

                tlvEntity[currentTLVIndex].Sub_TLVEntity = new TLVEntity[1];

                Construct(temp, subLength, tlvEntity[currentTLVIndex].Sub_TLVEntity, oLength);

            }

 
            currentStatus = 'L';

            break;

        case 'L':      

            //判断长度字节的最高位是否为1,如果为1,则该字节为长度扩展字节,由下一个字节开始决定长度

            if((buffer[currentIndex] & 0x80) != 0x80)

            {

                tlvEntity[currentTLVIndex].Length = new unsigned char[1];

                memcpy(tlvEntity[currentTLVIndex].Length, buffer + currentIndex, 1);

                tlvEntity[currentTLVIndex].Length[1] = 0;

                tlvEntity[currentTLVIndex].LengthSize = 1;

                valueSize = tlvEntity[currentTLVIndex].Length[0];

                currentIndex += 1;

            }

            else

            {

                //为1的情况

                unsigned int lengthSize = buffer[currentIndex] & 0x7f;

                currentIndex += 1; //从下一个字节开始算Length域

                for (int index = 0; index < lengthSize; index++)

                {

                    valueSize += buffer[currentIndex + index] << (index * 8); //计算Length域的长度

                }

 
                tlvEntity[currentTLVIndex].Length = new unsigned char[lengthSize];

                memcpy(tlvEntity[currentTLVIndex].Length, buffer + currentIndex, lengthSize);
                tlvEntity[currentTLVIndex].Length[lengthSize] = 0;

 
                tlvEntity[currentTLVIndex].LengthSize = lengthSize;

                currentIndex += lengthSize;

            }

            currentStatus = 'V';

            break;

        case 'V':

            tlvEntity[currentTLVIndex].Value = new unsigned char[valueSize];

            memcpy(tlvEntity[currentTLVIndex].Value, buffer + currentIndex, valueSize);

            tlvEntity[currentTLVIndex].Value[valueSize] = 0;

            currentIndex += valueSize;

            //进入下一个TLV构造循环

            currentTLVIndex += 1;

            currentStatus = 'T';

            break;

        default:

            return;

        }

    }

    entityLength = currentTLVIndex;

}

 

// 解析TLV

void TLVPackage::Parse(

    TLVEntity* tlvEntity,

    unsigned int entityLength,

    unsigned char* buffer,

    unsigned int& bufferLength

    )

{

    int currentIndex = 0;

    int currentTLVIndex = 0;

    unsigned long valueSize = 0;

    while(currentTLVIndex < entityLength)

    {

        valueSize = 0;

        TLVEntity entity = tlvEntity[currentTLVIndex];

        memcpy(buffer + currentIndex, entity.Tag, 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] = 0x80 | entity.LengthSize;

            currentIndex += 1;

        }

        memcpy(buffer + currentIndex, entity.Length, entity.LengthSize);    //解析Length

        currentIndex += entity.LengthSize;

        //判断是否包含子嵌套TLV

        if(entity.Sub_TLVEntity == NULL)

        {

            memcpy(buffer + currentIndex, entity.Value, valueSize); //解析Value

            currentIndex += valueSize;

        }

        else

        {

            unsigned int oLength;

            Parse(entity.Sub_TLVEntity, 1, buffer + currentIndex, oLength); //解析子嵌套TLV

            currentIndex += oLength;

        }

        currentTLVIndex++;

    }

    buffer[currentIndex] = 0;

    bufferLength = currentIndex;

}

写测试程序:

// 上发测试数据

unsigned char requestBuf[] = {

        0x9F, 0x1C, 0x12, 0x33, 0x33, 0x30, 0x32, 0x32, 0x37, 0x31, 0x39, 0x36, 0x32, 0x30, 0x34, 0x30, 0x34,

        0x32, 0x37, 0x31, 0x38, 0x9F, 0x62, 0x01, 0x01, 0x57, 0x12, 0x62, 0x22, 0x89, 0x00, 0x00, 0x02, 0x91,

        0x01, 0xD0, 0x90, 0x32, 0x01, 0x02, 0x47, 0x17, 0x13, 0x00, 0x0F, 0x5F, 0x20, 0x0A, 0x48, 0x55, 0x47,

        0x55, 0x4F, 0x20, 0x4D, 0x49, 0x4E, 0x47, 0x9F, 0x1F, 0x3C, 0x25, 0x39, 0x39, 0x36, 0x32, 0x32, 0x32,

        0x38, 0x39, 0x30, 0x30, 0x30, 0x30, 0x30, 0x32, 0x39, 0x31, 0x30, 0x31, 0x5E, 0x47, 0x55, 0x4F, 0x20,

        0x4D, 0x49, 0x4E, 0x47, 0x2F, 0x48, 0x55, 0x5E, 0x30, 0x39, 0x30, 0x33, 0x32, 0x30, 0x31, 0x30, 0x32,

        0x34, 0x37, 0x31, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0x32, 0x38, 0x39, 0x30,

        0x30, 0x3F

};

    TLVEntity tlvEntity[TLV_MAX_LENGTH];

    unsigned int tlv_count;

    //构造TLV

    TLVPackage::Construct(requestBuf, sizeof(requestBuf), tlvEntity, tlv_count);

    unsigned char parseBuf[1024];

    unsigned int buf_count;

    //解析TLV

    TLVPackage::Parse(tlvEntity, tlv_count, parseBuf, buf_count);

    if(strncmp((char*)parseBuf, (char*)requestBuf, sizeof(requestBuf)) == 0)

    {
        AfxMessageBox("TRUE");

    }

    else

    {
        AfxMessageBox("FALSE");

    }

最后测试结果中,可以得到最后将弹出“TRUE”的对话框。证明构造TLV得到的TLVEntity,再对这个实体进行解析,可以的到解析后的字节

数组, 最后通过strncmp的方法比较判断,是否原始字节数组和解析后的字节数组是否一致。

猜你喜欢

转载自blog.csdn.net/my_angle2016/article/details/114284225