首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C >> 關於C

C語言實現TLV消息編碼解析與常用操作

     編輯:關於C

TLV(Type-Length-Value)作為通信協議中經典的編碼格式,廣泛應用於智能卡、物聯網、金融支付等領域。本文詳細講解 TLV 結構特點,並提供完整的 C 語言實現:消息鏈表構建、按 Tag 排序、異構消息比較查找相同信元。

一、TLV 格式速覽

  • T (Tag):標識信元類型,長度固定(常見 1-4 字節)
  • L (Length):V 字段長度,固定長度(常見 1-2 字節)
  • V (Value):數據載荷,長度由 L 指定,可嵌套子 TLV
  • 字節序:通常採用大端模式網絡傳輸

示例(T=4字節, L=2字節, 大端):

消息: 09 00 00 00  04 00  01 01 01 01
T = 0x09000000 (9)
L = 0x0004 (4)
V = 01 01 01 01 (4字節全為1)

二、核心數據結構設計

#define TLV_T_LEN 4
#define TLV_L_LEN 2

typedef enum { ERR = -1, OK = 0 } RETCODE;

// 信元結構
typedef struct _stIE {
    unsigned int ulTag;      // T
    unsigned short usLen;    // L
    unsigned char *pValue;   // V (指向原始緩衝區,不復制)
} IE;

// 消息鏈表節點
typedef struct _stMsg {
    IE ie;
    struct _stMsg *pNext;
} Msg;

三、關鍵算法實現

3.1 有序鏈表構建(按 Tag 遞增)

// 創建空鏈表(帶頭節點)
int CreateMsgList(Msg **ppList) {
    *ppList = (Msg*)malloc(sizeof(Msg));
    if (!*ppList) return ERR;
    memset(&(*ppList)->ie, 0, sizeof(IE));
    (*ppList)->pNext = NULL;
    return OK;
}

// 解析單個信元(零拷貝指向原緩衝區)
IE* GetIEFromMsg(const unsigned char *pInMsg) {
    IE *pIE = (IE*)malloc(sizeof(IE));
    if (!pIE) return NULL;
    pIE->ulTag = *(unsigned int*)pInMsg;
    pIE->usLen = *(unsigned short*)(pInMsg + TLV_T_LEN);
    pIE->pValue = (unsigned char*)(pInMsg + TLV_T_LEN + TLV_L_LEN);
    return pIE;
}

// 按 Tag 有序插入
int InsertIEToMsgList(Msg *pList, const IE *pIE) {
    Msg *pNew = (Msg*)malloc(sizeof(Msg));
    if (!pNew) return ERR;
    memcpy(&pNew->ie, pIE, sizeof(IE));
    pNew->pNext = NULL;

    Msg *p = pList;
    while (p->pNext && p->pNext->ie.ulTag <= pIE->ulTag)
        p = p->pNext;
    pNew->pNext = p->pNext;
    p->pNext = pNew;
    return OK;
}

// 完整解析並構建有序鏈表
int CreateSortMsgList(unsigned char *pInMsg, unsigned int ulMsgLen, Msg **ppOutList) {
    if (CreateMsgList(ppOutList) != OK) return ERR;
    unsigned int offset = 0;
    while (offset < ulMsgLen) {
        IE *pIE = GetIEFromMsg(pInMsg + offset);
        if (!pIE || InsertIEToMsgList(*ppOutList, pIE) != OK) {
            DestoryMsgList(*ppOutList);
            return ERR;
        }
        offset += TLV_T_LEN + TLV_L_LEN + pIE->usLen;
    }
    return OK;
}

3.2 消息排序輸出

int Sort(unsigned char *pInMsg, unsigned int ulMsgLen, unsigned char *pOutMsg) {
    Msg *pList = NULL;
    if (CreateSortMsgList(pInMsg, ulMsgLen, &pList) != OK) return ERR;

    unsigned char *pDst = pOutMsg;
    Msg *p = pList->pNext;  // 跳過頭節點
    while (p) {
        memcpy(pDst, &p->ie, TLV_T_LEN + TLV_L_LEN);
        memcpy(pDst + TLV_T_LEN + TLV_L_LEN, p->ie.pValue, p->ie.usLen);
        pDst += TLV_T_LEN + TLV_L_LEN + p->ie.usLen;
        p = p->pNext;
    }
    DestoryMsgList(pList);
    return OK;
}

3.3 異構消息比較:查找完全相同信元

// 比較兩個信元 T/L/V 是否完全一致
int IsSameIE(const IE *a, const IE *b) {
    return (a->ulTag == b->ulTag &&
            a->usLen == b->usLen &&
            memcmp(a->pValue, b->pValue, a->usLen) == 0) ? OK : ERR;
}

// 統計兩個消息中相同信元個數
int CompareMsg(unsigned char *pMsg1, unsigned int len1,
               unsigned char *pMsg2, unsigned int len2,
               unsigned int *pSameCount) {
    Msg *list1 = NULL, *list2 = NULL;
    if (CreateSortMsgList(pMsg1, len1, &list1) != OK ||
        CreateSortMsgList(pMsg2, len2, &list2) != OK) {
        DestoryMsgList(list1); DestoryMsgList(list2);
        return ERR;
    }

    unsigned int count = 0;
    for (Msg *p = list1->pNext; p; p = p->pNext) {
        for (Msg *q = list2->pNext; q; q = q->pNext) {
            if (IsSameIE(&p->ie, &q->ie) == OK) count++;
        }
    }
    *pSameCount = count;
    DestoryMsgList(list1); DestoryMsgList(list2);
    return OK;
}

四、測試驗證

int main() {
    // 測試數據:包含多個 Tag 重複、順序打亂的信元
    unsigned char msg1[] = { /* ... */ };
    unsigned char msg2[] = { /* ... */ };

    unsigned int len1 = sizeof(msg1), len2 = sizeof(msg2);
    unsigned char *sorted = (unsigned char*)malloc(len1);
    Sort(msg1, len1, sorted);  // 輸出按 Tag 排序後的消息

    unsigned int same = 0;
    CompareMsg(msg1, len1, msg2, len2, &same);  // 輸出相同信元數
    printf("Same Number: %u\n", same);
    return 0;
}

五、工程化注意事項

  1. 內存管理:鏈表銷毀時遞歸釋放節點與信元結構體,pValue 指向原始緩衝區不釋放
  2. 字節序處理:跨平台時需統一網絡字節序(htonl/ntohl、htons/ntohs)
  3. 邊界檢查:解析循環中嚴格校驗 offset + TLV_T_LEN + TLV_L_LEN + usLen <= ulMsgLen 防緩衝區越界
  4. 擴展性:結構體預留 pNext 易於支持嵌套 TLV 解析

總結

本文提供的 TLV 處理框架採用零拷貝解析 + 有序鏈表索引設計,時間複雜度 O(n log n)(插入排序)或 O(n)(歸併排序優化),空間複雜度 O(n)。代碼結構清晰、模塊化程度高,可直接集成到嵌入式協議棧或網關解析模塊中,並易於擴展支持 BER-TLV、嵌套結構等高級特性。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved