简体中文

RDT的切包和组包

星空无限传媒xk8027-高清全集免费看

一、模块概述

RDT(Reliable Data Transfer)是 TUTK P2P SDK 中的可靠传输模块,提供通用的 RDT_ReadRDT_Write 数据传输接口,适用于需要稳定传输数据的场景(如设备控制指令、文件传输等)。

核心特性说明:

  • 发送端/接收端均内置缓冲区,缓存传输中或待处理的数据;
  • 传输机制:数据不会严格按"一帧一帧"传输,可能存在「单帧分片」或「多帧合并」的情况;
  • 关键要求:接收端必须通过自定义帧格式解析数据,实现分片重组,才能获取完整的业务数据。

二、RDT帧设计

为实现数据分片重组,需自定义统一的帧格式,确保发送端和接收端遵循相同的解析规则。推荐帧结构分为5个部分,兼顾完整性校验和扩展性:

(一)参考帧结构
// 帧结构总览(字节数固定部分+可变部分) frmBegin[4] | frmInfo[5] | data[dataSize] | frmEnd[2] // 各部分说明 1. frmBegin[4]:帧起始标志(固定4字节)  - 作用:标识一帧数据的开始,用于同步帧边界  - 示例:0xAA 0xBB 0xCC 0xDD(可自定义,需两端一致) 2. frmInfo[5]:帧信息头(固定5字节,含业务核心信息)  - 子结构:type[1] + dataSize[4]    - type[1]:数据类型(1字节,自定义业务类型,如0x01=控制指令、0x02=文件数据)    - dataSize[4]:数据体长度(4字节,大端/小端需两端统一,推荐大端) 3. data[dataSize]:业务数据体(可变长度,由dataSize指定)  - 存储实际需要传输的业务数据(如指令内容、文件分片等) 4. frmEnd[2]:帧结束标志(固定2字节)  - 作用:标识一帧数据的结束,用于校验帧完整性  - 示例:0xEE 0xFF(可自定义,需两端一致)
说明:帧结构的固定部分(frmBegin、frmInfo、frmEnd)总长度为11字节(也可根据实际场景自行扩展),可变部分长度由dataSize动态指定,确保解析时可准确定位各字段边界。
(二)帧结构自定义说明
  • 核心约束:frmBeginfrmInfofrmEnd 的长度和格式需在发送端、接收端完全一致,否则会导致解析失败;
  • 扩展建议:若需更多业务字段(如校验码、序列号),可扩展 frmInfo 长度(如改为8字节),示例:                        
    frmInfo[8] = type[1] + seq[2] + dataSize[4] + crc[1](seq=序列号,crc=数据校验码);
  • 注意:帧标志(frmBegin/frmEnd)需避免与业务数据重复,防止误识别帧边界。

三、帧读取方法

接收端需按「帧起始标志 → 帧信息头 → 业务数据 → 帧结束标志」的顺序读取数据,通过循环重试机制处理分片传输场景,确保获取完整帧。

以下为各环节的标准实现(C语言示例),依赖 RDT 核心接口 RDT_Read(rdt_channel_id, buf, len, timeout)

1. 读取帧起始标志(frmBegin)

读取固定4字节的帧起始标志,用于定位帧的开始位置。

// 全局变量:RDT通道ID(需在建立RDT连接后获取,如IOTC_Connect_RDT返回的通道ID) int rdt_channel_id = -1; // 帧起始标志定义(需与发送端一致) #define FRAME_BEGIN_SIZE 4 const unsigned char FRAME_BEGIN[] = {0xAA, 0xBB, 0xCC, 0xDD}; /** * 读取帧起始标志 * @return 0=读取成功且标志匹配,负数=错误码(-1=缓冲区不足,-2=标志不匹配,其他=RDT_Read错误码) */ int readFrameBegin() {    int readSize = 0;        // 单次读取字节数    int rest = FRAME_BEGIN_SIZE; // 剩余需读取的字节数    unsigned char buffer[FRAME_BEGIN_SIZE] = {0}; // 接收缓冲区    // 循环读取完整的起始标志(处理分片)    while (rest > 0) {        readSize = RDT_Read(            rdt_channel_id,            buffer + (FRAME_BEGIN_SIZE - rest), // 当前写入位置            rest,            1000 // 超时时间(ms),可根据业务调整        );        // 处理读取结果        if (readSize == RDT_ER_TIMEOUT) {            // 超时:继续重试(避免因分片传输导致读取失败)            continue;        } else if (readSize < 0) {            // 非超时错误(如通道关闭):返回RDT错误码            return readSize;        }        rest -= readSize;    }    // 验证起始标志是否匹配(防止误识别)    if (memcmp(buffer, FRAME_BEGIN, FRAME_BEGIN_SIZE) != 0) {        return -2; // 标志不匹配,返回自定义错误码    }    return 0; // 读取成功且标志有效 }
2. 读取帧信息头(frmInfo)

读取固定5字节的帧信息头,解析出数据类型(type)和数据体长度(dataSize),为后续数据读取做准备。

// 帧信息头固定大小(5字节) #define FRAME_INFO_SIZE 5 /** * 读取帧信息头 * @param frmInfo 接收帧信息的缓冲区(需提前分配至少FRAME_INFO_SIZE字节) * @param frmInfoBufferSize 缓冲区实际大小(用于合法性校验) * @return 0=读取成功,负数=错误码(-1=参数非法,其他=RDT_Read错误码) */ int readFrameInfo(char* frmInfo, size_t frmInfoBufferSize) {    // 参数合法性校验(避免空指针和缓冲区溢出)    if (frmInfo == NULL || frmInfoBufferSize < FRAME_INFO_SIZE) {        return -1;    }    int readSize = 0;    int rest = FRAME_INFO_SIZE;    memset(frmInfo, 0, frmInfoBufferSize); // 清空缓冲区    // 循环读取完整的帧信息头    while (rest > 0) {        readSize = RDT_Read(            rdt_channel_id,            frmInfo + (FRAME_INFO_SIZE - rest),            rest,            1000        );        if (readSize == RDT_ER_TIMEOUT) {            continue;        } else if (readSize < 0) {            return readSize;        }        rest -= readSize;    }    return 0; }
3. 解析帧信息头(parseFrameInfo)

从读取到的 frmInfo 中解析出 type(数据类型)和 dataSize(数据体长度),需注意字节序转换(若为大端传输)。

#include// 用于ntohl函数(网络字节序转主机字节序) /** * 解析帧信息头 * @param frmInfo 已读取的帧信息头缓冲区(长度>=FRAME_INFO_SIZE) * @param outType 输出参数:数据类型(需提前分配内存) * @param outDataSize 输出参数:数据体长度(需提前分配内存) * @return 0=解析成功,-1=参数非法 */ int parseFrameInfo(const char* frmInfo, int* outType, int* outDataSize) {    if (frmInfo == NULL || outType == NULL || outDataSize == NULL) {        return -1;    }    // 解析数据类型(1字节,直接读取)    *outType = (unsigned char)frmInfo[0];    // 解析数据体长度(4字节,假设发送端为网络字节序,转换为主机字节序)    uint32_t dataSizeNet = 0;    memcpy(&dataSizeNet, frmInfo + 1, 4); // frmInfo[1]~frmInfo[4]为dataSize    *outDataSize = ntohl(dataSizeNet); // 大端转主机字节序(若为小端则用ntohs/直接读取)    return 0; }
4. 读取业务数据体(data)

根据解析出的 dataSize 读取完整的业务数据,确保缓冲区大小足够容纳数据体。

/** * 读取业务数据体 * @param dataBuf 接收数据的缓冲区(需提前分配>=dataSize字节) * @param dataBufSize 缓冲区实际大小(用于合法性校验) * @param dataSize 预期读取的数据体长度(从frmInfo解析获得) * @return 实际读取的字节数(正常=dataSize),负数=错误码(-1=参数非法,其他=RDT_Read错误码) */ int readOneFrameData(char* dataBuf, size_t dataBufSize, int dataSize) {    // 参数合法性校验(防止缓冲区溢出和非法参数)    if (dataBuf == NULL || dataBufSize < (size_t)dataSize || dataSize <= return="" int="" readsize="0;" rest="" totalread="0;" while=""> 0) {        readSize = RDT_Read(            rdt_channel_id,            dataBuf + (dataSize - rest), // 当前写入位置            rest,            1000        );        if (readSize == RDT_ER_TIMEOUT) {            continue;        } else if (readSize < 0) {            return readSize;        }        totalRead += readSize;        rest -= readSize;    }    return totalRead; }
5. 读取帧结束标志(frmEnd)

读取固定2字节的帧结束标志,验证帧的完整性。

// 帧结束标志定义(需与发送端一致) #define FRAME_END_SIZE 2 const unsigned char FRAME_END[] = {0xEE, 0xFF}; /** * 读取帧结束标志 * @return 0=读取成功且标志匹配,负数=错误码(-2=标志不匹配,其他=RDT_Read错误码) */ int readFrameEnd() {    int readSize = 0;    int rest = FRAME_END_SIZE;    unsigned char buffer[FRAME_END_SIZE] = {0};    // 循环读取完整的结束标志    while (rest > 0) {        readSize = RDT_Read(            rdt_channel_id,            buffer + (FRAME_END_SIZE - rest),            rest,            1000        );        if (readSize == RDT_ER_TIMEOUT) {            continue;        } else if (readSize < 0) {            return readSize;        }        rest -= readSize;    }    // 验证结束标志是否匹配    if (memcmp(buffer, FRAME_END, FRAME_END_SIZE) != 0) {        return -2;    }    return 0; }

四、完整读取示例

将上述分步读取方法整合,实现持续监听并解析RDT数据帧的完整流程,包含错误处理和异常兼容。

// 业务数据缓冲区大小(根据实际最大dataSize调整,示例:100KB) #define MAX_DATA_BUFFER_SIZE 100 * 1024 // 业务数据处理函数(示例:根据数据类型分发处理) void handleBusinessData(int dataType, const char* dataBuf, int dataSize) {    switch (dataType) {        case 0x01: // 控制指令类型            printf("收到控制指令,长度:%d字节,内容:%s\n", dataSize, dataBuf);            // TODO:执行控制指令逻辑            break;        case 0x02: // 文件数据类型            printf("收到文件数据,长度:%d字节\n", dataSize);            // TODO:写入文件或拼接文件分片            break;        default:            printf("收到未知类型数据,类型:%d,长度:%d字节\n", dataType, dataSize);            break;    } } // 主循环:持续读取并解析RDT数据帧 int rdtFrameReadLoop() {    char frmInfo[FRAME_INFO_SIZE] = {0};    char dataBuffer[MAX_DATA_BUFFER_SIZE] = {0};    int dataType = 0;    int dataSize = 0;    int ret = 0;    // 循环监听数据(直到通道关闭或主动退出)    while (1) {        // 1. 读取帧起始标志        ret = readFrameBegin();        if (ret < 0) {            if (ret == -2) {                printf("帧起始标志不匹配,跳过异常数据\n");                continue; // 标志不匹配,跳过当前数据,继续下一轮监听            } else {                printf("读取帧起始标志失败,错误码:%d\n", ret);                break; // RDT通道错误,退出循环            }        }        // 2. 读取帧信息头        ret = readFrameInfo(frmInfo, sizeof(frmInfo));        if (ret < 0) {            printf("读取帧信息头失败,错误码:%d\n", ret);            break;        }        // 3. 解析帧信息头        ret = parseFrameInfo(frmInfo, &dataType, &dataSize);        if (ret < 0) {            printf("解析帧信息头失败\n");            continue;        }        // 4. 校验数据大小合法性(防止缓冲区溢出)        if (dataSize <= 0="" datasize=""> MAX_DATA_BUFFER_SIZE) {            printf("无效的数据大小:%d字节(超出缓冲区限制),跳过当前帧\n", dataSize);            // 跳过当前帧的剩余数据(避免影响后续帧解析)            // TODO:可选实现:读取并丢弃当前帧的data和frmEnd            continue;        }        // 5. 读取业务数据体        ret = readOneFrameData(dataBuffer, sizeof(dataBuffer), dataSize);        if (ret < 0) {            printf("读取业务数据失败,错误码:%d\n", ret);            break;        }        if (ret != dataSize) {            printf("数据体不完整(预期:%d字节,实际:%d字节),跳过当前帧\n", dataSize, ret);            continue;        }        // 6. 读取帧结束标志        ret = readFrameEnd();        if (ret < 0) {            if (ret == -2) {                printf("帧结束标志不匹配,跳过当前帧\n");                continue;            } else {                printf("读取帧结束标志失败,错误码:%d\n", ret);                break;            }        }        // 7. 处理完整的业务数据        handleBusinessData(dataType, dataBuffer, dataSize);        // 清空缓冲区(避免残留数据影响下一轮解析)        memset(dataBuffer, 0, sizeof(dataBuffer));    }    printf("RDT帧读取循环退出\n");    return ret; }
说明:完整示例包含参数校验、异常处理、缓冲区管理等关键逻辑,可直接集成到实际项目中,只需根据业务需求修改MAX_DATA_BUFFER_SIZEhandleBusinessData函数。
示例说明
  • 流程完整性:覆盖「帧起始 → 帧信息 → 业务数据 → 帧结束」的全链路读取和校验;
  • 错误处理:针对标志不匹配、数据大小异常、超时等场景做兼容,避免程序崩溃;
  • 扩展性:handleBusinessData 函数可根据实际业务需求扩展(如指令解析、文件存储等);
  • 注意:需在调用 rdtFrameReadLoop 前确保 rdt_channel_id 已通过 RDT 连接接口(如 IOTC_Connect_RDT)获取并有效。

即刻开启您的物联网之旅

联系解决方案专家
Kalay App
资讯安全白皮书
全球专利布局
解决方案
新闻动态
公司动态
行业资讯
媒体报道
永续发展
经营者的话
社会参与
环境永续
公司治理

+86 755 27702549

7×24小时服务热线

法律声明 隐私权条款

关注“TUTK”

TUTK服务尽在掌握

© 2022 星空无限传媒xk8027版权所有粤ICP备14023641号
在线咨询
扫一扫

TUTK服务尽在掌握

全国免费服务热线
+86 755 27702549

返回顶部