C语言实现TLV消息组成

3 篇文章 2 订阅
1 篇文章 0 订阅

一、前言

在网络数据传输过程中,TLV格式的消息比较常见。那么什么是TLV格式?
一个完整的TLV消息是由 消息头部+消息体(1)+消息体(2)+消息体(n) 组成。
如下图所示:
一个完整的TLV消息

  1. TLV消息头
    根据上图所示,TLV的消息头是由28个字节组成,且长度固定不变。
    length:消息的总长度,即TLV消息头长度 + TLV消息体长度。(4字节)
    version:消息的版本号,目前填2,且在使用中一般是固定不变的。(2字节)
    commandId:消息的命令类型,接受消息或者发送消息,根据需要填写。(2字节)
    seqence:消息序列号,范围在0x01 ~ 0x7FFFFFFF之间。(4字节)
    checkNumber:消息校验值,一般保留不使用。(16字节)
  2. TLV消息体
    由上图得知,TLV消息体是由一个或者多个TLV消息单体组成,单个消息体长度是不定的,因为有的value是整数型,有的value是字符串等其它类型。
    tag:消息tag/消息id,一般接受TLV消息方可以通过此tag。有的地方将tag解释为type,其实无论怎么理解,tag所占长度是不变的。(2字节)
    length:单个消息体value的长度,整型一般为4位,字符串需要用strlen()极端。(2字节)
    value:消息值,字节数有数据类型决定。

二、TLV应用

下面我们来模拟用TLV格式进行网络数据传输,采用TCP短链发送消息。TCP服务端用网络调试助手来接受消息。Demo示例:Gitee
需要发送的数据如下:
需要发送的数据

  1. 实现tcp发送功能
    tcp_socket.h代码如下:

    //
    // Created by jerry on 2021/4/5.
    //
    
    #ifndef C_TLV_DEMO_TCP_SOCKET_H
    #define C_TLV_DEMO_TCP_SOCKET_H
    
    #define TCP_SERVER_IP   "192.168.2.142"
    #define TCP_SERVER_PORT 8080
    
    /* 需要发送的tcp消息 */
    struct TCP_MSG
    {
        char msgLength; // 消息长度
        char *msg;      // 消息体
    };
    
    /* 通过tcp短链接发送消息 */
    int send_message(struct TCP_MSG *tcpMsg);
    
    #endif //C_TLV_DEMO_TCP_SOCKET_H
    
    

    tcp_socket.c代码如下:

    //
    // Created by jerry on 2021/4/5.
    //
    #include <stdio.h>
    #include <string.h>
    
    #include <sys/socket.h>
    #include <arpa/inet.h>
    #include <unistd.h>
    
    #include "tcp_socket.h"
    
    #define DEBUG 1
    #define LOGD(fmt, ...) {if (DEBUG == 1 ) printf("[D][%s:%d] "fmt"\n", __FUNCTION__, __LINE__, ##__VA_ARGS__);}
    
    /* 关闭socket套接字 */
    void close_tcp_socket(int fd)
    {
        close(fd);
    }
    
    /* 建立tcp链接 */
    int open_tcp_socket_connect()
    {
        int fd = socket(AF_INET, SOCK_STREAM, 0);
    
        if (fd <= 0)
        {
            LOGD("open socket failed!");
            return -1;
        }
    
        const char *server_ip      = TCP_SERVER_IP;
        unsigned short server_port = TCP_SERVER_PORT;
    
        struct sockaddr_in serverAddr;
        bzero(&serverAddr, sizeof(serverAddr));
    
        serverAddr.sin_family = AF_INET;
        serverAddr.sin_port   = htons(server_port);
        inet_pton(AF_INET, server_ip, &serverAddr.sin_addr);
    
        if (0 != connect(fd, (struct sockaddr *)&serverAddr, sizeof(serverAddr)))
        {
            LOGD("tcp connect failed!");
            close_tcp_socket(fd);
            return -1;
        }
    
        return fd;
    }
    
    /* 发送消息并关闭tcp链接 */
    int send_message(struct TCP_MSG *tcpMsg)
    {
        if (!tcpMsg || tcpMsg->msgLength == 0 || !tcpMsg->msg)
        {
            LOGD("TCP_MSG or msgLength or msg is NULL, return.");
            return -1;
        }
    
        int fd = open_tcp_socket_connect();
        if (0 > fd)
        {
            return  -1;
        }
    
        int len = send(fd, tcpMsg->msg, tcpMsg->msgLength, 0);
        if (0 >= len)
        {
            LOGD("send tcp message failed!");
        }
    
        close_tcp_socket(fd);
    
        return 0;
    }
    
    
    1. 实现构建TLV消息代码
      build_tlv.h代码如下:
    #ifndef __BUILD_TLV_H__
    #define __BUILD_TLV_H__
    
    static int TLV_MSG_SEQENCE = 1; // TLV 消息序列号,从1开始递增,取值范围为0x00000000~0x7FFFFFFF
    
    #define TLV_MSG_SEQENCE_MIN = 0x01;        // TLV 消息序列号最小值
    #define TLV_MSG_SEQENCE_MAX = 0x7FFFFFFF;  // TLV 消息序列号最大值
    
    
    
    /**
     *  TLV 消息命令类型,即此消息是要发送的消息还是接受的消息
     **/
    enum TLV_COMMAND_ID
    {
        SEND_MSG = 0x0002,  // 发送消息
        RECV_MSG = 0x0003,  // 接受消息
    };
    
    /**
     *  TLV 消息的类型,每个 TLV_BODY 都有一个类型
     **/
    typedef enum TLV_BODY_TYPE
    {
        TLV_BODY_TYPE_STRING = 0x00,  // 字符串型
        TLV_BODY_TYPE_INT    = 0x01,  // 整数型
    } TLV_BODY_TYPE;
    
    /**
     *  TLV 消息头部,长度共28位
     **/
    struct TLV_HEAD
    {
        int   tlvHeadLength;           // TLV 消息总长度,即TLV_HEAD + TLV_BODY
        short tlvHeadVersion;          // TLV 消息版本号
        short tlvHeadCommandId;        // TLV 消息命令类型
        int   tlvHeadSeqence;          // TLV 消息序列号
        char  tlvHeadCheckNumber[16];  // TLV 消息校验
    };
    
    /**
     *  TLV 消息体
     *
     *  实际上tlvBodyValueLength和tlvBodyValue在代码中并未引用
     *  而是通过build_tlv_body()将这部分数值组建到buffer里面
     *  这样做的目的是:
     *      1.为了减少代码含量,将重复部分封装到build_tlv_body()里面,
     *          用户只关心tlvBodyType、tlvBodyTag和需要组建的value值即可。
     *          里面的一些大小端转化用户也无需关注。根据需要传入实际值即可。
     *      2.在头文件中便于查看TLV_BODY的结构
     **/
    struct TLV_BODY
    {
        int   tlvBodyType;             // TLV 消息值的类型(TLV_BODY_TYPE)
    
        short tlvBodyTag;              // TLV 消息tag(消息编号id),根据此tag解析value
        short tlvBodyValueLength;      // TLV 消息值的长度
        char  tlvBodyValue[0];         // TLV 消息值
    };
    
    
    /**
     *  TLV 消息
     **/
    struct TLV_MSG
    {
        struct TLV_HEAD tlvHead;
        struct TLV_BODY tlvBody;
    };
    
    /* 组建TLV消息(消息头 + 消息体) */
    void *build_tlv_msg(struct TLV_MSG *tlvMsg, char *buffer, void *value);
    
    #endif
    

    build_tlv.c代码如下:

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <arpa/inet.h>
    
    #include "build_tlv.h"
    
    #define DEBUG 1
    #define LOGD(fmt, ...) {if (DEBUG == 1 ) printf("[D][%s:%d] "fmt"\n", __FUNCTION__, __LINE__, ##__VA_ARGS__);}
    
    
    /**********************************************************************************
     *  Function:     void *buid_tlv_head(struct TLV_HEAD *head, char *buffer)
     *  Description:  按网络字节序组建TLV消息的头部 (网络中发送数据时采用此方法)
     *  Input:        struct TLV_MSG *tlvMsg    TLV 消息结构体
     *                char *buffer              TLV 消息存储buffer
     *  Return:       NULL
     **********************************************************************************/
    static void *build_tlv_head(struct TLV_MSG *tlvMsg, char *buffer)
    {
        if (!tlvMsg || !buffer)
        {
            LOGD("Head or buffer is null, return.")
            return NULL;
        }
    
        /* TLV 消息头部处理 (进行网络字节序转换) */
        tlvMsg->tlvHead.tlvHeadLength    = htonl(0);        // (int)   TLV 消息的总长度,包括该字段本身。由于第一次构建消息时长度未知,因此先置0,等到消息体构建完毕时再填充
        tlvMsg->tlvHead.tlvHeadVersion   = htons(2);        // (short) TLV 消息的版本号,目前填2
        tlvMsg->tlvHead.tlvHeadCommandId = htons(SEND_MSG); // (short) TLV 消息命令类型
        tlvMsg->tlvHead.tlvHeadSeqence   = htonl(TLV_MSG_SEQENCE);   // (int)   TLV 消息序列号
        memset(tlvMsg->tlvHead.tlvHeadCheckNumber, 0x00, sizeof(tlvMsg->tlvHead.tlvHeadCheckNumber)); // (char) 校验值,保留不使用
    
        /* 组装TLV 消息头 */
        memcpy(buffer + tlvMsg->tlvHead.tlvHeadLength,
               &tlvMsg->tlvHead.tlvHeadLength, sizeof(tlvMsg->tlvHead.tlvHeadLength));           // 0~3位存储 tlvHeadLength (注意tlvHeadLength初始值为0)
        tlvMsg->tlvHead.tlvHeadLength += sizeof(tlvMsg->tlvHead.tlvHeadLength);                  // tlvHeadLength(4) = 0 + 4
    
        memcpy(buffer + tlvMsg->tlvHead.tlvHeadLength,
               &tlvMsg->tlvHead.tlvHeadVersion, sizeof(tlvMsg->tlvHead.tlvHeadVersion));         // 4~5位存储 tlvHeadVersion
        tlvMsg->tlvHead.tlvHeadLength += sizeof(tlvMsg->tlvHead.tlvHeadVersion);                 // tlvHeadLength(6) = 4 + 2
    
        memcpy(buffer + tlvMsg->tlvHead.tlvHeadLength,
               &tlvMsg->tlvHead.tlvHeadCommandId, sizeof(tlvMsg->tlvHead.tlvHeadCommandId));     // 6~7位存储 tlvHeadCommandId
        tlvMsg->tlvHead.tlvHeadLength += sizeof(tlvMsg->tlvHead.tlvHeadCommandId);               // tlvHeadLength(8) = 6 + 2
    
        memcpy(buffer + tlvMsg->tlvHead.tlvHeadLength,
               &tlvMsg->tlvHead.tlvHeadSeqence, sizeof(tlvMsg->tlvHead.tlvHeadSeqence));        // 8~11位存储 tlvHeadSeqence
        tlvMsg->tlvHead.tlvHeadLength += sizeof(tlvMsg->tlvHead.tlvHeadSeqence);                // tlvHeadLength(12) = 8 + 4
    
        memcpy(buffer + tlvMsg->tlvHead.tlvHeadLength,
               tlvMsg->tlvHead.tlvHeadCheckNumber, sizeof(tlvMsg->tlvHead.tlvHeadCheckNumber)); // 12~27位存储 tlvHeadCheckNumber
        tlvMsg->tlvHead.tlvHeadLength += sizeof(tlvMsg->tlvHead.tlvHeadCheckNumber);            // tlvHeadLength(28) = 12 + 16
    
        /* TLV 消息序列号处理,若tlvHeadSeqence大于0x7FFFFFFF则初始化置1 */
        if (0x7FFFFFFF > TLV_MSG_SEQENCE)
        {
            TLV_MSG_SEQENCE ++;
        } else {
            TLV_MSG_SEQENCE = 1;
        }
    
        return NULL;
    }
    
    /**********************************************************************************
     *  Function:     void *build_tlv_msg(struct TLV_MSG *tlvMsg,
     *                                           char *buffer, void *value)
     *  Description:  按网络字节序组建TLV消息体
     *  Input:        struct TLV_MSG *tlvMsg    TLV 消息结构体
     *                char *buffer              TLV 消息存储buffer
     *                void *value               需要组建的值
     *  Return:       NULL
     **********************************************************************************/
    void *build_tlv_msg(struct TLV_MSG *tlvMsg, char *buffer, void *value)
    {
        if (!tlvMsg || !buffer || !value)
        {
            LOGD("Head or buffer or value is null, return.")
            return NULL;
        }
    
        /* 若TLV_HEAD还没有构建,则先构建TLV_HEAD */
        if (tlvMsg->tlvHead.tlvHeadLength == 0)
        {
            LOGD("tlvHead is NULL, build!");
            build_tlv_head(tlvMsg, buffer);
        }
    
        /* 根据tlvBodyType对数据进行处理 */
        switch (tlvMsg->tlvBody.tlvBodyType) {
            /* String 型数据处理 */
            case TLV_BODY_TYPE_STRING:
                {
                    /* tag */
                    short tag    = tlvMsg->tlvBody.tlvBodyTag;
                    short tagNbo = htons(tag);
    
                    /* value */
                    char *val = (char *)value; // 字符串类型的数据在网络中传输不用转换大小端
    
                    /* value length */
                    short valLength    = strlen(val);
                    short valLengthNbo = htons(valLength);
                    LOGD("(## String ##) Tag: %d, Length: %d, Value: %s", tag, valLength, val);
    
    
                    memcpy(buffer + tlvMsg->tlvHead.tlvHeadLength,
                           &tagNbo, sizeof(tagNbo));                        // 28~29位存储 tlvBodyTag (这个时候TLV_HEAD已经构建完成)
                    tlvMsg->tlvHead.tlvHeadLength += sizeof(tagNbo);        // tlvHeadLength(30) = 28 + 2
    
                    memcpy(buffer + tlvMsg->tlvHead.tlvHeadLength,
                           &valLengthNbo, sizeof(valLengthNbo));            // 30~31位存储 tlvBodyValueLength
                    tlvMsg->tlvHead.tlvHeadLength += sizeof(valLengthNbo);  // tlvHeadLength(32) = 30 + 2
    
                    memcpy(buffer + tlvMsg->tlvHead.tlvHeadLength,
                           val, strlen(val));                              // 32~n位存储 tlvBodyValue
                    tlvMsg->tlvHead.tlvHeadLength += strlen(val);          // tlvHeadLength(n) = 32 + sizeof(valLengthNbo)
    
                }
                break;
    
            /* int 型数据处理 */
            case TLV_BODY_TYPE_INT:
                {
                    /* tag */
                    short tag    = tlvMsg->tlvBody.tlvBodyTag;
                    short tagNbo = htons(tag);
    
                    /* value */
                    int val    = *(int *)value;
                    int valNbo = htonl(val);
    
                    /* value length */
                    short valLength    = sizeof(val);
                    short valLengthNbo = htons(valLength);
    
                    LOGD("(##   int  ##) Tag: %d, Length: %d, Value: %d", tag, valLength, val);
    
                    memcpy(buffer + tlvMsg->tlvHead.tlvHeadLength,
                           &tagNbo, sizeof(tagNbo));                        // 28~29位存储 tlvBodyTag (这个时候TLV_HEAD已经构建完成)
                    tlvMsg->tlvHead.tlvHeadLength += sizeof(tagNbo);        // tlvHeadLength(30) = 28 + 2
                    memcpy(buffer + tlvMsg->tlvHead.tlvHeadLength,
                           &valLengthNbo, sizeof(valLengthNbo));            // 30~31位存储 tlvBodyValueLength
                    tlvMsg->tlvHead.tlvHeadLength += sizeof(valLengthNbo);  // tlvHeadLength(32) = 30 + 2
                    memcpy(buffer + tlvMsg->tlvHead.tlvHeadLength,
                           &valNbo, sizeof(valNbo));                        // 32~35位存储 tlvBodyValue
                    tlvMsg->tlvHead.tlvHeadLength += sizeof(valNbo);        // tlvHeadLength(36) = 32 + 4
                }
                break;
            default:
                LOGD("Other tlv body type.")
                break;
        }
    
        /* 给TLV_HEAD.tlvHeadLength 赋值 */
        int tlvHeadLength = tlvMsg->tlvHead.tlvHeadLength;
        int tlvHeadLengthNbo = htonl(tlvHeadLength);
        memcpy(buffer, &tlvHeadLengthNbo, sizeof(tlvHeadLengthNbo)); // TLV_HEAD.tlvHeadLength占头部前四位,因此buffer的指针不用移动,直接赋值即可
    
        return NULL;
    }
    
    1. 实现main.c示例使用代码
      代码如下:
    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    
    #include "build_tlv.h"
    #include "tcp_socket.h"
    
    #define DEBUG 1
    #define LOGD(fmt, ...) {if (DEBUG == 1 ) printf("[D][%s:%d] "fmt"\n", __FUNCTION__, __LINE__, ##__VA_ARGS__);}
    
    static const int count      = 100;         // tag: 1022
    static const char hello[10] = "Hello TLV"; // tag: 1023
    static const char *into     = "test";      // tag: 1024
    
    static void tlv_test()
    {
        /* TLV_MSG 结构体 */
        struct TLV_MSG tlvMsg;
        memset(&tlvMsg, 0x00, sizeof(tlvMsg));
    
        /* 最终组建的tlv消息buffer */
        char buffer[4096] = {0};
        memset(buffer, 0x00, sizeof buffer);
    
        /* 添加TLV的BODY,这儿只传入tlvBodyType、 tlvBodyTag、和BODY具体的内容value即可 */
        tlvMsg.tlvBody.tlvBodyType = TLV_BODY_TYPE_INT;
        tlvMsg.tlvBody.tlvBodyTag  = 1022;
        build_tlv_msg(&tlvMsg, buffer, (void *)(&count));
    
        /* 添加TLV的BODY,这儿只传入tlvBodyType、 tlvBodyTag、和BODY具体的内容value即可 */
        tlvMsg.tlvBody.tlvBodyType = TLV_BODY_TYPE_STRING;
        tlvMsg.tlvBody.tlvBodyTag  = 1023;
        build_tlv_msg(&tlvMsg, buffer, (void *)hello);
    
        /* 添加TLV的BODY,这儿只传入tlvBodyType、 tlvBodyTag、和BODY具体的内容value即可 */
        tlvMsg.tlvBody.tlvBodyType = TLV_BODY_TYPE_STRING;
        tlvMsg.tlvBody.tlvBodyTag  = 1024;
        build_tlv_msg(&tlvMsg, buffer, (void *)into);
    
    
        /* 通过TCP短链接发送消息 */
        struct TCP_MSG *tcpMsg = NULL;
        tcpMsg = (struct TCP_MSG *)calloc(1, sizeof(struct TCP_MSG));
        tcpMsg->msgLength = tlvMsg.tlvHead.tlvHeadLength;
    
        tcpMsg->msg = (char *)calloc(1, sizeof(char) * tcpMsg->msgLength);
        memcpy(tcpMsg->msg, buffer, tcpMsg->msgLength);
    
        send_message(tcpMsg);
    
        free(tcpMsg->msg);
        tcpMsg->msg = NULL;
        free(tcpMsg);
        tcpMsg = NULL;
    }
    
    int main() {
    
        tlv_test();
    
        return 0;
    }
    
    

三、运行结果

  1. 运行结果
    如下图所示,网络调试助手接收到发过来的TCP消息:
    网络调试助手接收的数据

  2. 数据分析
    我们对网络调试助手接收到的16进制消息转字符串进行分析:

    ### 消息头部 ###
    00 00 00 39   # 消息长度 57字节
    00 02         # 消息版本号 02
    00 02         # 消息类型(发送) 02
    00 00 00 01   # 消息序列号 
    00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  # 消息校验,保留
    
    ### 消息体 (1) ###
    03 FE        # tag    1022
    00 04        # length 4
    00 00 00 64  # value  100
    
    ### 消息体 (2) ###
    03 FF         # tag    1023
    00 09         # length 9
    48 65 6C 6C 6F 20 54 4C 56 # value Hello TLV
    
    ### 消息体 (3) ###
    04 00         # tag    1024
    00 04         # length 4
    74 65 73 74   # value  test
    

    由此可见,接收和发送的消息是一致的,且按照TLV头部+TLV消息体(1)+TLV消息体(2)+TLV消息体(3)格式。

四、注意事项

  1. TLV在网络传输中需要进行网络大小端转换,即对应的数据要进行htonl()/htons()转换。上面给出的buid_tlv_msg()函数内部已经做过处理,用户无需关心。
  2. build_tlv.hstruc TLV_BODY{}结构体tlvBodyValueLengthtlvBodyValue并未在代码中引用。这样做的目的在上面的代码示例中有详细解释。
  3. 用户使用此份Demo只需要按照main.ctlv_test()函数的组建TLV方法使用即可。
  • 5
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
TLV协议是一种常用的数据编码格式,它由Tag(标签)、Length(长度)和Value(值)三部分组成。在C语言中,可以使用结构体来实现TLV协议。 首先,定义一个TLV结构体,包含Tag、Length和Value三个成员: ```c typedef struct tlv { uint8_t tag; uint16_t length; uint8_t *value; } TLV; ``` 其中,Tag用一个字节表示,Length用两个字节表示,Value是一个指向字节数组的指针。 接下来,可以定义一些函数来进行TLV编码和解码: ```c // 编码一个TLV结构体 void encode_tlv(TLV *tlv, uint8_t *buf, int *len) { buf[0] = tlv->tag; buf[1] = (tlv->length >> 8) & 0xFF; buf[2] = tlv->length & 0xFF; memcpy(buf + 3, tlv->value, tlv->length); *len = 3 + tlv->length; } // 解码一个TLV结构体 void decode_tlv(TLV *tlv, uint8_t *buf, int len) { tlv->tag = buf[0]; tlv->length = (buf[1] << 8) | buf[2]; tlv->value = malloc(tlv->length); memcpy(tlv->value, buf + 3, tlv->length); } ``` 编码函数将一个TLV结构体编码成一个字节数组,解码函数将一个字节数组解码成一个TLV结构体,并将Value成员指向新分配的内存空间。 对于TLV序列,可以定义一个TLV链表结构体来存储多个TLV结构体: ```c typedef struct tlv_list { TLV *tlv; struct tlv_list *next; } TLV_LIST; ``` 其中,tlv指向一个TLV结构体,next指向下一个TLV链表结构体。 可以通过遍历TLV链表来实现多个TLV结构体的编码和解码。具体实现方法可以参考以下示例代码: ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stdint.h> typedef struct tlv { uint8_t tag; uint16_t length; uint8_t *value; } TLV; typedef struct tlv_list { TLV *tlv; struct tlv_list *next; } TLV_LIST; // 编码一个TLV结构体 void encode_tlv(TLV *tlv, uint8_t *buf, int *len) { buf[0] = tlv->tag; buf[1] = (tlv->length >> 8) & 0xFF; buf[2] = tlv->length & 0xFF; memcpy(buf + 3, tlv->value, tlv->length); *len = 3 + tlv->length; } // 解码一个TLV结构体 void decode_tlv(TLV *tlv, uint8_t *buf, int len) { tlv->tag = buf[0]; tlv->length = (buf[1] << 8) | buf[2]; tlv->value = malloc(tlv->length); memcpy(tlv->value, buf + 3, tlv->length); } // 添加一个TLV链表结构体 void add_tlv(TLV_LIST **list, TLV *tlv) { TLV_LIST *new_node = malloc(sizeof(TLV_LIST)); new_node->tlv = tlv; new_node->next = NULL; if (*list == NULL) { *list = new_node; } else { TLV_LIST *cur_node = *list; while (cur_node->next != NULL) { cur_node = cur_node->next; } cur_node->next = new_node; } } // 释放一个TLV结构体 void free_tlv(TLV *tlv) { free(tlv->value); free(tlv); } // 释放一个TLV链表结构体 void free_tlv_list(TLV_LIST *list) { while (list != NULL) { TLV_LIST *cur_node = list; list = list->next; free_tlv(cur_node->tlv); free(cur_node); } } int main() { // 创建一个TLV链表结构体,并添加两个TLV结构体 TLV_LIST *tlv_list = NULL; TLV *tlv1 = malloc(sizeof(TLV)); tlv1->tag = 0x01; tlv1->length = 3; tlv1->value = (uint8_t*)malloc(3); tlv1->value[0] = 0x01; tlv1->value[1] = 0x02; tlv1->value[2] = 0x03; add_tlv(&tlv_list, tlv1); TLV *tlv2 = malloc(sizeof(TLV)); tlv2->tag = 0x02; tlv2->length = 5; tlv2->value = (uint8_t*)malloc(5); tlv2->value[0] = 0x04; tlv2->value[1] = 0x05; tlv2->value[2] = 0x06; tlv2->value[3] = 0x07; tlv2->value[4] = 0x08; add_tlv(&tlv_list, tlv2); // 编码TLV链表 int tlv_list_len = 0; TLV_LIST *cur_node = tlv_list; while (cur_node != NULL) { tlv_list_len += cur_node->tlv->length + 3; cur_node = cur_node->next; } uint8_t *tlv_list_buf = malloc(tlv_list_len); int tlv_list_pos = 0; cur_node = tlv_list; while (cur_node != NULL) { encode_tlv(cur_node->tlv, tlv_list_buf + tlv_list_pos, &(cur_node->tlv->length)); tlv_list_pos += cur_node->tlv->length; cur_node = cur_node->next; } // 解码TLV链表 cur_node = NULL; int pos = 0; while (pos < tlv_list_len) { TLV *tlv = malloc(sizeof(TLV)); decode_tlv(tlv, tlv_list_buf + pos, tlv_list_len - pos); pos += tlv->length; add_tlv(&cur_node, tlv); } // 打印结果 cur_node = cur_node; while (cur_node != NULL) { printf("Tag: %02X\n", cur_node->tlv->tag); printf("Length: %d\n", cur_node->tlv->length); printf("Value: "); for (int i = 0; i < cur_node->tlv->length; i++) { printf("%02X ", cur_node->tlv->value[i]); } printf("\n"); cur_node = cur_node->next; } // 释放内存 free(tlv_list_buf); free_tlv_list(tlv_list); free_tlv_list(cur_node); return 0; } ``` 在这个示例中,首先创建一个TLV链表结构体,并分别添加两个TLV结构体。然后编码TLV链表成一个字节数组,并将其解码成另一个TLV链表结构体。最后打印结果并释放内存。 通过这个示例,可以看到使用C语言实现TLV协议是比较简单的。需要注意的是,由于TLV协议的长度是使用两个字节表示的,因此需要考虑其字节序。在本例中,假设使用的是大端字节序。在实际应用中,需要根据具体情况选择适当的字节序。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值