protected override Message GetNextMessageImpl() { Message message = null; if (listSequenceSendable == null) { var tmp = new List <Item>(); //因为3.5不支持协变,所以需要这个转化。 GetChildren().ForEach(item => tmp.Add(item)); listSequenceSendable = new ListSequencable(ConversationID, tmp); listSequenceSendable.Name = this.AbsolutePath; listSequenceSendable.PostCompleted += o => TransferState = TransferState.PostCompleted; listSequenceSendable.Errored += o => TransferState = TransferState.Error; SendItemsMessage sendItemsMessage = new SendItemsMessage(tmp); //!!!!!!!!!注意:这里看起来很奇怪。原因是虽然添加到List中,但希望他们的Parent还是这个DirItem. //TODO 重构 如果让DirItem实现ListSequenceSendable呢? GetChildren().ForEach(item => { item.Parent = this; if (item is FileItem) { Conversation?.InternalWormHole(item); } }); sendItemsMessage.ParentItemID = ID; sendItemsMessage.ConversationID = ConversationID; message = sendItemsMessage; } else { if (!createFolderMessageSent) { FileDataMessage fm = new FileDataMessage() { ItemID = ID, Length = 0, Data = new byte[0] }; fm.ConversationID = ConversationID; message = fm; //TODO 注意 如果这个文件夹是空的,这个会导致状态变化。只有长度为0,才能允许调用Progress。否则的话,会导致重复计算进度。 ((IProgressable)this).Progress(0); createFolderMessageSent = true; } else { //因为这个类监听了list的状态变化,当list状态变化时,这个类的状态也会变化。 message = listSequenceSendable.GetNextMessage(); } } return(message); }
internal void CheckFileDataMessage(FileDataMessage message, FileItem fileItem) { var logger = Logger; if (message.Length < FileDataMessage.BUFFER_SIZE) { if (message.Length + fileItem.TransferredLength != fileItem.Length) { var msg = "可能的接收文件错误。message.length=" + message.Length + " data.length=" + message.Data.Length + " file.length=" + fileItem.Length + " fileItem.TransferredLength=" + fileItem.TransferredLength + " file.name" + fileItem.Name; //ShowMessage(msg);文件读取的时候,存不在在读取到的数据,在中间过程中,与给定长度不一致的时候? logger.Debug(msg); } } }
protected IMessage ProcessData(Connection connection, BinaryReader reader) { IMessage result = new NullMessage(); IMessage currentMessage = MessageFactory.FromStream(reader); switch (currentMessage.MessageId) { case MessageIdentifier.FileChanged: { if (ClientHasBeenValidated == true) { FileChangedMessage message = currentMessage as FileChangedMessage; string filePath = Path.Join(connection.LocalSyncPath, message.FileData.Path); FileInfo localFile = new FileInfo(filePath); //we only need the file data when a file on the client was changed and that file is newer than our local copy if (message.FileData.OperationType == WatcherChangeTypes.Changed || message.FileData.OperationType == WatcherChangeTypes.Created) { if (localFile.Exists == false || localFile.LastWriteTimeUtc < message.FileData.LastWriteTimeUtc) { FileMetaData = message.FileData; result = new FileRequestMessage(message.FileData); } else { result = new NullMessage(); } } else { if (message.FileData.OperationType == WatcherChangeTypes.Renamed) { string oldFilePath = Path.Join(connection.LocalSyncPath, message.FileData.OldPath); if (File.Exists(oldFilePath)) { File.Move(oldFilePath, filePath); } } if (message.FileData.OperationType == WatcherChangeTypes.Deleted) { if (File.Exists(filePath)) { File.Delete(filePath); } } result = new NullMessage(); } } break; } case MessageIdentifier.FileData: { if (ClientHasBeenValidated == true && FileMetaData != null) { FileDataMessage message = currentMessage as FileDataMessage; message.FilePath = Path.Join(connection.LocalSyncPath, FileMetaData.Path); ReceiveBegin(this, new ServerEventArgs() { FileData = FileMetaData, FullLocalPath = message.FilePath }); try { message.FromBinaryStream(reader); //change last write to match client file File.SetLastWriteTimeUtc(message.FilePath, FileMetaData.LastWriteTimeUtc); File.SetLastAccessTimeUtc(message.FilePath, FileMetaData.LastAccessTimeUtc); File.SetCreationTimeUtc(message.FilePath, FileMetaData.CreateTimeUtc); ReceiveEnd(this, new ServerEventArgs() { FileData = FileMetaData, FullLocalPath = message.FilePath, Success = true }); } catch (Exception ex) { Logger.Log(LogPriority.High, "Server #{0} error writing file: {1}", ServerId, message.FilePath); ReceiveEnd(this, new ServerEventArgs() { FileData = FileMetaData, FullLocalPath = message.FilePath, Success = false }); } } break; } case MessageIdentifier.Verification: { VerificationMessage message = currentMessage as VerificationMessage; if (message != null) { if (message.Key == connection.LocalAccessKey) { result = new VerificationMessage(connection.RemoteAccessKey, NetworkResponse.Valid); //store validation result for later use ClientHasBeenValidated = true; } } break; } } return(result); }
public ClientSendResult SendFile(FileMetaData data) { ClientSendResult args = new ClientSendResult(); args.FileData = data; TcpClient client = new TcpClient(_connection.Address, _connection.Port); #if DEBUG == false //timeouts don't work well when you're debugging client.SendTimeout = 5000; #endif BufferedStream stream = new BufferedStream(client.GetStream()); BinaryReader reader = new BinaryReader(stream); BinaryWriter writer = new BinaryWriter(stream); try { //introduce ourselves if (Handshake(reader, writer) == true) { args.WasSuccessful = true; //tell server that we would like to send them a file IMessage message = new FileChangedMessage(data); writer.Write(message.ToBytes()); //see if server wants the file message = MessageFactory.FromStream(reader); //server says they want the whole load if (message.MessageId == MessageIdentifier.FileRequest) { message = new FileDataMessage(); writer.Write(message.ToBytes()); string basePath = _connection.LocalSyncPath; string localFilePath = Path.Join(basePath, data.Path); if (File.Exists(localFilePath)) { FileInfo toSend = new FileInfo(localFilePath); using (var fileReader = new BinaryReader(File.OpenRead(localFilePath))) { byte[] buffer; writer.Write(IPAddress.HostToNetworkOrder(toSend.Length)); while ((buffer = fileReader.ReadBytes(BUFFER_SIZE)).Length > 0) { writer.Write(buffer); } writer.Flush(); } } } } } catch (EndOfStreamException ex) { //end of stream exception doesn't necessairly mean that the transfer was not successful so separate out //from generic exception } catch (Exception ex) { args.WasSuccessful = false; _logger.Log("Error: {0}", ex.Message); } finally { client.Close(); } return(args); }
//TODO 在Send结束后,需要启动一个等待动作,等待对方回复确认消息。 protected override Message GetNextMessageImpl() { Debug.Assert(TransferState != TransferState.Error, @"Item状态已经出错,为什么还到了这里呢?"); //上次的包!=null, 而且已经PostComplted if (IsPostCompleted) { return(null); } FileDataMessage message = null; if (filestream == null) { try { if (new FileInfo(AbsolutePath).Exists) { filestream = new FileStream(AbsolutePath, FileMode.Open, FileAccess.Read, FileShare.Read); } } catch (Exception ex) { //下面检查filestream==null,会进入错误状态。 } } if (filestream == null) { ErrorCode = TransferErrorCode.NullFileStream; TransferState = TransferState.Error; return(null); } if (!createFileMessageSent) { message = new FileDataMessage(); message.Data = new byte[0]; message.Offset = 0; message.Length = 0; message.ItemID = ID; message.ConversationID = ConversationID; createFileMessageSent = true; ((IProgressable)this).Progress(0); } else { try { filestream.Seek(TransferredLength, SeekOrigin.Begin); byte[] buffer = new byte[FileDataMessage.BUFFER_SIZE]; int count = filestream.Read(buffer, 0, FileDataMessage.BUFFER_SIZE); if (count > 0) { message = new FileDataMessage(); if (count == FileDataMessage.BUFFER_SIZE) { message.Data = buffer; } else { byte[] tmp = new byte[count]; Array.Copy(buffer, tmp, count); message.Data = tmp; } message.Offset = TransferredLength; message.Length = count; message.ItemID = ID; message.ConversationID = ConversationID; #if DEBUG Env.Instance.CheckFileDataMessage(message, this); #endif } else { ErrorCode = TransferErrorCode.ReadFileDataFailed; TransferState = TransferState.PostCompleted; } } catch (Exception e) { TransferState = TransferState.Error; Env.Instance.Logger.Log(LogLevel.Error, e, "FileItemRead Exception"); message = null; } } if (message != null) { if (message.Length > 0) { //注意:不是每个类型的GetNextMessage的Message.SendCompleted都适合触发Progress。 //如果把这个方法添加到基类(SequencableItem.GetNextMessage中),因为那个方法会被递归调用,例如,文件夹会调用File的GetNextMessage(通过SequencableItem) ,message.SendComplted会被处理一次 //然后外层的东西调用文件夹的GetNextMessage返回的时候,message.SendCompleted又被调用了一次。 message.SendCompleted += o => ((IProgressable)this).Progress((int)message.Length); } //写在这里是为了处理空文件的情况。空文件发第一个消息,就会进入这里。 } if (message == null) { Debug.WriteLine("Get a null file message"); } if (TransferredLength + (message == null ? 0 : message.Length) == Length) { TransferState = TransferState.PostCompleted; //SendCompeleted会在父类中处理 } return(message); }