简体中文

基于RDTAPIs的文件下载

基于RDTAPIs的文件下载

主要内容:

1.IO交互
2.通道的创建和销毁
3.数据的传输
4.结束标志 的判断

IO交互:

基于RDTAPIs的文件下载.png

1.APP会通过avSendIOCtrl(IOCTRL_FILEMANAGER_FILE_LIST_REQ)查询指定时间内的文件。设备通过avSendIOCtrl(IOCTRL_FILEMANAGER_FILE_LIST_RESP)回复对应的文件列表。
2.当用户选中部分文件下载的时候,则APP端会avSendIOCtrl(IOCTRL_FILEMANAGER_FILE_DOWNLOAD_REQ)告知设备要下载哪些文件。设备则根据自身的硬件和软件资源,可以自定义使用的通道数,通过avSendIOCtrl(IOCTRL_FILEMANAGER_FILE_DOWNLOAD_RESP)告知APP使用哪些通道以及使用哪种APIs来进行数据传输。
3.APP和设备端分别创建对应的通道。
4.数据传输。
5.数据传输后关闭通道。

RDT通道的创建和销毁

  • 创建(APP端/设备端通用)
int createChannelForDownload(int sid,int iotc_channel_id)
{
    return RDT_Create(sid,5000,iotcChannelId);
}
  • 销毁(APP端/设备端通用)
void destoryChannelOfDownload(int rdt_id)
{
    if(rdt_id < 0)
        return ;
        
    RDT_Abort(rdt_id);
}

数据的传输

RDT协议本身提供的接口只有Read和Write,所以存在粘包的情况,需要另外设计切包的机制。每个数据包分为包头、数据和包尾,具体参考:https://share.note.youdao.com/s/JOFyOca5

  • 设备端

    说明:RDT的数据传输是可靠的传输,每次调用RDT_Write,都将数据写入本地的发送缓存区,所以等所有的数据都使用RDT_Write写入后,需要检查发送缓存区是否为空,为空,则说明数据已经送完。
    写入数据相关(伪)代码:

#define RDT_HEADER_FRAMEBEGIN_ADDR 0
#define RDT_HEADER_FILENAME_ADDR 4
#define RDT_HEADER_FILESIZE_ADDR 68
#define RDT_HEADER_FRAMESIZE_ADDR 72
#define RDT_HEADER_ENDFLAG_ADDR 76
#define RDT_FRAME_BUFFER_ADDR 77
#define RDT_FRAME_BUFFER_SIZE 10243
#define RDT_FRAME_HEADER_SIZE 77
#define RDT_PAYLOAD_WRITE_POSTION 77
#define RDT_FRAME_TAIL_SIZE 2

union typeXChange{
int int_data;    char char_data[4];
};

//把size写入buffer对应位置。
bool swapInt2CharBuffer(const int int_data,char*buffer,size_t bufferSize)
{
    if(!buffer || bufferSize != 4)
        return false;

    typeXChange tmp;
    tmp.int_data = int_data;
    memcpy(buffer,tmp.char_data,4);
    return true;
}

//在buffer起始处写入header
void createPacketHeader(char* buffer_write_postion, size_t bufferSize, char* fileName, size_t fileSize, size_t payloadSize)
{
    memset(buffer_write_postion,0,RDT_FRAME_HEADER_SIZE);    

    *buffer_write_postion='I';
    *(buffer_write_postion+1)='O';
    *(buffer_write_postion+2)='T';
    *(buffer_write_postion+3)='C';  
  
    strncpy(buffer_write_postion+RDT_HEADER_FILENAME_ADDR,fileName,strlen(fileName));
    swapInt2CharBuffer(fileSize,buffer_write_postion+RDT_HEADER_FILESIZE_ADDR,4);
    swapInt2CharBuffer(payloadSize,buffer_write_postion+RDT_HEADER_FRAMESIZE_ADDR,4);
}

//在buffer结尾2个字节写入tail
void createPacketTail(char* buffer_write_postion,size_t bufferSize)
{
    *buffer_write_postion = 'G';
    *(buffer_write_postion+1) = 'C';
}

//发送单个文件的伪代码
void sendOneFile2Client(int rdt_id, file f, bool isLastFile)
{
    int ret = 0;
    size_t fileSize = f.size();
    string fileName = f.name();
    size_t rdt_file_size = fileSize;
    unsigned char rdt_endflag = 0;
    int buffer_for_payload_size = RDT_FRAME_BUFFER_SIZE - RDT_FRAME_HEADER_SIZE - RDT_FRAME_TAIL_SIZE;//计算可以给数据最大的存储空间

    f.open();
    while(1){
        char buffer[RDT_FRAME_BUFFER_SIZE] = {0};  
        int readSize = file.read(buffer+RDT_PAYLOAD_WRITE_POSTION,buffer_for_payload_size); //预留了777个字节给header
        if(readsize < buffer_for_payload_size){//此时表示已经无法读满一个完整的payload size,估计已经到了文件最后一包。
            if(isLastFile){ //如果是最后一个文件,设定endflag为1。
                rdt_endflag = 1;
            }
            if(readsize <= 0){
                readsize = 1;
                break;
            }
        } else {
            filesize -= readsize;
        }
        createPacketHeader(buffer,RDT_FRAME_HEADER_SIZE,fileName,rdt_file_size,readsize);
        createPacketTail(buffer+RDT_FRAME_HEADER_SIZE+readsize,RDT_FRAME_TAIL_SIZE);
        ret = RDT_Write(rdt_id,buffer,readSize);
        if(ret < 0){
            goto LAB_SEND_RETURN_ERROR;        
        }
        
        if(rdt_endflag) {
            RDT_Flush(rdt_id);
            break;
        }
    }

    f.close();
    LAB_SEND_RETURN_OK:
        return 0;
    LAB_SEND_RETURN_ERROR:
        return ret;
}

//发送线程,伪代码
void sendFileList(void* arg)
{
    //此处先创建rdt_id,具体参考本章节前面创建通道的方法
    int rdt_id = createChannelForDownload(sid,iotc_channel_id);
    if(rdt_id < 0){
        return ;    
    }
    
    //通道创建成功后,再进行文件传输。
    int fileCount = fileList.count();
    int fileIndex = 1;
    int ret = 0;
    foreach(file file, fileList){
        bool isLastFile = (fileCount == fileIndex);
        ret = sendOneFile2Client(rdt_id, file,isLastFile);
        if(ret < 0){
            printf("%s send error %d\n",filename,ret);
            break;        
        }
    }
    
    //正常发送完,需要检查缓存区的数据是否有被送出去。
    if(ret == 0){
        do{
           st_RDT_Status status; 
           ret = RDT_Status_Check(rdt_id,&status); 
           if(ret < 0)
               break; //异常
           if(status.BufSizeInSendQueue == 0){
               break;//已经发送完最后一帧
           } else {
               msleep(100);       
           }
       } while(1);
    }
    
    //关闭通道
    destoryChannelOfDownload(rdt_id);
}
  • APP端
    接收端也需要按照对应的格式也切包和解析数据,如果切包有误,则可能导致数据解析异常。

读取通道数据和解析方法(伪)代码:

//创建通道参考本章第四部分,此处假设已经创建成功RDT通道,拿到rdt_id
//读数据头
#define HEADER_SIZE 77
int readRDTHeader(int rdt_id,char* headerBuffer,int bufferSize)
{
    if(rdt_id < 0 || !headerBuffer || buffSize < HEADER_SIZE)
        return -1;

    memset(headerBuffer,0,bufferSize);
    int restSize = HEADER_SIZE;
    int headerIndex = 0;    //此处需要读取一个完整的header再去做解析
    do{
        int size = RDT_Read(headerBuffer + headerIndex , restSize,1000);
        if(size > 0){
            headerIndex += size;
            restSize -= size;
        } else if(size != RDT_ER_TIMEOUT)){
            return size;
        }    
    }while(restSize > 0);
    
    return 0;
}

//读取对应长度的二进制文件
int readBinaryDataAndSaveOneFrame(file f,int rdt_id,char* dataBuffer,int bufferSize,int dataLength)
{
    if(rdt_id < 0 || !dataBuffer || buffSize < 0 || dataLength < 0)
        return -1;
    
    int restDataSize = dataLength;
    int readSize = bufferSize < restDataSize ? bufferSize : restDataSize;
    
    do{
        int size = RDT_Read(dataBuffer,readSize,1000);
        if(size > 0){
            restDataSize -= size;
        } else if(size != RDT_ER_TIMEOUT)){
            return size;
        }
        //保存到本地文件
        f.write(dataBuffer,size);
    }while(restDataSize > 0);
    
    return 0;
}

//读尾,2个字节,为"GC"
int readTail()
{
    int restSize = 2;
    char buffer[4] = {0};
    int bufferIndex;
    
    do{
        int size = RDT_Read(dataBuffer+bufferIndex,restSize,1000);
        if(size > 0){
            restSize -= size;
            bufferIndex += size;   
        } else if(size != RDT_ER_TIMEOUT){
            return size; 
        }
    } while(restSize > 0);
    
    if(buffer[0] != 'G' || buffer[1] != 'C') {
        return -1;
    } else{
        return 0;    
    }
}

//接收数据伪代码
#define RDT_BUFFER_SIZE 20480
void recvFilesFromChannel(int rdt_id)
{
    int endFlag = 0;
    int ret = 0;
    file f;
    string fileName, lastFileName;
    
    while(!endFlag){
        char buffer[RDT_BUFFER_SIZE]={0};
        if(ret = readRDTHeader(rdt_id,buffer,RDT_BUFFER_SIZE) < 0){
            break;    
        }    
        //此处进行数据头解析出fileName,dataLength,endFlag;
        //帧头定义可以参考:https://share.note.youdao.com/s/JOFyOca5
        if(!f.isOpen()){
            f.open(fileName);
            lastFileName = fileName;                          
        } else{
            //关闭上个文件,打开一个新文件;
            if(fileName != lastFileName){
                f.close();
                f.open(fileName);
                lastFileName = fileName;             
            }   
        }
        
        if(ret = readBinaryDataAndSaveOneFrame(f,rdt_id,buffer,RDT_BUFFER_SIZE,dataLength) < 0){
            break;
        }

        if(ret = readTail() < 0){
            break;
        }
    }
    
    if(f.isOpen){
        f.close();    
    }
}

//接收线程
void createRDTChannelAndSaveFiles(void* arg)
{
    //创建RDT通道,iotcChannelId来源于IOCTRL_FILEMANAGER_FILE_DOWNLOAD_RESP拿到的channel
    int rdt_id = createChannelForDownload(sid,iotc_channel_id);
    if(rdt_id < 0){
        return ;    
    }

    //接收数据
    recvFilesFromChannel(rdt_id); 
       
    //关闭通道
    destoryChannelOfDownload(rdt_id);
}

即刻开启您的物联网之旅

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

+86 755 27702549

7×24小时服务热线

法律声明 隐私权条款

关注“TUTK”

TUTK服务尽在掌握

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

TUTK服务尽在掌握

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

返回顶部