1
文件接收端在收到文件信息IPMSG_SENDMSG后
首先解析存储文件信息
此函数在TRecvDlg的构造函数中
ShareInfo *DecodeShareMsg(char *buf)
{
ShareInfo *shareInfo = new ShareInfo;
FileInfo *fileInfo = NULL;
char *tok, *p, *p2, *p3;
char *file = separate_token(buf, FILELIST_SEPARATOR, &p);
for (int cnt=0; file; cnt++, file=separate_token(NULL, FILELIST_SEPARATOR, &p))
{
ConvertShareMsgEscape(file); // "::" -> ';'
if ((tok = separate_token(file, ':', &p2)) == NULL)
break;
fileInfo = new FileInfo(atoi(tok));
if ((tok = separate_token(NULL, ':', &p2)) == NULL || strlen(tok) >= MAX_PATH)
break;
while ((p3 = strchr(tok, '?')) != NULL) // UNICODE 傑偱偺巄掕
*p3 = '_';
fileInfo->SetFname(tok);
if ((tok = separate_token(NULL, ':', &p2)) == NULL)
break;
fileInfo->SetSize(hex2ll(tok));
if ((tok = separate_token(NULL, ':', &p2)) == NULL)
break;
fileInfo->SetMtime(strtoul(tok, 0, 16));
if ((tok = separate_token(NULL, ':', &p2)))
{
fileInfo->SetAttr(strtoul(tok, 0, 16));
u_int attr_type = GET_MODE(fileInfo->Attr());
if (attr_type != IPMSG_FILE_DIR && attr_type != IPMSG_FILE_REGULAR)
{
delete fileInfo;
continue;
}
}
//IPMSG_FILE_REGULAR 普通文件
else fileInfo->SetAttr(IPMSG_FILE_REGULAR);
//防止内存溢出
if ((shareInfo->fileCnt % BIG_ALLOC) == 0)
shareInfo->fileInfo = (FileInfo **)realloc(shareInfo->fileInfo, (shareInfo->fileCnt + BIG_ALLOC) * sizeof(FileInfo *));
shareInfo->fileInfo[shareInfo->fileCnt++] = fileInfo;
fileInfo = NULL;
}
if (fileInfo) // 僨僐乕僪拞偵敳偗偨
delete fileInfo;
if (shareInfo->fileCnt <= 0)
{
delete shareInfo;
return NULL;
}
return shareInfo;
}
而后发起对文件发送端的连接
TRecvDlg::SaveFile(void)函数中调用 ConnectRecvFile()
实现对服务器端得连接
连接成功响应 FD_CONNECT
BOOL TRecvDlg::TcpEvent(int sd, LPARAM lParam)
{
if (WSAGETSELECTERROR(lParam) || fileObj == NULL || fileObj->conInfo == NULL)
return FALSE;
switch (LOWORD(lParam))
{
//connect done 当客户端同服务器端连成功响应 FD_CONNECT 以后开始文件传输
case FD_CONNECT:
StartRecvFile();
break;
case FD_CLOSE:
EndRecvFile();
break;
}
return TRUE;
}
2
BOOL TRecvDlg::StartRecvFile(void)
{
//客户端发送 IPMSG_GETFILEDATA
char buf[MAX_PATH], tcpbuf[MAX_BUF];
// msg.packetNo 包序号 fileObj->fileInfo->Id() 要传送文件在文件列表中序号
wsprintf(buf, fileObj->isDir ? "%x:%x:" : "%x:%x:%x:", msg.packetNo, fileObj->fileInfo->Id(), 0);
fileObj->conInfo->complete = TRUE;
msgMng->MakeMsg(tcpbuf, fileObj->isDir ? IPMSG_GETDIRFILES : IPMSG_GETFILEDATA, buf);
msgMng->ConnectDone(hWnd, fileObj->conInfo);
if (::send(fileObj->conInfo->sd, tcpbuf, strlen(tcpbuf), 0) < (int)strlen(tcpbuf))
return EndRecvFile(), FALSE;
fileObj->conInfo->startTick = fileObj->conInfo->lastTick = ::GetTickCount();
if (fileObj->startTick == 0)
fileObj->startTick = fileObj->conInfo->startTick;
//fileObj->isDir 表示是普通文件
if (fileObj->isDir == FALSE)
fileObj->curFileInfo = *fileObj->fileInfo;
fileObj->recvBuf = new char [cfg->TransMax];
// 0byte file 偩偗偼丄摿椺
if (fileObj->isDir == FALSE && fileObj->curFileInfo.Size() == 0)
{
if (OpenRecvFile() == TRUE)
{
CloseRecvFile(TRUE);
fileObj->status = FS_COMPLETE;
}
else fileObj->status = FS_ERROR;
PostMessage(WM_TCPEVENT, fileObj->conInfo->sd, FD_CLOSE);
return TRUE;
}
DWORD id; // 巊傢偢乮95宯偱 error 偵側傞偺傪杊偖偩偗乯
fileObj->hThread = ~0; // 旝柇側椞堟傪旔偗傞
// thread 撪偱偼 MT 懳墳偑昁梫側 crt 偼巊傢偢
if ((fileObj->hThread = ::CreateThread(NULL, 0, RecvFileThread, this, 0, &id)) == NULL)
{
EndRecvFile();
return FALSE;
}
return TRUE;
}
3
DWORD WINAPI TRecvDlg::RecvFileThread(void *_recvDlg)
{
TRecvDlg *recvDlg = (TRecvDlg *)_recvDlg;
RecvFileObj *fileObj = recvDlg->fileObj;
fd_set rfd;
tim tv;
int sock_ret;
BOOL (TRecvDlg::*RecvFileFunc)(void) =
fileObj->isDir ? TRecvDlg::RecvDirFile : TRecvDlg::RecvFile;
FD_ZERO(&rfd);
FD_SET(fileObj->conInfo->sd, &rfd);
//从连接服务器端成功到开始传输文件本程序的等待时间是 120s
for (int waitCnt=0; waitCnt < 120 && fileObj->hThread != NULL; waitCnt++)
{
tv.tv_sec = 1, tv.tv_usec = 0;
if ((sock_ret = ::select(fileObj->conInfo->sd + 1, &rfd, NULL, NULL, &tv)) > 0)
{
waitCnt = 0;
if ((recvDlg->*RecvFileFunc)() != TRUE)
break;
if (fileObj->status == FS_COMPLETE)
break;
}
else if (sock_ret == 0) {
FD_ZERO(&rfd);
FD_SET(fileObj->conInfo->sd, &rfd);
fileObj->conInfo->lastTick = ::GetTickCount();
recvDlg->PostMessage(WM_RECVDLG_FILEBUTTON, 0, 0);
}
else if (sock_ret == SOCKET_ERROR) {
break;
}
}
recvDlg->CloseRecvFile(fileObj->status == FS_COMPLETE ? TRUE : FALSE);
if (fileObj->status != FS_COMPLETE)
fileObj->status = FS_ERROR;
recvDlg->PostMessage(WM_TCPEVENT, fileObj->conInfo->sd, FD_CLOSE);
::ExitThread(0);
return 0;
}
BOOL TRecvDlg::CloseRecvFile(BOOL setAttr)
{
if (fileObj->hFile != INVALID_HANDLE_VALUE)
{
if (setAttr)
{
FILETIME ft;
UnixTime2FileTime(fileObj->curFileInfo.Mtime(), &ft);
#if 1 // 巄掕張抲乮protocol format 曄峏偺壜擻惈乯
if (fileObj->isDir || (fileObj->curFileInfo.Mtime() & 0xffffff00))
#endif
::SetFileTime(fileObj->hFile, NULL, &ft, &ft);
}
fileObj->totalTrans += fileObj->offset;
fileObj->totalFiles++;
fileObj->offset = fileObj->woffset = 0;
::CloseHandle(fileObj->hFile);
fileObj->hFile = INVALID_HANDLE_VALUE;
}
return TRUE;
}
***************/////////////////////////////////////////////////*************
BOOL TRecvDlg::RecvFile(void)
{
int wresid = (int)(fileObj->offset - fileObj->woffset);
_int64 remain = fileObj->curFileInfo.Size() - fileObj->offset;
int size = 0;
if (remain > cfg->TransMax - wresid)
remain = cfg->TransMax - wresid;
if ((size = ::recv(fileObj->conInfo->sd, fileObj->recvBuf + wresid, (int)remain, 0)) <= 0)
return FALSE;
if (fileObj->hFile == INVALID_HANDLE_VALUE)
if (OpenRecvFile() == FALSE)
return FALSE;
wresid += size;
if (fileObj->offset + size >= fileObj->curFileInfo.Size() || cfg->TransMax == wresid)
{
DWORD wsize;
if (::WriteFile(fileObj->hFile, fileObj->recvBuf, wresid, &wsize, 0) != TRUE || wresid != (int)wsize)
return MessageBox(WRITEFAIL_MSGSTR), FALSE;
fileObj->woffset += wresid;
}
fileObj->offset += size;
DWORD nowTick = ::GetTickCount();
if (nowTick - fileObj->conInfo->lastTick >= 1000)
{
fileObj->conInfo->lastTick = nowTick;
PostMessage(WM_RECVDLG_FILEBUTTON, 0, 0);
}
if (fileObj->offset >= fileObj->curFileInfo.Size())
fileObj->status = fileObj->isDir ? FS_ENDFILE : FS_COMPLETE;
return TRUE;
}