简体   繁体   中英

Constantly read from NetworkStream async

I am a farily new .NET-developer and I'm currently reading up on async / await . I need to work on a framework used for testing devices that are controlled by remotely accessing servers using TCP and reading/writing data from/to these servers. This will be used for unit tests.

There is no application-layer protocol and the server may send data based on external events. Therefore I must be able to continuously capture any data coming from the server and write it to a buffer, which can be read from a different context.

My idea goes somewhere along the lines of the following snippet:

// ...
private MemoryStream m_dataBuffer;
private NetworkStream m_stream;
// ...

public async void Listen()
{
  while (Connected)
  {
    try
    {
      int bytesReadable = m_dataBuffer.Capacity - (int)m_dataBuffer.Position;

      // (...) resize m_dataBuffer if necessary (...)

      m_stream.ReadTimeout = Timeout;

      lock (m_dataBuffer)
      {
        int bytesRead = await m_stream.ReadAsync(m_dataBuffer.GetBuffer(), 
          (int)m_dataBuffer.Position, bytesReadable);
        m_stream.Position += bytesRead;
      }
    }
    catch (IOException ex)
    {
      // handle read timeout.
    }
    catch (Exception)
    {
      throw new TerminalException("ReadWhileConnectedAsync() exception");
    }
  }
}

This seems to have the following disadvantages:

  1. If calling and awaiting the Listen function, the caller hangs, even though the caller must be able to continue (as the network stream should be read as long as the connection is open).
  2. If declaring it async void and not awaiting it, the application crashes when exceptions occur in the Task.
  3. If declaring it async Task and not awaiting it, I assume the same happens (plus I get a warning)?

The following questions ensue:

  • Can I catch exceptions thrown in Listen if I don't await it?
  • Is there a better way to constantly read from a network stream using async / await ?
  • Is it actually sane to try to continuously read from a network stream using async / await or is a thread a better option?

async void should at the very least be async Task with the return value thrown away. That makes the method adhere to sane standards and pushes the responsibility into the caller which is better equipped to make decisions about waiting and error handling.

But you don't have to throw away the return value. You can attach a logging continuation:

async Task Log(Task t) {
 try { await t; }
 catch ...
}

And use it like this:

Log(Listen());

Throw away the task returned by Log (or, await it if you wish to logically wait).

Or, simply wrap everything in Listen in a try-catch. This appears to be the case already.

Can I catch exceptions thrown in Listen if I don't await it?

You can find out about exceptions using any way that attaches a continuation or waits synchronously (the latter is not your strategy).

Is there a better way to constantly read from a network stream using async/await?

No, this is the way it's supposed to be done. At any given time there should be one read IO outstanding. (Or zero for a brief period of time.)

Is it actually sane to try to continuously read from a network stream using async/await or is a thread a better option?

Both will work correctly. There is a trade-off to be made. Synchronous code can be simpler, easier to debug and even less CPU intensive. Asynchronous code saved on thread stack memory and context switches. In UI apps await has significant benefits.

I would do something like this:

const int MaxBufferSize = ... ;
Queue<byte> m_buffer = new Queue<byte>(MaxBufferSize);
NetworkStream m_stream = ... ;

...

// this will create a thread that reads bytes from 
// the network stream and writes them into the buffer 
Task.Run(() => ReadNetworkStream());


private static void ReadNetworkStream()
{
    while (true)
    {
        var next = m_stream.ReadByte();
        if (next < 0) break; // no more data

        while (m_buffer.Count >= maxBufferSize)
            m_buffer.Dequeue(); // drop front

        m_buffer.Enqueue((byte)next);
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM