使用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需要阻塞线程的时候,会首先占用发送端的线程,而发送端线程被占用是不会影响丢包的。但是,如果线程多的抠脚,理论上接收端也可能被阻塞的,只要接收端被阻塞,发送端没有被阻塞,就一定会丢包。所以,理论上是不能完全解决这个问题的,除非你电脑内核比线程多哈哈哈哈。

原文地址:https://www.cnblogs.com/judes/p/5943654.html