Chinaunix首页 | 论坛 | 博客
  • 博客访问: 142972
  • 博文数量: 49
  • 博客积分: 2010
  • 博客等级: 大尉
  • 技术积分: 576
  • 用 户 组: 普通用户
  • 注册时间: 2009-09-17 11:27
文章分类

全部博文(49)

文章存档

2011年(1)

2010年(15)

2009年(33)

我的朋友

分类: WINDOWS

2010-03-25 14:48:21

2. DWORD WINAPI TMainWin::SendFileThread(void *_sendFileObj)
   3. {
   4. SendFileObj *obj = (SendFileObj *)_sendFileObj;
   5. fd_set fds;
   6. fd_set *rfds = NULL, *wfds = &fds;
   7. timeval tv;
   8. int sock_ret;
   9. BOOL ret = FALSE, completeWait = FALSE;
  10. // 这里SendFileFunc根据command类型自动选择两种函数 : send file or send directory

  11. BOOL (TMainWin::*SendFileFunc)(SendFileObj *obj) =
  12. obj->command == IPMSG_GETDIRFILES ? TMainWin::SendDirFile : TMainWin::SendFile;
  13.
  14. FD_ZERO(&fds);
  15. FD_SET(obj->conInfo->sd, &fds);
  16. // 这里for条件引入了一个简单的超时机制

  17. // 正常情况下,只要文件未传送完,循环不会退出

  18. for (int waitCnt=0; waitCnt < 180 && obj->hThread != NULL; waitCnt++)
  19. {
  20. tv.tv_sec = 1, tv.tv_usec = 0;
  21. // 这里select有什么用途呢? 对于select功能我还不是完全明白

  22. // 根据我的分析,这里主要是利用了select函数的等待功能

  23. // 如果sd描述符没有就绪,则在select中最久等待1秒

  24. // 如此反复等待最多180次,也就是3分钟,超过三分钟后,for循环结束

  25. if ((sock_ret = ::select(obj->conInfo->sd + 1, rfds, wfds, NULL, &tv)) > 0)
  26. {
  27. // 套接字可用,清除等待

  28. waitCnt = 0;
  29.
  30. //下面的代码是一个有限状态机

  31. /*
  32. * 控制变量 completeWait, obj->status
  33. * 状态机迁移过程(一般状态下):
  34. * (1) if ((mainWin->*SendFileFunc)(obj) != TRUE)
  35. * (2) if (obj->status == FS_COMPLETE)
  36. * (3) if (completeWait)
  37. *
  38. * 1首先被反复执行,直到文件发送完毕。
  39. * 在没有错误的情况下,1总是等价于if(false)
  40. * 于是其后的2每次都会被执行,判断是否完成
  41. * 一旦完成,completeWait被设置为True,
  42. * 在下一次循环里,进入3
  43. * 在3的语句体内执行recv函数,等待对方应答
  44. *
  45. */

  46. if (completeWait)
  47. {
  48. // 本分支在文件发送完后执行

  49. if (::recv(obj->conInfo->sd, (char *)&ret, sizeof(ret), 0) >= 0)
  50. ret = TRUE;
  51. break;
  52. }
  53. else if ((mainWin->*SendFileFunc)(obj) != TRUE)
  54. {
  55. //本分支仅在发送出错时进行

  56. break;
  57. }
  58. else if (obj->status == FS_COMPLETE)
  59. {
  60. // 本分支在发送完成后执行

  61. completeWait = TRUE, rfds = &fds, wfds = NULL;
  62. if (obj->fileSize == 0) { ret = TRUE; break; }
  63. }
  64. }
  65. else if (sock_ret == 0) {
  66. // select超时,重置fds

  67. FD_ZERO(&fds);
  68. FD_SET(obj->conInfo->sd, &fds);
  69. }
  70. else if (sock_ret == SOCKET_ERROR) {
  71. // select错误,算了,离去吧~

  72. break;
  73. }
  74. }
  75.
  76. // 如果发送的是文件夹,还需要擦一下屁股

  77. if (obj->isDir)
  78. {
  79. mainWin->CloseSendFile(obj);
  80. while (--obj->dirCnt >= 0)
  81. ::FindClose(obj->hDir[obj->dirCnt]);
  82. }
  83.
  84. // ret是对方发回的返回值,告知发送方是否完成接收

  85. obj->status = ret ? FS_COMPLETE : FS_ERROR;
  86. // 发送TCPEVENT消息,关闭句柄

  87. // 消息处理流程: EventUser->TcpEvent->EndSendFile

  88. mainWin->PostMessage(WM_TCPEVENT, obj->conInfo->sd, FD_CLOSE);
  89. // 退出发送线程

  90. ::ExitThread(0);
  91. return 0;
  92. }
93.

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

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

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

   1.
   2. BOOL TMainWin::SendFile(SendFileObj *obj)
   3. {
   4. if (obj == NULL || obj->hFile == INVALID_HANDLE_VALUE) //判断文件句柄是否合法

   5. return FALSE;
   6.
   7. int size = 0;
   8. _int64 remain = obj->fileSize - obj->offset; //取得还需要传递的总字节数

   9.
  10. //传数据

  11. if (remain > 0 && (size = ::send(obj->conInfo->sd, obj->mapAddr + (obj->offset % cfg->ViewMax), remain > cfg->TransMax ? cfg->TransMax : (int)remain, 0)) < 0)
  12. return FALSE;
  13.
  14. // 根据本次成功发送的数据量,调整offset

  15. obj->offset += size;
  16.
  17. // 如果offset等于文件大小了,那么设置obj状态为完成

  18. // 由于存在传文件夹模式和传文件模式,所以状态分情况设置

  19. if (obj->offset == obj->fileSize)
  20. obj->status = obj->command == IPMSG_GETDIRFILES ? FS_ENDFILE : FS_COMPLETE;
  21. else if ((obj->offset % cfg->ViewMax) == 0)//没有完成,但是已经传送完成了本部分数据映射,需要调整映射窗口

  22. {
  23. ::UnmapViewOfFile(obj->mapAddr); // 删除旧映射

  24. remain = obj->fileSize - obj->offset; // 计算新的剩余量

  25. // 映射下一块,一次8M ,如果只剩下最后一点了,则少于8M (remain)

  26. obj->mapAddr = (char *)::MapViewOfFile(obj->hMap, FILE_MAP_READ, (int)(obj->offset >> 32), (int)obj->offset, (int)(remain > cfg->ViewMax ? cfg->ViewMax : remain));
  27. }
  28. // 更新总消耗时间

  29. obj->conInfo->lastTick = ::GetTickCount();
  30.
  31. return TRUE;
  32. }
33.


阅读(1428) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~