简体中文

RDT的切包和组包

RDT的切包和组包

更新日期:2025/2/13


概述

RDTAPIs是P2P sdk里面的一个提供可靠传输的模块,提供通用的数据传输(read和write)接口,发送端和接收端都有缓存区用以缓存发送中或者接收中的数据。

因为RDT模块在发送数据时,不会完全按照一帧一帧地传,实际为可能一帧被分成多个分片,或者多帧组成一个分片来传,所以接收端在读取数据的时候,需要对数据进行重组。


RDT的帧设计

RDT的一帧可以设计为5个部分,分别为帧起始标志,帧类型,当前帧大小,帧数据,帧结束位:

frmBegin[4]|frmInfo[5]|data[...]|frmEnd[2]

设计帧头frmInfo为:type+dataSize

type[1]|dataSize[4]

开发者可以根据实际场景,修改frmInfo,只要确保两端使用同样的frmInfo即可。


RDT的帧读取方法


  1. RDT帧的读取原则:
  2. 先读取frmBegin,4个字节。
  3. 接着读取一个frmInfo,5个字节。
  4. 从frmInfo中解析出type以及dataSize,然后进行数据读取:


读取数据头:


/**
 * 读取帧信息数据(通过RDT通道)
 * @param frmInfo 接收帧信息的缓冲区
 * @param frmInfoBufferSize 缓冲区大小(需 >= frmInfoSize,防止溢出)
 * @param frmInfoSize 预期读取的帧信息总大小
 * @return 0=读取成功,负数=错误码(RDT_Read返回的错误)
 */
int readFrameInfo(char* frmInfo, size_t frmInfoBufferSize, int frmInfoSize) {
    // 参数合法性校验:缓冲区大小不足或指针为空
    if (frmInfo == NULL || frmInfoBufferSize < (size_t)frmInfoSize) {
        return -1;  // 可替换为具体错误码(如自定义的BUFFER_SIZE_INSUFFICIENT)
    }

    int readSize = 0;          // 单次读取字节数
    int rest = frmInfoSize;    // 剩余需读取的字节数
    memset(frmInfo, 0, frmInfoBufferSize);  // 清空接收缓冲区

    // 循环读取直到获取完整帧信息
    while (rest > 0) {
        // 读取数据:从剩余偏移位置开始,读取剩余长度,超时1000ms
        readSize = RDT_Read(
            rdt_channel_id,
            frmInfo + (frmInfoSize - rest),  // 当前写入位置 = 起始位置 + 已读长度
            rest,
            1000
        );

        // 处理读取结果
        if (readSize == RDT_ER_TIMEOUT) {
            // 超时:继续尝试读取(不返回错误,直到读取完成或其他错误)
            continue;
        }
        else if (readSize < 0) {
            // 发生错误(非超时):返回错误码
            return readSize;
        }

        // 累加已读长度
        rest -= readSize;
    }

    // 全部读取完成
    return 0;
}


读取数据:

//读取帧数据,确保buffer足够大,否则进行resize buffer。

/**
 * 通过RDT通道读取一帧完整数据
 * @param dataBuf 接收数据的缓冲区
 * @param dataBufferSize 缓冲区大小(需确保 >= dataSize,防止止溢出)
 * @param dataSize 预期读取的帧数据总大小
 * @return 实际读取的字节数(0~dataSize),负数表示错误(RDT_Read返回的错误码)
 */
int readOneFrame(char* dataBuf, size_t dataBufferSize, int dataSize) {
    // 参数合法性校验
    if (dataBuf == NULL || dataBufferSize < (size_t)dataSize) {
        return -1;  // 可替换为具体错误码(如BUFFER_SIZE_INSUFFICIENT)
    }

    int readSize = 0;          // 单次读取字节数
    int rest = dataSize;       // 剩余需读取的字节数
    int totalRead = 0;         // 累计读取字节数
    memset(dataBuf, 0, dataBufferSize);  // 清空接收缓冲区

    // 循环读取直到获取完整帧数据或发生错误
    while (rest > 0) {
        // 读取数据:从当前偏移位置开始,读取剩余长度,超时1000ms
        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;
    }

    // 返回实际读取的总字节数(正常情况应等于dataSize)
    return totalRead;
}


读取帧结束标志:


/**
 * 读取帧结束标志(固定2字节)
 * @return 0=读取成功,负数=错误码(RDT_Read返回的错误)
 */
int readFrameEndFlag() {
    const int END_FLAG_SIZE = 2;  // 帧结束标志固定大小(2字节)
    int readSize = 0;             // 单次读取字节数
    int rest = END_FLAG_SIZE;     // 剩余需读取的字节数
    char buffer[8] = {0};         // 接收缓冲区(预留8字节避免溢出)

    // 循环读取直到获取完整的结束标志
    while (rest > 0) {
        // 读取数据:从剩余偏移位置开始,读取剩余长度,超时1000ms
        readSize = RDT_Read(
            rdt_channel_id,
            buffer + (END_FLAG_SIZE - rest),  // 当前写入位置
            rest,
            1000
        );

        // 处理读取结果
        if (readSize == RDT_ER_TIMEOUT) {
            // 超时:继续尝试读取
            continue;
        }
        else if (readSize < 0) {
            // 发生错误(非超时):返回错误码
            return readSize;
        }

        // 更新剩余读取长度
        rest -= readSize;
    }

    // 读取成功(如需验证标志值,可在此处添加校验逻辑)
    // 示例:if (buffer[0] != 0xAA || buffer[1] != 0x55) return -ERROR_INVALID_END_FLAG;
    
    return 0;
}


所以循环读取一个完整帧的方法:


// 帧信息固定大小(5字节)
#define FRAME_INFO_SIZE 5

// 主循环:持续读取并处理完整数据帧
while (1) {
    // 1. 读取帧信息(5字节)
    char frmInfoBuffer[8] = {0};  // 缓冲区预留8字节,确保大于FRAME_INFO_SIZE
    int ret = readFrameInfo(frmInfoBuffer, sizeof(frmInfoBuffer), FRAME_INFO_SIZE);
    if (ret < 0) {
        printf("读取帧信息失败,错误码:%d\n", ret);
        break;
    }

    // 2. 解析帧信息:获取数据类型和数据大小
    int dataType = 0;
    int dataSize = 0;
    parseFrameInfo(frmInfoBuffer, &dataType, &dataSize); 

    // 校验数据大小合理性(防止缓冲区溢出)
    if (dataSize <= 0 || dataSize > 100 * 1024) {
        printf("无效的数据大小:%d,跳过当前帧\n", dataSize);
        continue;  // 数据大小异常,跳过处理
    }

    // 3. 读取完整数据帧
    char dataBuffer[100 * 1024] = {0};  // 100KB数据缓冲区
    ret = readOneFrame(dataBuffer, sizeof(dataBuffer), dataSize);
    if (ret < 0) {
        printf("读取数据帧失败,错误码:%d\n", ret);
        break;
    }
    // 验证是否读取完整(readOneFrame返回实际读取长度)
    if (ret != dataSize) {
        printf("数据帧不完整(预期:%d,实际:%d),跳过处理\n", dataSize, ret);
        continue;
    }

    // 4. 处理完整数据帧
    handleUserData(dataBuffer, dataSize, dataType);  // 优化:传入数据大小和类型,增强处理能力

    // 5. 读取帧结束标志(验证帧完整性)
    ret = readFrameEndFlag();
    if (ret < 0) {
        printf("读取帧结束标志失败,错误码:%d\n", ret);
        break;
    }
}


到此为止,已经完成了对RDT数据的解析,开发者可以在开发过程中,根据项目的实际情况,设计自己的帧格式。

即刻开启您的物联网之旅

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

+86 755 27702549

7×24小时服务热线

法律声明 隐私权条款

关注“TUTK”

TUTK服务尽在掌握

© 2022 物联智慧科技(深圳)有限公司版权所有粤ICP备14023641号
在线咨询
扫一扫

TUTK服务尽在掌握

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

返回顶部