private async Task SendProtocolVersion( string destination, int protocolId ) { if (!SamHelper.IsDestinationStringValid(destination)) { throw new ArgumentOutOfRangeException( "destination" ); } using ( var ms = new MemoryStream( sizeof(byte) + sizeof(int) ) ) { using ( var binaryWriter = new EndianBinaryWriter( _littleEndianConverter, ms ) ) { binaryWriter.Write( (byte)ReliableDatagramCodes.ProtocolVersion ); binaryWriter.Write(protocolId); await _samHelper.SendDatagram( destination, ms.ToArray() ).ThrowIfCancelled(_cts.Token).ConfigureAwait(false); } } }
private async Task SendMessageStatus( string destination, uint messageId, MessageStatusArgs.MessageStatusCode statusCode, uint blocksReceived ) { if (!SamHelper.IsDestinationStringValid(destination)) { throw new ArgumentOutOfRangeException("destination"); } using ( var ms = new MemoryStream( sizeof(byte) + sizeof(uint) + sizeof(byte) + sizeof(uint) ) ) { using ( var binaryWriter = new EndianBinaryWriter( _littleEndianConverter, ms ) ) { binaryWriter.Write( (byte)ReliableDatagramCodes.MessageStatus ); binaryWriter.Write(messageId); binaryWriter.Write((byte)statusCode); binaryWriter.Write(blocksReceived); await _samHelper.SendDatagram( destination, ms.ToArray() ).ThrowIfCancelled(_cts.Token).ConfigureAwait(false); } } }
private async Task SendBlockConfirmation( string destination, uint messageId, uint blockId ) { if (!SamHelper.IsDestinationStringValid(destination)) { throw new ArgumentOutOfRangeException( MyNameof.GetLocalVarName(() => destination)); } using ( var ms = new MemoryStream( sizeof(byte) + sizeof(uint) + sizeof(uint) ) ) { using ( var binaryWriter = new EndianBinaryWriter( _littleEndianConverter, ms ) ) { binaryWriter.Write( (byte)ReliableDatagramCodes.BlockConfirmation ); binaryWriter.Write(messageId); binaryWriter.Write(blockId); await _samHelper.SendDatagram( destination, ms.ToArray() ).ThrowIfCancelled(_cts.Token).ConfigureAwait(false); } } }
private async void OnGetProtocolVersionReceived( GetProtocolVersionArgs getProtocolVersionArgs ) { try { using (_stateHelper.GetFuncWrapper()) { string fromDestination = getProtocolVersionArgs.Destination; if (_dropDestinations.Contains(fromDestination)) { return; } if (!SamHelper.IsDestinationStringValid(fromDestination)) { return; } await SendProtocolVersion( fromDestination, _settings.ProtocolVersion ).ConfigureAwait(false); } } catch (OperationCanceledException) { } catch (WrongDisposableObjectStateException) { } catch (Exception exc) { _log.Error( "OnGetProtocolVersionReceived" + " unexpected error '{0}'", exc.ToString() ); } }
public async Task <uint> SendReliableMessage( string destination, byte[] data, IProgress <OutMessageProgressInfo> progress = null, uint messageId = 0, uint replyToMessageId = 0, byte messageKind = 0, CancellationToken token = default(CancellationToken) ) { using (_stateHelper.GetFuncWrapper()) { try { Assert.True(SamHelper.IsDestinationStringValid(destination)); Assert.NotNull(data); Assert.InRange(data.Length, 1, _settings.MaxMesageLength); if (messageId == 0) { messageId = await GetNextOutMessageId().ConfigureAwait(false); } DestinationInfo destinationInfo; using (await _destinationDbLockSem.GetDisposable(token: token).ConfigureAwait(false)) { if (!_destinationDb.ContainsKey(destination)) { _destinationDb.Add(destination, new DestinationInfo()); } destinationInfo = _destinationDb[destination]; } OutMessageInfo outMessageInfo; using (await destinationInfo.OutMessagesDbLockSem.GetDisposable(token: token).ConfigureAwait(false)) { if ( destinationInfo.OutMessagesDb.ContainsKey(messageId) ) { throw new EnumException <SendReliableMessageExcs>( SendReliableMessageExcs.ExistMessageId ); } outMessageInfo = new OutMessageInfo( data, replyToMessageId, _settings.MaxBlockSize, progress ); destinationInfo.OutMessagesDb.Add(messageId, outMessageInfo); } try { uint blocksCount = outMessageInfo.BlockCount; /* * Handshake */ using ( var compositeTokenSource = CancellationTokenSource.CreateLinkedTokenSource( _cts.Token, token ) ) { try { var messageStatusTask = MessageStatusReceived .Where( x => x.Destination == destination && x.MessageId == messageId ) .FirstAsync() .ToTask(compositeTokenSource.Token); byte[] messageHash = GetMessageHash( messageId, replyToMessageId, messageKind, data ); for ( outMessageInfo.HandshakeAttemptNo = 0; outMessageInfo.HandshakeAttemptNo <= _settings.HandshakeAttemptCount; outMessageInfo.HandshakeAttemptNo++ ) { if ( outMessageInfo.HandshakeAttemptNo == _settings.HandshakeAttemptCount ) { throw new EnumException <SendReliableMessageExcs>( SendReliableMessageExcs.HandshakeTimeout ); } await SendHandshakeStart( destination, messageId, replyToMessageId, (uint)data.Length, outMessageInfo.BlockSize, messageHash, outMessageInfo.Data[0], messageKind ).ConfigureAwait(false); var waitTasks = await Task.WhenAny( messageStatusTask, Task.Delay( TimeSpan.FromSeconds( _settings.HandshakeTimeoutSeconds ), compositeTokenSource.Token ) ).ConfigureAwait(false); if (waitTasks == messageStatusTask) { var messageStatus = await messageStatusTask.ConfigureAwait(false); if ( !( messageStatus.StatusCode == MessageStatusArgs.MessageStatusCode.HandshakeOk || ( messageStatus.StatusCode == MessageStatusArgs.MessageStatusCode.AllBlocksReceived && outMessageInfo.BlockCount == 1 ) ) ) { throw new EnumException <SendReliableMessageExcs>( SendReliableMessageExcs.HandshakeError, messageStatus.WriteObjectToJson() ); } break; } } outMessageInfo.Status = OutMessageStatus.HandshakeOk; outMessageInfo.BlockConfirmed[0] = true; if (outMessageInfo.BlockCount > 1) { // Handshake established, start sending blocks DateTime lastBlockConfirmationTime = DateTime.UtcNow; var blocksInProgress = new SortedSet <uint>(); var blocksInProgressLockSem = new SemaphoreSlim(1); var blockSentTimes = new DateTime[blocksCount]; using ( BlockConfirmationReceived .Where(_ => _.Destination == destination && _.MessageId == messageId ) .ObserveOn(TaskPoolScheduler.Default) .Subscribe(async x => { if ( x.BlockId < blocksCount && !outMessageInfo.BlockConfirmed[x.BlockId] ) { await outMessageInfo.ConfirmBlock(x.BlockId).ConfigureAwait(false); lastBlockConfirmationTime = DateTime.UtcNow; using (await blocksInProgressLockSem.GetDisposable().ConfigureAwait(false)) { blocksInProgress.Remove(x.BlockId); } } } ) ) { while (true) { compositeTokenSource.Token.ThrowIfCancellationRequested(); IEnumerable <int> blocksToSend; using ( await blocksInProgressLockSem.GetDisposable(token: compositeTokenSource.Token) .ConfigureAwait(false)) { if ( blocksInProgress.Count == 0 && outMessageInfo.BlockConfirmed.All(x => x) ) { break; } blocksInProgress.RemoveWhere( i => DateTime.UtcNow > ( blockSentTimes[i] + TimeSpan.FromSeconds( _settings .ConfirmationOneBlockTimeoutSeconds ) ) ); blocksToSend = Enumerable.Range(0, (int)blocksCount) .Where( i => !blocksInProgress.Contains((uint)i) && !outMessageInfo.BlockConfirmed[i] ) .Take( Math.Max( 0, _settings.WindowSize - blocksInProgress.Count ) ); } foreach (int blockId in blocksToSend) { await SendBlock( destination, messageId, (uint)blockId ).ConfigureAwait(false); await outMessageInfo.SendBlock( (uint)blockId ).ConfigureAwait(false); using ( await blocksInProgressLockSem.GetDisposable(token: compositeTokenSource.Token) .ConfigureAwait(false)) { blocksInProgress.Add((uint)blockId); } blockSentTimes[blockId] = DateTime.UtcNow; } if ( DateTime.UtcNow > lastBlockConfirmationTime + TimeSpan.FromSeconds( _settings.ConfirmationTimeoutSeconds ) ) { throw new EnumException <SendReliableMessageExcs>( SendReliableMessageExcs.SendBlocksTimeout ); } await Task.Delay(50, compositeTokenSource.Token).ConfigureAwait(false); } } } outMessageInfo.Status = OutMessageStatus.AllBlocksSent; // all blocks sent and confirmed _log.Trace( "{4}, send reliable message of {0} bytes " + "to {1} id={2},rid={3},kind={5}", data.Length, destination.Substring(0, 20), messageId, replyToMessageId, _reliableSamHelperGuid.ToString().Substring(0, 5), messageKind ); return(messageId); } finally { compositeTokenSource.Cancel(); } } } finally { TryRemoveOutMessageInfo(destinationInfo, messageId); } } catch (EnumException <SendReliableMessageExcs> ) { throw; } catch (OperationCanceledException) { throw; } catch (TimeoutException) { throw; } catch (Exception exc) { throw new EnumException <SendReliableMessageExcs>( SendReliableMessageExcs.UnexpectedError, innerException: exc ); } } }
private async Task SendBlock( string destination, uint messageId, uint blockId ) { if (string.IsNullOrWhiteSpace(destination)) { throw new ArgumentNullException("destination"); } if (!SamHelper.IsDestinationStringValid(destination)) { throw new ArgumentOutOfRangeException("destination"); } DestinationInfo destinationInfo; using (await _destinationDbLockSem.GetDisposable().ConfigureAwait(false)) { if (!_destinationDb.ContainsKey(destination)) { throw new ArgumentException("destination"); } destinationInfo = _destinationDb[destination]; } OutMessageInfo outMessageInfo; using (await destinationInfo.OutMessagesDbLockSem.GetDisposable().ConfigureAwait(false)) { if (!destinationInfo.OutMessagesDb.ContainsKey(messageId)) { throw new ArgumentOutOfRangeException("messageId"); } outMessageInfo = destinationInfo.OutMessagesDb[messageId]; } if (blockId >= outMessageInfo.Data.Count) { throw new ArgumentOutOfRangeException("blockId"); } var dataToSend = outMessageInfo.Data[(int)blockId]; using ( var ms = new MemoryStream( sizeof(byte) + sizeof(uint) + sizeof(uint) + dataToSend.Length ) ) { using ( var binaryWriter = new EndianBinaryWriter( _littleEndianConverter, ms ) ) { binaryWriter.Write( (byte)ReliableDatagramCodes.BlockSend ); binaryWriter.Write(messageId); binaryWriter.Write(blockId); binaryWriter.Write(dataToSend); await _samHelper.SendDatagram( destination, ms.ToArray() ).ThrowIfCancelled(_cts.Token).ConfigureAwait(false); } } }
private async void OnHandshakeStartReceived( HandshakeStartArgs handshakeStartArgs ) { try { using (_stateHelper.GetFuncWrapper()) { string fromDestination = handshakeStartArgs.Destination; if (_dropDestinations.Contains(fromDestination)) { return; } if (!SamHelper.IsDestinationStringValid(fromDestination)) { return; } DestinationInfo destinationInfo; using (await _destinationDbLockSem.GetDisposable().ConfigureAwait(false)) { if ( !_destinationDb.ContainsKey( handshakeStartArgs.Destination ) ) { destinationInfo = new DestinationInfo(); _destinationDb.Add( handshakeStartArgs.Destination, destinationInfo ); } else { destinationInfo = _destinationDb[ handshakeStartArgs.Destination ]; } } using (await destinationInfo.InMessagesDbLockSem.GetDisposable().ConfigureAwait(false)) { if ( destinationInfo.InMessagesDb.ContainsKey( handshakeStartArgs.MessageId ) ) { var inMessageInfo = destinationInfo.InMessagesDb[ handshakeStartArgs.MessageId ]; if ( inMessageInfo.Status == InMessageStatus.HandshakeReceived ) { await SendMessageStatus( fromDestination, handshakeStartArgs.MessageId, MessageStatusArgs.MessageStatusCode.HandshakeOk, 0 ).ConfigureAwait(false); return; } else if ( inMessageInfo.Status == InMessageStatus.AllBlocksReceived ) { await SendMessageStatus( fromDestination, handshakeStartArgs.MessageId, MessageStatusArgs.MessageStatusCode.AllBlocksReceived, inMessageInfo.BlocksReceivedCount ).ConfigureAwait(false); return; } else { await SendMessageStatus( fromDestination, handshakeStartArgs.MessageId, MessageStatusArgs .MessageStatusCode .HandshakeErrorDuplicatedId, inMessageInfo.BlocksReceivedCount ).ConfigureAwait(false); return; } } else { if ( handshakeStartArgs.MessageSize > _settings.MaxMesageLength ) { await SendMessageStatus( fromDestination, handshakeStartArgs.MessageId, MessageStatusArgs.MessageStatusCode .HandshakeErrorWrongTotalSize, 0 ).ConfigureAwait(false); return; } if (handshakeStartArgs.BlockSize > _settings.MaxBlockSize) { await SendMessageStatus( fromDestination, handshakeStartArgs.MessageId, MessageStatusArgs.MessageStatusCode .HandshakeErrorWrongBlockSize, 0 ).ConfigureAwait(false); return; } var inMessageInfo = new InMessageInfo( handshakeStartArgs.MessageSize, handshakeStartArgs.BlockSize, handshakeStartArgs.ReplyToMessageId, handshakeStartArgs.MessageKind, handshakeStartArgs.MessageHash ); if ( inMessageInfo.ReceivedDataChunkSizes[0] != handshakeStartArgs.FirstBlockData.Length ) { await SendMessageStatus( fromDestination, handshakeStartArgs.MessageId, MessageStatusArgs.MessageStatusCode .HandshakeErrorWrongBlockSize, 0 ).ConfigureAwait(false); return; } destinationInfo.InMessagesDb.Add( handshakeStartArgs.MessageId, inMessageInfo ); await SendMessageStatus( fromDestination, handshakeStartArgs.MessageId, MessageStatusArgs.MessageStatusCode.HandshakeOk, 0 ).ConfigureAwait(false); OnBlockSendReceived( new BlockSendArgs() { BlockData = handshakeStartArgs.FirstBlockData, BlockId = 0, Destination = handshakeStartArgs.Destination, MessageId = handshakeStartArgs.MessageId } ); return; } } } } catch (OperationCanceledException) { } catch (WrongDisposableObjectStateException) { } catch (Exception exc) { _log.Error( "OnHandshakeStartReceive" + " unexpected error '{0}'", exc.ToString() ); } }
private async void OnGetMessageStatusReceived( GetMessageStatusArgs getMessageStatusArgs ) { try { using (_stateHelper.GetFuncWrapper()) { string fromDestination = getMessageStatusArgs.Destination; if (_dropDestinations.Contains(fromDestination)) { return; } if (!SamHelper.IsDestinationStringValid(fromDestination)) { return; } bool destinationDbContainsKey; using (await _destinationDbLockSem.GetDisposable().ConfigureAwait(false)) { destinationDbContainsKey = _destinationDb.ContainsKey( fromDestination ); } if ( !destinationDbContainsKey ) { await SendMessageStatus( fromDestination, getMessageStatusArgs.MessageId, MessageStatusArgs .MessageStatusCode .UnknownMessageId, 0 ).ConfigureAwait(false); return; } DestinationInfo destinationInfo; using (await _destinationDbLockSem.GetDisposable().ConfigureAwait(false)) { destinationInfo = _destinationDb[fromDestination]; } bool inMessagesDbContainsKey; using (await destinationInfo.InMessagesDbLockSem.GetDisposable().ConfigureAwait(false)) { inMessagesDbContainsKey = destinationInfo.InMessagesDb.ContainsKey( getMessageStatusArgs.MessageId ); } if ( !inMessagesDbContainsKey ) { await SendMessageStatus( fromDestination, getMessageStatusArgs.MessageId, MessageStatusArgs .MessageStatusCode .UnknownMessageId, 0 ).ConfigureAwait(false); return; } InMessageInfo inMessageInfo; using (await destinationInfo.InMessagesDbLockSem.GetDisposable().ConfigureAwait(false)) { inMessageInfo = destinationInfo.InMessagesDb[ getMessageStatusArgs.MessageId ]; } int blocksReceivedCount = inMessageInfo.BlocksReceived.Count(x => x); if ( inMessageInfo.Status == InMessageStatus.HandshakeReceived ) { await SendMessageStatus( fromDestination, getMessageStatusArgs.MessageId, MessageStatusArgs .MessageStatusCode .HandshakeOk, (uint)blocksReceivedCount ).ConfigureAwait(false); return; } if ( inMessageInfo.Status == InMessageStatus.AllBlocksReceived ) { await SendMessageStatus( fromDestination, getMessageStatusArgs.MessageId, MessageStatusArgs .MessageStatusCode .AllBlocksReceived, (uint)inMessageInfo.BlocksReceived.Length ).ConfigureAwait(false); } } } catch (OperationCanceledException) { } catch (WrongDisposableObjectStateException) { } catch (Exception exc) { _log.Error( "OnGetMessageStatusReceived" + " unexpected error '{0}'", exc.ToString() ); } }
private async void OnBlockSendReceived( BlockSendArgs blockSendArgs ) { try { using (_stateHelper.GetFuncWrapper()) { string fromDestination = blockSendArgs.Destination; if (_dropDestinations.Contains(fromDestination)) { return; } if (!SamHelper.IsDestinationStringValid(fromDestination)) { return; } DestinationInfo destinationInfo; using (await _destinationDbLockSem.GetDisposable().ConfigureAwait(false)) { if ( !_destinationDb.ContainsKey( blockSendArgs.Destination ) ) { return; } destinationInfo = _destinationDb[blockSendArgs.Destination]; } InMessageInfo inMessageInfo; using (await destinationInfo.InMessagesDbLockSem.GetDisposable().ConfigureAwait(false)) { if ( !destinationInfo.InMessagesDb.ContainsKey( blockSendArgs.MessageId ) ) { return; } inMessageInfo = destinationInfo.InMessagesDb[ blockSendArgs.MessageId ]; } if ( inMessageInfo.Status == InMessageStatus.AllBlocksReceived ) { return; } if (inMessageInfo.BlockCount > 1) { await SendBlockConfirmation( fromDestination, blockSendArgs.MessageId, blockSendArgs.BlockId ).ConfigureAwait(false); } if ( await inMessageInfo.ReceiveBlock( blockSendArgs.BlockId, blockSendArgs.BlockData ).ConfigureAwait(false) ) { var allData = new List <byte>( (int)inMessageInfo.MessageSize ); foreach ( byte[] blockData in inMessageInfo.ReceivedData ) { allData.AddRange(blockData); } var dataArray = allData.ToArray(); if ( inMessageInfo.MessageHash.SequenceEqual( GetMessageHash( blockSendArgs.MessageId, inMessageInfo.ReplyToMessageId, inMessageInfo.MessageKind, dataArray ) ) ) { ReliableMessageReceived.OnNext( new ReliableMessageReceivedArgs() { Destination = fromDestination, Data = allData.ToArray(), MessageId = blockSendArgs.MessageId, ReplyToMessageId = inMessageInfo.ReplyToMessageId, MessageKind = inMessageInfo.MessageKind } ); } await inMessageInfo.FlushBlocks().ConfigureAwait(false); } } } catch (OperationCanceledException) { } catch (WrongDisposableObjectStateException) { } catch (Exception exc) { _log.Error( "OnBlockSendReceived" + " unexpected error '{0}'", exc.ToString() ); } }