-1

I have code like this:

  public byte[] Read()
  {
     try
     {
        if (ClientSocket.Available != 0)
        {
           var InBuffer = new byte[ClientSocket.Available];
           ClientSocket.Receive(InBuffer);
           return InBuffer;
        }
        else
        {
           return null;
        }

     }
     catch (Exception ex)
     {
        Console.WriteLine(ex.ToString());
     }

  }

I'd like to make an async equivalent without totally changing the flow of the code (warts and all) and I am running into issues, I also want to switch to NetworkStream as it has built in async methods

I'd like the sig to be Task<byte[]> Read() but:

  1. NetworkStream.ReadAsync expects to be passed a byte[] but doesn't return it, so I can't simply return stream.Read(...)
  2. NetworkStream doesn't appear to tell you how many bytes are available to be read.
  3. If there is no data available I don't want to call stream.Read just pass back null.

So regardless of issues in the above method - I know it is not optimal - how might I do this?

The aim being I can do, or equivalent.

byte [] bytes = await x.Read();

Mr. Boy
  • 60,845
  • 93
  • 320
  • 589

1 Answers1

1

NetworkStream.ReadAsync is like most of the "give me a byte buffer" methods I've come across; you give it a byte array and ask it to read X number of bytes from the network and place it into the buffer. It may read less than you requested, but it won't read more. It returns the number of bytes read. At all times your code keeps ahold of the byte array buffer so you would end up doing something like this:

byte[] buf = new byte[4096];
int bytesRead = await networkStream.ReadAsync(buf, 0, buf.Length. someCancelationToken);

byte[] rtn = new byte[bytesRead];
Array.Copy(buf, 0, rtn, 0, rtn.Length);

return rtn;

That is to say you read as an Async op, then return an array sized to exactly the number of bytes reported as read, which comes from the buffer. A method employing this code would return a Task

NetworkStream also has a method CanRead that would appear to address your "if there is nothing to read" requirement

There is also an overload of ReadAsync that accepts from you a Memory as the buffer. You'd use this in a similar way except you would call the Slice method on it when you knew how many bytes had been read, to return you a Memory looking at just that section of the buffer. If you then called ToArray on the result of the Slice call you'd get an array sized to your liking (bytes read). There's likely little difference in the two in this context, though using Memory (and it's related class Span) can reduce the number of memory allocations for some operations

Caius Jard
  • 72,509
  • 5
  • 49
  • 80
  • And then simply wrap this in a method sig `Task ...` ? I was hoping to avoid `await` multiple times but it seems if I need to manipulate the state after `ReadAsync` that's simply impossible. – Mr. Boy Mar 24 '20 at 12:04
  • Sure, it's a `Task` but not sure what you mean by multiple `await` - It only uses await once? Do you mean you're trying to avoid nested awaits (the calling method awaits yours, you await networkStream's..). If so, don't; it's a premature micro-optimization - perhaps see https://blog.stephencleary.com/2016/12/eliding-async-await.html – Caius Jard Mar 24 '20 at 19:41
  • ps; don't forget to name your method Read*Async* – Caius Jard Mar 24 '20 at 19:45
  • Oh yes. This is a coding standard not a technical one right? – Mr. Boy Mar 25 '20 at 11:28
  • Yes; it essentially lets other people know "this method behaves in an asyncronous way" - some good discussion [here](https://stackoverflow.com/questions/15951774/does-the-use-of-the-async-suffix-in-a-method-name-depend-on-whether-the-async) – Caius Jard Mar 25 '20 at 12:11