IPMSG飞鸽传书7——文件发送源码分析

  1. DWORD WINAPI TMainWin::SendFileThread( void *_sendFileObj)
  2. {
  3.      SendFileObj *obj = (SendFileObj *)_sendFileObj;
  4.      fd_set       fds;
  5.      fd_set       *rfds = NULL, *wfds = &fds;
  6.      timeval      tv;
  7.      int           sock_ret;
  8.      BOOL          ret = FALSE, completeWait = FALSE;
  9.      // 这里SendFileFunc根据command类型自动选择两种函数 :
  10.     // send file or send directory
  11.      BOOL          (TMainWin::*SendFileFunc)(SendFileObj *obj) =
  12.                  obj->command == IPMSG_GETDIRFILES ? TMainWin::SendDirFile : TMainWin::SendFile;
  13.      FD_ZERO(&fds);
  14.      FD_SET(obj->conInfo->sd, &fds);
  15.      // 这里for条件引入了一个简单的超时机制
  16.      // 正常情况下,只要文件未传送完,循环不会退出
  17.      for ( int waitCnt=0; waitCnt < 180 && obj->hThread != NULL; waitCnt++)
  18.      {
  19.          tv.tv_sec = 1, tv.tv_usec = 0;
  20.          // 这里select有什么用途呢? 对于select功能我还不是完全明白
  21.          // 根据我的分析,这里主要是利用了select函数的等待功能
  22.          // 如果sd描述符没有就绪,则在select中最久等待1秒
  23.          // 如此反复等待最多180次,也就是3分钟,超过三分钟后,for循环结束
  24.          if ((sock_ret = ::select(obj->conInfo->sd + 1, rfds, wfds, NULL, &tv)) > 0)
  25.          {
  26.              // 套接字可用,清除等待
  27.              waitCnt = 0;
  28.              //下面的代码是一个有限状态机
  29.             
  30.              if (completeWait)
  31.              {
  32.                  // 本分支在文件发送完后执行
  33.                  if (::recv(obj->conInfo->sd, ( char *)&ret, sizeof (ret), 0) >= 0)
  34.                      ret = TRUE;
  35.                  break ;
  36.              }
  37.              else if ((mainWin->*SendFileFunc)(obj) != TRUE)
  38.              {
  39.                  //本分支仅在发送出错时进行
  40.                  break ;  
  41.              }
  42.              else if (obj->status == FS_COMPLETE)
  43.              {
  44.                  // 本分支在发送完成后执行
  45.                  completeWait = TRUE, rfds = &fds, wfds = NULL;
  46.                  if (obj->fileSize == 0) { ret = TRUE; break ; }
  47.              }
  48.          }
  49.          else if (sock_ret == 0) {
  50.              // select超时,重置fds
  51.              FD_ZERO(&fds);
  52.              FD_SET(obj->conInfo->sd, &fds);
  53.          }
  54.          else if (sock_ret == SOCKET_ERROR) {
  55.              // select错误,算了,离去吧~
  56.              break ;
  57.          }
  58.      }
  59.      // 如果发送的是文件夹,还需要擦一下屁股
  60.      if (obj->isDir)
  61.      {
  62.          mainWin->CloseSendFile(obj);
  63.          while (--obj->dirCnt >= 0)
  64.              ::FindClose(obj->hDir[obj->dirCnt]);
  65.      }
  66.      // ret是对方发回的返回值,告知发送方是否完成接收
  67.      obj->status = ret ? FS_COMPLETE : FS_ERROR;
  68.      // 发送TCPEVENT消息,关闭句柄
  69.      // 消息处理流程: EventUser->TcpEvent->EndSendFile
  70.      mainWin->PostMessage(WM_TCPEVENT, obj->conInfo->sd, FD_CLOSE);
  71.      // 退出发送线程
  72.      ::ExitThread(0);
  73.      return    0;
  74. }

上面传送数据最重要的一句是:

             else if ((mainWin->*SendFileFunc)(obj) != TRUE)

SendFileFunc的实际内容是什么呢?由函数开始赋值的指针知道:

  1. BOOL TMainWin::SendFile(SendFileObj *obj)
  2. {
  3.      if (obj == NULL || obj->hFile == INVALID_HANDLE_VALUE)   //判断文件句柄是否合法
  4.          return    FALSE;
  5.      int       size = 0;
  6.      _int64   remain = obj->fileSize - obj->offset;    //取得还需要传递的总字节数
  7.      //传数据
  8.      if (remain > 0 && (size = ::send(obj->conInfo->sd, obj->mapAddr + (obj->offset % cfg->ViewMax), remain > cfg->TransMax ? cfg->TransMax : ( int )remain, 0)) < 0)
  9.          return    FALSE;
  10.      // 根据本次成功发送的数据量,调整offset
  11.      obj->offset += size;
  12.      // 如果offset等于文件大小了,那么设置obj状态为完成
  13.      // 由于存在传文件夹模式和传文件模式,所以状态分情况设置
  14.      if (obj->offset == obj->fileSize)
  15.          obj->status = obj->command == IPMSG_GETDIRFILES ? FS_ENDFILE : FS_COMPLETE;
  16.      else if ((obj->offset % cfg->ViewMax) == 0) //没有完成,但是已经传送完成了本部分数据映射,需要调整映射窗口
  17.      {
  18.          ::UnmapViewOfFile(obj->mapAddr);     // 删除旧映射
  19.          remain = obj->fileSize - obj->offset;    // 计算新的剩余量
  20.          // 映射下一块,一次8M ,如果只剩下最后一点了,则少于8M (remain)
  21.          obj->mapAddr = ( char *)::MapViewOfFile(obj->hMap, FILE_MAP_READ, ( int )(obj->offset >> 32), ( int )obj->offset, ( int )(remain > cfg->ViewMax ? cfg->ViewMax : remain));
  22.      }
  23.      // 更新总消耗时间
  24.      obj->conInfo->lastTick = ::GetTickCount();
  25.      return    TRUE;
  26. }
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值