Files
SimpleRemoter/server/2015Remote/CDlgFileSend.cpp
2026-04-19 22:55:21 +02:00

252 lines
8.6 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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);
}
}