本文分析了飛鴿傳輸核心傳送過程。
- DWORD WINAPI TMainWin::SendFileThread(void *_sendFileObj)
- {
- SendFileObj *obj = (SendFileObj *)_sendFileObj;
- fd_set fds;
- fd_set *rfds = NULL, *wfds = &fds;
- timeval tv;
- int sock_ret;
- BOOL ret = FALSE, completeWait = FALSE;
- // 這里SendFileFunc根據(jù)command類型自動(dòng)選擇兩種函數(shù) : send file or send directory
- BOOL (TMainWin::*SendFileFunc)(SendFileObj *obj) =
- obj->command == IPMSG_GETDIRFILES ? TMainWin::SendDirFile : TMainWin::SendFile;
- FD_ZERO(&fds);
- FD_SET(obj->conInfo->sd, &fds);
- // 這里for條件引入了一個(gè)簡(jiǎn)單的超時(shí)機(jī)制
- // 正常情況下,只要文件未傳送完,循環(huán)不會(huì)退出
- for (int waitCnt=0; waitCnt < 180 && obj->hThread != NULL; waitCnt++)
- {
- tv.tv_sec = 1, tv.tv_usec = 0;
- // 這里select有什么用途呢? 對(duì)于select功能我還不是完全明白
- // 根據(jù)我的分析,這里主要是利用了select函數(shù)的等待功能
- // 如果sd描述符沒有就緒,則在select中最久等待1秒
- // 如此反復(fù)等待最多180次,也就是3分鐘,超過三分鐘后,for循環(huán)結(jié)束
- if ((sock_ret = ::select(obj->conInfo->sd + 1, rfds, wfds, NULL, &tv)) > 0)
- {
- // 套接字可用,清除等待
- waitCnt = 0;
- //下面的代碼是一個(gè)有限狀態(tài)機(jī)
- if (completeWait)
- {
- // 本分支在文件發(fā)送完后執(zhí)行
- if (::recv(obj->conInfo->sd, (char *)&ret, sizeof(ret), 0) >= 0)
- ret = TRUE;
- break;
- }
- else if ((mainWin->*SendFileFunc)(obj) != TRUE)
- {
- //本分支僅在發(fā)送出錯(cuò)時(shí)進(jìn)行
- break;
- }
- else if (obj->status == FS_COMPLETE)
- {
- // 本分支在發(fā)送完成后執(zhí)行
- completeWait = TRUE, rfds = &fds, wfds = NULL;
- if (obj->fileSize == 0) { ret = TRUE; break; }
- }
- }
- else if (sock_ret == 0) {
- // select超時(shí),重置fds
- FD_ZERO(&fds);
- FD_SET(obj->conInfo->sd, &fds);
- }
- else if (sock_ret == SOCKET_ERROR) {
- // select錯(cuò)誤,算了,離去吧~
- break;
- }
- }
- // 如果發(fā)送的是文件夾,還需要擦一下屁股
- if (obj->isDir)
- {
- mainWin->CloseSendFile(obj);
- while (--obj->dirCnt >= 0)
- ::FindClose(obj->hDir[obj->dirCnt]);
- }
- // ret是對(duì)方發(fā)回的返回值,告知發(fā)送方是否完成接收
- obj->status = ret ? FS_COMPLETE : FS_ERROR;
- // 發(fā)送TCPEVENT消息,關(guān)閉句柄
- // 消息處理流程: EventUser->TcpEvent->EndSendFile
- mainWin->PostMessage(WM_TCPEVENT, obj->conInfo->sd, FD_CLOSE);
- // 退出發(fā)送線程
- ::ExitThread(0);
- return 0;
- }
上面?zhèn)魉蛿?shù)據(jù)最重要的一句是:
else if ((mainWin->*SendFileFunc)(obj) != TRUE)
SendFileFunc的實(shí)際內(nèi)容是什么呢?由函數(shù)開始賦值的指針知道:
- BOOL TMainWin::SendFile(SendFileObj *obj)
- {
- if (obj == NULL || obj->hFile == INVALID_HANDLE_VALUE) //判斷文件句柄是否合法
- return FALSE;
- int size = 0;
- _int64 remain = obj->fileSize - obj->offset; //取得還需要傳遞的總字節(jié)數(shù)
- //傳數(shù)據(jù)
- if (remain > 0 && (size = ::send(obj->conInfo->sd, obj->mapAddr + (obj->offset % cfg->ViewMax), remain > cfg->TransMax ? cfg->TransMax : (int)remain, 0)) < 0)
- return FALSE;
- // 根據(jù)本次成功發(fā)送的數(shù)據(jù)量,調(diào)整offset
- obj->offset += size;
- // 如果offset等于文件大小了,那么設(shè)置obj狀態(tài)為完成
- // 由于存在傳文件夾模式和傳文件模式,所以狀態(tài)分情況設(shè)置
- if (obj->offset == obj->fileSize)
- obj->status = obj->command == IPMSG_GETDIRFILES ? FS_ENDFILE : FS_COMPLETE;
- else if ((obj->offset % cfg->ViewMax) == 0)//沒有完成,但是已經(jīng)傳送完成了本部分?jǐn)?shù)據(jù)映射,需要調(diào)整映射窗口
- {
- ::UnmapViewOfFile(obj->mapAddr); // 刪除舊映射
- remain = obj->fileSize - obj->offset; // 計(jì)算新的剩余量
- // 映射下一塊,一次8M ,如果只剩下最后一點(diǎn)了,則少于8M (remain)
- obj->mapAddr = (char *)::MapViewOfFile(obj->hMap, FILE_MAP_READ, (int)(obj->offset >> 32), (int)obj->offset, (int)(remain > cfg->ViewMax ? cfg->ViewMax : remain));
- }
- // 更新總消耗時(shí)間
- obj->conInfo->lastTick = ::GetTickCount();
- return TRUE;
- }
很多朋友向我要飛鴿帶注釋的源碼,實(shí)在很抱歉,我只注釋了這么多,其余的也沒有深入地看。如果你對(duì)帶注釋的源碼感興趣,不妨來這里看看:
http://code.google.com/p/ipigeon/這是我在GoogleCode上開的一個(gè)項(xiàng)目,大家一起來注釋飛鴿源碼吧!