• 使用MSCOMM发送任意文件,还有一些注意事项


    第一步:发送文件

    FILE* pSENDFILE = _wfopen(m_edit_chosefile, _T("rb"));//以二进制打开待发送文件的的文件指针
    fseek(pSENDFILE, 0, SEEK_END);
    int len = ftell(pSENDFILE);//获得待发送文件总长度,2的32次方=2G
    fseek(pSENDFILE, 0, SEEK_SET);

    if (len > (30 * 1024 * 1024))//如果待发送文件大于30M
    {

    int bigloop = len / (30 * 1024 * 1024);//一共有bigloop个30M
    int bigremain = len % (30 * 1024 * 1024);//发送bigloop个30M之后还bigremain个字节
    for (int i = 0; i < bigloop; i++)//每次循环发送30M,循环结束代表发送完成了bigloop次30M文件
    {
    BYTE ByteBuf_1K[1 * 1024];//每次读取文件buf为1k
    CByteArray ByteArray_1K;//每次发送文件array为1k
    ByteArray_1K.SetSize(1 * 1024);
    for (int j = 0; j < (30 * 1024); j++)//这个循环实现发送30M文件
    {
      fread(ByteBuf_1K, 1 * 1024, 1, pSENDFILE);//从文件读取1k数据到buf中
      for (int k = 0; k < 1 * 1024; k++)
      ByteArray_1K.SetAt(k, ByteBuf_1K[k]);//把buf的1k数据转化为十六进制到array中,用于串口发送
      m_mscomm.put_Output((COleVariant)ByteArray_1K);//发送

      Sleep(2);//等待接收端接收
    }
    Sleep(2000);//等待接收端写30M数据到文件中
    }
    /*发送bigloop次30M之后,如果剩余数据大于1K*/
    if (bigremain > 1 * 1024)
    {

    int smallloop = bigremain / (1 * 1024);//需要发送loop次
    int smallremain = bigremain % (1 * 1024);///发送loop次之后还剩remain个字节
    BYTE ByteBuf_1K[1 * 1024];//这个buf用来存从待发送文件独读出来的数据
    CByteArray ByteArray_1K;//这个array用来存待发送文件十六进制数据
    ByteArray_1K.SetSize(1 * 1024);//一次发送1k数据
    for (int i = 0; i < smallloop; i++)
    {
    fread(ByteBuf_1K, 1 * 1024, 1, pSENDFILE);//把待发送文件的第loop个4k以二进制读取在buf里
    for (int k = 0; k < (1 * 1024); k++)
    ByteArray_1K.SetAt(k, ByteBuf_1K[k]);//把buf里的字符以十六进制存在array里
    m_mscomm.put_Output((COleVariant)ByteArray_1K);//发送
    Sleep(2);//等待接收方写文件所需时间,否则会有乱码;这个时间过大会阻塞,过小会丢包
    }
    /*发送剩余的smallremain个字节*/
    BYTE *bufremain;
    bufremain = new BYTE[smallremain];//new1个数组,发送剩余数据
    CByteArray arrayremain;//装剩余数据的十六进制
    arrayremain.SetSize(smallremain);
    fread(bufremain, smallremain, 1, pSENDFILE);//读取剩余数据
    for (int l = 0; l < smallremain; l++)//转换数据为CByteArray类型
    arrayremain.SetAt(l, bufremain[l]);
    m_mscomm.put_Output((COleVariant)arrayremain);//发送剩余数据
    Sleep(200);//短暂休眠
    delete bufremain;//回收bufremain
    fclose(pSENDFILE);//关闭文件指针
    }
    /*发送bigloop次30M之后,如果剩余数据小于1K*/
    else
    {

    BYTE *buf;
    buf = new BYTE[bigremain];//new一个buf来存从文件指针读取的数据
    fread(buf, bigremain, 1, pSENDFILE);//读取待发送文件数据,存在buf里面
    CByteArray array;//用来保存发送数据的十六进制形式
    array.SetSize(bigremain);//给array设置大小,这个大小为待发送文件的字节数
    for (int m = 0; m < bigremain; m++)
    array.SetAt(m, buf[m]);//数据转十六进制
    m_mscomm.put_Output((COleVariant)array);//发送数据
    Sleep(200);//短暂睡眠,可以不取
    delete buf;//回收
    array.RemoveAll();//关闭文件指针
    fclose(pSENDFILE);
    }
    }

    过程:上面的程序只是发送文件大概的过程,没有写得很详细,本人觉得思想是最重要的,代码写得来没有算法懂得来好。

       首先发送端发送文件大小,通过一定编码,如加入特殊包头,接收端解码,获得文件大小;

       判断文件是否大于30M,当大于30M,算得有多少个30M,多少个30M后还剩余多少,这里用除法和取余的方式。

       一个循环,发送多少个30M,这里有个小循环,为了不丢包,一次实际发送1k数据,发送30*1024次就发送完了30M。

        走到了这一步,后面就简单了,就不赘述了。

    第二步:接收文件

    variant = m_mscomm.get_Input();//从接收缓冲区获得的数据是variant结构体类型数据
    colesafearray = variant;

    long len, k;
    len = colesafearray.GetOneDimSize();//获取缓冲区长度
    for (k = 0; k < len; k++)
    {

      colesafearray.GetElement(&k, bigBuf + c);//把缓冲区的数据写到bigbuf里
      c++;
      count_all++;
      if (c == (30 * 1024 * 1024 - 1))//如果10M接收完成
        {
        fwrite(bigBuf, c, 1, pFILE);//写入10M数据到文件
        fflush(pFILE);//刷新
        c = 0;//重新接收
        }
      if (count_all == file_all)//如果已经接收到了发送文件大小个字节,代表发送和接收完成
        {
      fwrite(bigBuf, c, 1, pFILE);//写入10M数据到文件
      fclose(pFILE);//关闭接收文件指针
      delete bigBuf;//回收缓冲区内存
      c = 0;//重置c
      count_all = 0;//重置count_all
      AfxMessageBox(_T("receive succes"));//弹出对话框表示接收成功

        }
    }

    过程:事先从发送端发送目标文件字节数到接收端,接收端定义两个int c和count_all都等于0;

    接收端每处理一个字节的数据到缓冲区,c和count_all++:

        当c=30M时,写入到本地文件,然后c置0;

        当count_all=目标文件大小时,再写,回收内存。

    ps:

    1、现象:使虚拟串口连接com1和com2,在com1的oncomm事件开头加一个断点,当另一个串口助手连接com2时,程序在断点停止。
    猜想:当com1和com2连接的时候,两个串口会互相发出数据。由于在com1的设置中,当接收缓冲区有数据时会触发oncomm事件,故有此现象。应该是类似握手的数据,以判断两个串口是否连接在一起。

    2、现象:发送大于4k数据时,接收端都只能接收到4096字节即4k数据,小于4k时正常。
    猜想:发送和接收数据,是一个主动和被动的过程,假如com1发送1个G的数据到com2,此时如果com1全部把1G数据放在发送缓冲区,而com2可能由于一些状况比如操作系统缓慢,那就会造成发送的多而接收的少,结果就是掉包,所以我估计微软为了更好的的传送数据,就让我的com1一次只能发送4k数据。

    3、现象:网上很多人说,设置这句话put_RThreshold(1),表示接收缓冲区每有1个或者大于1个数据的时候就会触发oncomm事件(oncomm事件其实是一个线程),然而我进入oncomm事件调试,发现安全数组的GetOneDimSize总是返回发送文件的字节大小,而不是等于1或着大于1的不确定的数。
    猜想:由于计算机速度太快,com1的4k数据太快就到了com2缓冲区,com2的get_Input里自动装了全部4k数据,所以可以把串口通信想象成这样一个简单的过程:
    com1把4k数据慢慢放进发送缓冲区---->com1的发送缓冲区慢慢把数据传输到com2的接收缓冲区---->此时微软悄悄把接收缓冲区的数据放在get_Input里---->然后这是com2发现接收缓冲区里有数据,产生oncomm事件,此时所有数据都被微软放在了get_Input里---->用户在oncomm事件里处理数据。
    我估计这sb微软把上面com2接收缓冲区里的数据自动存起来,不需要用户再去处理,只需要用户在oncomm里处理数据。然而我因为网上的回答“put_RThreshold(1),表示接收缓冲区每有1个或者大于1个数据的时候就会触发oncomm事件”懵逼了两天,调试结果就是和自己想不一样,所以也就骂了两天微软。

    4、为何我电脑后台软件开多了,会有丢包的现象?

    猜想:由于发送端和接收端都是线程函数,当pc线程多了之后,就会占用我们这两个线程,发送端线程被占用之后还好,当接收端被占用了,而此时如果发送端还在发送,可能很短

    的时间,但是电脑速度极快,这一点点的时间差可能发送端已经走了几个发送1k数据的循环了,而此时接收端正被阻塞着,这是就丢包了。由于我这程序的设计思想,只要丢包了1个字节以上,那么最终导致丢(总字节数%30M)个字节。所以,必须保证不能丢一个字节数据。

    解决:把发送端线程函数线程优先级设置为最低。这样可以一定程度解决丢包问题,因为当pc需要阻塞线程的时候,会首先占用发送端的线程,而发送端线程被占用是不会影响丢包的。但是,如果线程多的抠脚,理论上接收端也可能被阻塞的,只要接收端被阻塞,发送端没有被阻塞,就一定会丢包。所以,理论上是不能完全解决这个问题的,除非你电脑内核比线程多哈哈哈哈。

  • 相关阅读:
    Elasticsearch系列---常见搜索方式与聚合分析
    Elasticsearch系列---Elasticsearch的基本概念及工作原理
    Elasticsearch系列---初识Elasticsearch
    记一次ES查询数据突然变为空的问题
    04、管道符、重定向与环境变量
    03、新手必须掌握的Linux命令
    02、安装Linux系统
    01、VM安装教程
    02、HTML 基础
    01、HTML 简介
  • 原文地址:https://www.cnblogs.com/judes/p/5943654.html
Copyright © 2020-2023  润新知