TLV(Type-Length-Value)作為通信協議中經典的編碼格式,廣泛應用於智能卡、物聯網、金融支付等領域。本文詳細講解 TLV 結構特點,並提供完整的 C 語言實現:消息鏈表構建、按 Tag 排序、異構消息比較查找相同信元。
示例(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;
// 創建空鏈表(帶頭節點)
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;
}
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;
}
// 比較兩個信元 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;
}
pValue 指向原始緩衝區不釋放htonl/ntohl、htons/ntohs)offset + TLV_T_LEN + TLV_L_LEN + usLen <= ulMsgLen 防緩衝區越界pNext 易於支持嵌套 TLV 解析本文提供的 TLV 處理框架採用零拷貝解析 + 有序鏈表索引設計,時間複雜度 O(n log n)(插入排序)或 O(n)(歸併排序優化),空間複雜度 O(n)。代碼結構清晰、模塊化程度高,可直接集成到嵌入式協議棧或網關解析模塊中,並易於擴展支持 BER-TLV、嵌套結構等高級特性。