0

I am trying to let sslStream read asynchronously for 5 seconds. If the stream received nothing after 5 seconds, the stream will be closed.

The problem of current implementation is that closing the stream using this trick will emit an error on console saying: Cannot access a disposed object.

Is there any other way to achieve my goal without this error?

Here's my code:

private static async Task<string> ReadMessageAsync(SslStream pSslStream)
{
    byte[] buffer = new byte[2048];
    StringBuilder messageData = new StringBuilder();
    int bytes = -1;
    try
    {
        do
        {
            bytes = await pSslStream.ReadAsync(buffer, 0, buffer.Length)
                .TimeoutAfter(TimeSpan.FromSeconds(5));

            Decoder decoder = Encoding.UTF8.GetDecoder();
            char[] chars = new char[decoder.GetCharCount(buffer, 0, bytes)];
            decoder.GetChars(buffer, 0, bytes, chars, 0);
            messageData.Append(chars);

            if (messageData.ToString().IndexOf("<EOF>") != -1)
            {
                break;
            }
        } while (bytes != 0);
    }
    catch (TimeoutException)
    {
        Console.WriteLine("Timeout");
        pSslStream.Close();
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }

    return messageData.ToString();
}

public static async Task<T> TimeoutAfter<T>(this Task<T> task, TimeSpan timeout)
{
    if (task != await Task.WhenAny(task, Task.Delay(timeout)))
    {
        throw new TimeoutException("Timeout");
    }

    return task.Result;
}

P/s: The extension method was from How do you catch CancellationToken.Register callback exceptions?

AhSeng Fan
  • 397
  • 5
  • 11
  • And why you are asking? Do you experience any problems with this solution? I mean this site goal is to solve problems, and if there are none - there is another site specifically intended for code review. – Evk Apr 08 '18 at 20:18
  • @Evk I have updated my question. Thank you. – AhSeng Fan Apr 08 '18 at 20:43
  • So after stream is closed - you should not perform any actions on it. Your error indicates that you are doing that, probably in some method which calls ReadMessageAsync and which code is not included in question. – Evk Apr 08 '18 at 20:46
  • @Evk No, the error pops up immediately after the call of stream close method and I do not perform any other operation to the stream outside of ReadMessageAsync. – AhSeng Fan Apr 08 '18 at 20:49
  • Then I suppose it is thrown by stream ReadAsync task which is interrupted by closing stream. In this case I'd it's fine and you should just catch and ignore it. – Evk Apr 08 '18 at 20:52
  • 1
    Note that in current implementation, if timeout is reached you just throw an exception, but read task is abandoned. So before throwing timeout exception you should registed continuation (with ContinueWith) for read task, and observe any errors thrown there. You might log some exceptions, but specifically ObjectDisposedException you can just ignore, because it's an expected one. – Evk Apr 08 '18 at 20:56
  • The trouble seems to be that the actual read task has no idea about your timeout implementation. Have you tried setting SslStream.ReadTimeout, or using ReadAsync() with a CancellationToken (CancellationTokenSource.CancelAfter())? – glenebob Apr 09 '18 at 05:40
  • @glenebob sslstream / networkstream simply ignore cancellationtoken so that doesn’t work. – AhSeng Fan Apr 09 '18 at 05:49
  • What about the ReadTimeout property? I seem to remember from some years ago that SslStream.ReadTimeout does actually work. – glenebob Apr 09 '18 at 06:16
  • @glenebob Only synchronous read works with readtimeout unfortunately. – AhSeng Fan Apr 09 '18 at 06:25
  • @Evk After searching solution for whole day, I think your method is the only way to deal with that disposed object access error. But I really don't know how to catch that `DisposedObjectException`. Do you mind to show me the code to achieve that? So then I can mark your answer. – AhSeng Fan Apr 09 '18 at 15:42

0 Answers0