Truman Starr
Truman Starr

Reputation: 19

Async/Await socket receiving data issue

Is this is a good approach to read data from Socket?

public class StateObject : IDisposable
{
    private const int _bufferSize = 1024;
    public byte[] Buffer { get; } = new byte[_bufferSize];
    public int BufferSize { get; } = _bufferSize;
    public MemoryStream MsStream { get; }

    public EnhStateObject()
    {
        MsStream = new MemoryStream();
    }

    public void Dispose()
    {
        MsStream.Dispose();
    }
}

public async static Task<int> ReceiveAsync(this Socket client, StateObject state)
{
    var task = await await Task<int>.Factory.FromAsync(
    (cb, s) => client.BeginReceive(state.Buffer, 0, state.BufferSize, SocketFlags.None, cb, s),
    client.EndReceive, null).ContinueWith(t =>
    {
        state.MsStream.Write(state.Buffer, 0, t.Result);
        return t;
    });

    if (task == state.BufferSize) await client.ReceiveAsync(state);

    return task;
}

I mean, transfer outside the object and storing the received information in it through the recursive call.

Or is there a more elegant way to do this using the same FromAsync wrapper?

Upvotes: 1

Views: 2083

Answers (1)

Dan
Dan

Reputation: 10548

No, this is not a good way of reading data from a socket. You are reinventing the wheel here; use NetworkStream instead. I don't know why you need to use the low-level Socket API, but, well, you don't.

Instead, wrap your Socket in a NetworkStream at the callsite, like so:

Socket socket = ....;
Stream stream = new NetworkStream(socket);

Then you can just read data using ReadAsync() as you would expect (bonus points: no state object to pass around and you can mock the stream with a standard MemoryStream in tests)

var buffer = new byte[1024];
var bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);

If you're using Tcp, I'd recommend using the TcpClient API instead of working directly with sockets.

Upvotes: 4

Related Questions