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 fileList, List 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 fileList; // 本次接收数据的所有节点 public Dictionary transHosts; public long beginTime; public int totalSize; private int finishHostNum; private int contentSlice; public FileTransMgr(string host) { localHost = host; totalSize = -1; fileList = new List(); transHosts = new Dictionary(); 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 transFiles, List 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; } } }