// CDlgFileSend.cpp: 实现文件 // #include "stdafx.h" #include "CDlgFileSend.h" #include "2015RemoteDlg.h" // For g_2015RemoteDlg->FindHost() // CDlgFileSend 对话框 IMPLEMENT_DYNAMIC(CDlgFileSend, CDialog) CDlgFileSend::CDlgFileSend(CMy2015RemoteDlg* pParent, Server* IOCPServer, CONTEXT_OBJECT* ContextObject, BOOL sendFile) : DialogBase(CDlgFileSend::IDD, pParent, IOCPServer, ContextObject, IDI_File), m_bIsSending(sendFile) { m_pParent = (CMy2015RemoteDlg*)pParent; } CDlgFileSend::~CDlgFileSend() { } void CDlgFileSend::DoDataExchange(CDataExchange* pDX) { __super::DoDataExchange(pDX); DDX_Control(pDX, IDC_PROGRESS_FILESEND, m_Progress); } BEGIN_MESSAGE_MAP(CDlgFileSend, CDialog) ON_WM_CLOSE() ON_WM_TIMER() ON_MESSAGE(WM_UPDATEFILEPROGRESS, &CDlgFileSend::OnUpdateFileProgress) ON_MESSAGE(WM_FINISHFILESEND, &CDlgFileSend::OnFinishFileSend) END_MESSAGE_MAP() // CDlgFileSend 消息处理程序 std::string GetPwdHash(); std::string GetHMAC(int offset); void RecvData(void* ptr) { FileChunkPacket* pkt = (FileChunkPacket*)ptr; } void CDlgFileSend::OnReceiveComplete(void) { LPBYTE szBuffer = m_ContextObject->InDeCompressedBuffer.GetBuffer(); unsigned len = m_ContextObject->InDeCompressedBuffer.GetBufferLen(); if (len == 0) return; BYTE cmd = szBuffer[0]; std::string hash = GetPwdHash(), hmac = GetHMAC(100); // V2 文件完成校验包 if (cmd == COMMAND_FILE_COMPLETE_V2) { if (len < sizeof(FileCompletePacketV2)) { Mprintf("[FileSend] FILE_COMPLETE_V2 包太短: %u < %zu\n", len, sizeof(FileCompletePacketV2)); return; } FileCompletePacketV2* completePkt = (FileCompletePacketV2*)szBuffer; // C2C 包:转发到目标客户端 if (completePkt->dstClientID != 0) { context* dstCtx = m_pParent->FindHost(completePkt->dstClientID); if (dstCtx) { dstCtx->Send2Client(szBuffer, len); Mprintf("[C2C] 转发校验包 -> 客户端 %llu\n", completePkt->dstClientID); } else { Mprintf("[C2C] 转发校验包失败: 目标客户端 %llu 不存在\n", completePkt->dstClientID); } return; } // 目标是服务端,本地校验 bool verifyOk = HandleFileCompleteV2((const char*)szBuffer, len, 0); Mprintf("[FileSend] 文件校验%s\n", verifyOk ? "通过" : "失败"); return; } if (cmd == COMMAND_SEND_FILE_V2) { // V2 协议 FileChunkPacketV2* chunk = (FileChunkPacketV2*)szBuffer; // C2C 包:转发到目标客户端,同时更新进度 if (chunk->dstClientID != 0) { // 如果已标记目标离线,直接忽略后续数据 if (m_bTargetOffline) { return; } // 转发到目标客户端 context* dstCtx = m_pParent->FindHost(chunk->dstClientID); if (dstCtx) { dstCtx->Send2Client(szBuffer, len); double percent = chunk->fileSize ? 100.0 * (chunk->offset + chunk->dataLength) / chunk->fileSize : 100.0; Mprintf("[C2C] 转发 %d/%d: %.1f%% (%llu/%llu)\n", 1 + chunk->fileIndex, chunk->totalFiles, percent, chunk->offset + chunk->dataLength, chunk->fileSize); } else { Mprintf("[C2C] CDlgFileSend 转发失败: 目标客户端 %llu 不存在,发送取消包\n", chunk->dstClientID); m_bTargetOffline = TRUE; // 发送取消包给发送方 FileResumePacketV2 cancelPkt = {}; cancelPkt.cmd = COMMAND_FILE_RESUME; cancelPkt.transferID = chunk->transferID; cancelPkt.srcClientID = 0; // 来自服务端 cancelPkt.dstClientID = chunk->srcClientID; // 回复给发送方 cancelPkt.fileIndex = chunk->fileIndex; cancelPkt.fileSize = chunk->fileSize; cancelPkt.receivedBytes = chunk->offset; cancelPkt.flags = FFV2_CANCEL; cancelPkt.rangeCount = 0; m_ContextObject->Send2Client((LPBYTE)&cancelPkt, sizeof(cancelPkt)); // 关闭此连接 FinishFileSend(FALSE); return; } BYTE* name = szBuffer + sizeof(FileChunkPacketV2); UpdateProgress(CString((char*)name, (int)chunk->nameLength), FileProgressInfo(chunk)); return; } // 目标是服务端,本地接收 int n = RecvFileChunkV2((char*)szBuffer, len, nullptr, nullptr, hash, hmac, 0); if (n) { Mprintf("RecvFileChunkV2 failed: %d\n", n); } BYTE* name = szBuffer + sizeof(FileChunkPacketV2); UpdateProgress(CString((char*)name, (int)chunk->nameLength), FileProgressInfo(chunk)); } else { // V1 协议 CONNECT_ADDRESS addr = { 0 }; memcpy(addr.pwdHash, hash.c_str(), min(hash.length(), sizeof(addr.pwdHash))); int n = RecvFileChunk((char*)szBuffer, len, &addr, RecvData, hash, hmac); if (n) { Mprintf("RecvFileChunk failed: %d. hash: %s, hmac: %s\n", n, hash.c_str(), hmac.c_str()); } FileChunkPacket* chunk = (FileChunkPacket*)szBuffer; BYTE* name = szBuffer + sizeof(FileChunkPacket); UpdateProgress(CString((char*)name, chunk->nameLength), FileProgressInfo(chunk)); } } void CDlgFileSend::UpdateProgress(CString file, const FileProgressInfo& info) { if (!GetSafeHwnd()) return; PostMessageA(WM_UPDATEFILEPROGRESS, (WPARAM)new CString(file), (LPARAM)new FileProgressInfo(info)); } void CDlgFileSend::FinishFileSend(BOOL succeed) { if (!GetSafeHwnd()) return; PostMessageA(WM_FINISHFILESEND, NULL, (LPARAM)succeed); } LRESULT CDlgFileSend::OnUpdateFileProgress(WPARAM wParam, LPARAM lParam) { CString* pFile = (CString*)wParam; FileProgressInfo* pInfo = (FileProgressInfo*)lParam; CString status; double percent = pInfo->fileSize ? (pInfo->offset + pInfo->dataLength) * 100. / pInfo->fileSize : 100.; m_bIsSending ? status.FormatL("发送文件(%d/%d): %.2f%%", 1 + pInfo->fileIndex, pInfo->totalFiles, percent): status.FormatL("接收文件(%d/%d): %.2f%%", 1 + pInfo->fileIndex, pInfo->totalFiles, percent); SetDlgItemText(IDC_STATIC_CURRENTINDEX, status); SetDlgItemText(IDC_STATIC_CURRENT_FILE, *pFile); m_Progress.SetPos((int)percent); // 只在第一次显示时置顶,后续更新不抢焦点 if (!IsWindowVisible()) { ShowWindow(SW_SHOW); SetWindowPos(&wndTopMost, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); } // 只有最后一个文件完成时才设置自动关闭定时器 // 避免多文件传输时第一个文件完成后就关闭对话框 bool isLastFile = (pInfo->fileIndex + 1 >= pInfo->totalFiles); if (percent >= 100. && isLastFile) { SetTimer(1, 3000, NULL); } else { // 传输新文件时取消之前的定时器 KillTimer(1); } delete pInfo; delete pFile; return 0; } LRESULT CDlgFileSend::OnFinishFileSend(WPARAM wParam, LPARAM lParam) { BOOL success = (BOOL)lParam; m_bIsSending ? SetDlgItemText(IDC_STATIC_CURRENTINDEX, success ? _TR("文件发送完成") : _TR("文件发送失败")): SetDlgItemText(IDC_STATIC_CURRENTINDEX, success ? _TR("文件接收完成") : _TR("文件接收失败")); if (success) m_Progress.SetPos(100); // 完成后取消置顶,恢复普通窗口 SetWindowPos(&wndNoTopMost, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); ShowWindow(SW_SHOW); SetTimer(1, 3000, NULL); // 3秒后自动关闭 return 0; } BOOL CDlgFileSend::OnInitDialog() { DialogBase::OnInitDialog(); // 多语言翻译 - Static控件 SetDlgItemText(IDC_STATIC_CURRENTINDEX, _TR("发送文件(999/999):")); SetIcon(m_hIcon, FALSE); SetWindowText(m_bIsSending ? _TR("发送文件") : _TR("接收文件")); CMenu* pSysMenu = GetSystemMenu(FALSE); if (pSysMenu != nullptr) { pSysMenu->EnableMenuItem(SC_CLOSE, MF_BYCOMMAND | MF_GRAYED); } return TRUE; } void CDlgFileSend::OnClose() { // V2传输使用主连接,关闭对话框时不应断开连接 if (!m_bKeepConnection) { CancelIO(); } // 等待数据处理完毕 if (IsProcessing()) { ShowWindow(SW_HIDE); return; } DialogBase::OnClose(); } void CDlgFileSend::OnTimer(UINT_PTR nIDEvent) { if (nIDEvent == 1) { KillTimer(1); PostMessageA(WM_CLOSE, 0, 0); } }