private async Task WriteBlocksAsync() { var token = CancellationTokenSource.Token; try { while (!token.IsCancellationRequested) { await _writerEvent.WaitAsync(token); while (_writer.Length > BlockSize || (EndOfStream && _writer.Length > 0)) { var message = Context.Request.Clone(); // Reset the message Id so it's set by CoapClient message.Id = 0; message.Options.Add(new Options.Block1(_writeBlockNumber, BlockSizeInternal, _writer.Length > BlockSizeInternal)); message.Payload = new byte[_writer.Length < BlockSizeInternal ? _writer.Length : BlockSizeInternal]; _writer.Peek(message.Payload, 0, BlockSizeInternal); Context.MessageId = await Context.Client.SendAsync(message, token); // Keep the response in the queue in case the Applciation needs it. var result = await Context.Client.GetResponseAsync(Context.MessageId, token); if (EndOfStream) { Context.Response = result; } if (result.Code.IsSuccess()) { _writer.AdvanceQueue(message.Payload.Length); _writeBlockNumber++; var block = result.Options.Get <Options.Block1>(); var blockDelta = block.BlockSize - BlockSizeInternal; // Only update the size if it's smaller if (blockDelta < 0) { BlockSizeInternal += blockDelta; _writeBlockNumber -= blockDelta / BlockSizeInternal; } else if (blockDelta > 0) { throw new CoapBlockException($"Remote endpoint requested to increase blocksize from {BlockSizeInternal} to {BlockSizeInternal + blockDelta}"); } } else if (result.Code.IsClientError() || result.Code.IsServerError()) { if (_writeBlockNumber == 0 && result.Code == CoapMessageCode.RequestEntityTooLarge && BlockSizeInternal > 16) { // Try again and attempt at sending a smaller block size. _writeBlockNumber = 0; BlockSizeInternal /= 2; continue; } Context.Response = result; throw new CoapBlockException($"Failed to send block ({_writeBlockNumber}) to remote endpoint", CoapException.FromCoapMessage(result), result.Code); } } // flag the mot recent flush has been performed if (_writer.Length <= BlockSize) { FlushFinishedEvent.Set(); } if (EndOfStream) { break; } } } catch (Exception ex) { // Hold onto the exception to throw it from a synchronous call. CaughtException = ex; } finally { EndOfStream = true; FlushFinishedEvent.Set(); } }