You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

261 lines
8.4 KiB

1 month ago
using System;
using System.Collections.Generic;
using System.Linq;
using Google.Protobuf;
using Google.Protobuf.WellKnownTypes;
using Sog;
namespace FileTransDataObject
{
// 一次传输所需信息
public class FileTransMgr
{
public static readonly int TIME_OUT = 30000;
// 文件传输时的最大长度, 搞小一点,免得底层分包,麻烦
public static readonly int CONTENT_SLICE_SIZE = 50 * 1024;
// 请求文件内容
public delegate void SendTransFileNotify(long transSeq, List<FileData> fileList, List<string> hostList);
public SendTransFileNotify _SendTransFileNotify;
public int TotalFile => fileList.Count;
public int TotalHost => transHosts.Count;
public int TotalSize
{
get
{
if (totalSize == -1)
{
totalSize = fileList.Sum(f => f.FileSize);
}
return totalSize;
}
}
// 负责数据传输的host
private string localHost;
// 本次传送的序列号, 用于快速判断是否同一次传送的Msg, 只用判断开始结束即可
public long transSeq;
// 本次请求传输的所有文件
public List<FileData> fileList;
// 本次接收数据的所有节点
public Dictionary<string, FileTransNode> transHosts;
public long beginTime;
public int totalSize;
private int finishHostNum;
private int contentSlice;
public FileTransMgr(string host)
{
localHost = host;
totalSize = -1;
fileList = new List<FileData>();
transHosts = new Dictionary<string, FileTransNode>();
contentSlice = CONTENT_SLICE_SIZE;
}
public void Clear()
{
fileList.Clear();
transHosts.Clear();
transSeq = 0;
beginTime = 0;
finishHostNum = 0;
totalSize = -1;
}
public bool IsFinish()
{
if (finishHostNum == transHosts.Count)
{
return true;
}
finishHostNum = transHosts.Values.Count(h => h.state != 0);
return finishHostNum == transHosts.Count;
}
public void SetContentSlice(int size)
{
contentSlice = size;
}
// transFiles: 待传输文件列表
// hostList: 接收文件的节点列表
public int BeginTrans(List<FileData> transFiles, List<string> hostList)
{
if (transFiles.Count == 0 || hostList.Count == 0)
{
TraceLog.Error("FileTransMgr.BeginTrans fail, file num {0} host num {1}", transFiles.Count, hostList.Count);
return -1;
}
Clear();
foreach (string hostName in hostList)
{
if (! string.IsNullOrEmpty(hostName) && ! transHosts.ContainsKey(hostName))
{
transHosts.Add(hostName, new FileTransNode {receiverHost = hostName});
}
}
if (transHosts.Count == 0)
{
TraceLog.Error("FileTransMgr.BeginTrans fail, host count is 0");
return -1;
}
fileList.AddRange(transFiles);
beginTime = AppTime.ServerAppTime.GetTime();
transSeq = beginTime;
foreach (FileTransNode transNode in transHosts.Values)
{
transNode.lastRecvMsgTime = beginTime;
}
TraceLog.Debug("FileTransMgr.BeginTrans file count {0} total size {1} host count {2}", TotalFile, TotalSize, TotalHost);
_SendTransFileNotify(transSeq, transFiles, hostList);
return 0;
}
public void Tick(long nowMs)
{
foreach (FileTransNode transNode in transHosts.Values)
{
if (transNode.state == 0 && transNode.lastRecvMsgTime > 0
&& nowMs >= transNode.lastRecvMsgTime + TIME_OUT)
{
transNode.state = -1;
}
}
}
public FileTransNode GetTransNode(string name)
{
transHosts.TryGetValue(name, out FileTransNode node);
return node;
}
// 20001; // 文件传送结束
// 20002; // 文件不存在
// 20003; // 偏移值不合法
public SMFileContentRes OnFileContentReq(SMFileContentReq req)
{
var res = new SMFileContentRes
{
Ret = 20001, FileName = req.FileName, FileMd5 = req.FileMd5,
ContentOffset = req.ContentOffset, TransSeq = req.TransSeq, HostName = localHost
};
var transNode = GetTransNode(req.HostName);
if (transNode == null)
{
TraceLog.Error("FileTransMgr.OnFileContentReq agent {0} not exist", req.HostName);
return res;
}
transNode.lastRecvMsgTime = AppTime.ServerAppTime.GetTime();
// 不是同一轮传输请求
if (transSeq != req.TransSeq)
{
TraceLog.Error("FileRecvMgr.OnFileContentReq transSeq local {0} req {1} not equal"
, transSeq, req.TransSeq);
return res;
}
// 文件不存在
var file = fileList.First(f => f.fileMd5 == req.FileMd5 && f.fileName == req.FileName);
if (file == null)
{
TraceLog.Error("FileTransMgr.OnFileContentReq file {0} md5 {1} not exist", req.FileName, req.FileMd5);
res.Ret = 20002;
return res;
}
// 偏移值不合法
if (req.ContentOffset < 0 || req.ContentOffset >= file.FileSize)
{
TraceLog.Error("FileTransMgr.OnFileContentReq invalid offset, file {0} md5 {1} size {2} req {3}",
file.fileName, file.fileMd5, file.FileSize, req.ContentOffset);
res.Ret = 20003;
return res;
}
int length = Math.Min(contentSlice, file.FileSize - req.ContentOffset);
TraceLog.Debug("FileTransMgr.OnFileContentReq file {0} md5 {1} size {2} offset {3} length {4}",
file.fileName, file.fileMd5, file.FileSize, req.ContentOffset, length);
res.Ret = 0;
res.Content = ByteString.CopyFrom(file.fileData, req.ContentOffset, length);
return res;
}
// 收到文件接收结果通知
public void OnFileRecvStateReq(SMFileRecvStateReq req)
{
// 传输序列号不一致? 应该不可能出现, 传输由TransNode发起, 如果不一致先回复当前RecvNode成功
// 同时不做任何处理, 等TransNode超时或者收到来自正确TransSeq的结果通知
if (transSeq != req.TransSeq)
{
TraceLog.Error("FileTransMgr.OnFileRecvStateReq local seq {0} host {1} seq {2} not match"
, transSeq, req.HostName, req.TransSeq);
return;
}
var transNode = GetTransNode(req.HostName);
if (transNode == null)
{
TraceLog.Error("FileTransMgr.OnFileRecvStateReq transNode {0} not exist", req.HostName);
return;
}
transNode.lastRecvMsgTime = AppTime.ServerAppTime.GetTime();
if (transNode.state != 0)
{
TraceLog.Error("FileTransMgr.OnFileRecvStateReq transNode {0} finish, state {1}", req.HostName, transNode.state);
return;
}
transNode.fileState.AddRange(req.FileList);
int succNum = 0;
foreach (FileData file in fileList)
{
var recvState = req.FileList.FirstOrDefault(f => f.FileMd5 == file.fileMd5 && f.FileName == file.fileName);
// 接收成功
if (recvState != null && recvState.RecvState == 1)
{
succNum++;
}
else
{
TraceLog.Error("FileTransMgr.OnFileRecvStateReq file {0} md5 {1} fail, host {2}",
file.fileName, file.fileMd5, req.HostName);
}
}
transNode.state = succNum == fileList.Count ? 1 : -1;
}
}
}