Cheetah
Cheetah

Reputation: 14379

Async socket reading

So I've started to muck around with sockets and asynchronously reading from them.

First question is what is the difference between this:

socket.BeginReceive(readResult.Buffer, 0, SocketReadResult.BufferSize, 0, new AsyncCallback(ReadCallback), readResult);

and

socket.BeginReceive(readResult.Buffer, 0, SocketReadResult.BufferSize, 0, ReadCallback, readResult);

Also, given this as my callback function, why did the example I read have a try/catch around the whole thing, surely you only need a try/catch around the socket.EndReceive() call?

public void ReadCallback(IAsyncResult ar)
{
    try
    {
        var readResult = (SocketReadResult)ar.AsyncState;
        var socket = readResult.Socket;
        int bytesRead = socket.EndReceive(ar);

        if (bytesRead > 0)
        {
            // There might be more data, so store the data received so far.
            readResult.Text.Append(Encoding.ASCII.GetString(readResult.Buffer, 0, bytesRead));

            // Get the rest of the data.
            socket.BeginReceive(readResult.Buffer, 0, SocketReadResult.BufferSize, 0, new AsyncCallback(ReadCallback), readResult);
        }
        else
        {
            var newRead = new SocketReadResult(socket);

            socket.BeginReceive(readResult.Buffer, 0, SocketReadResult.BufferSize, 0, new AsyncCallback(ReadCallback), newRead);

            // All the data has arrived; put it in response.
            if (readResult.Text.Length > 1) ((IMessageSender)this).RouteMessage(this, new MessageString(readResult.Text.ToString()));
        }
    }
    catch (Exception e)
    {
        // TODO: manage this exception.
    }
}

public struct SocketReadResult
{
    StringBuilder Text;
    Socket Socket;
    byte[] Buffer;

    public const int BufferSize = 1024;

    public SocketReadResult(Socket s)
    {
        Socket = s;
        Buffer = new byte[BufferSize];
        Text = new StringBuilder();
    }
}

Last of all, should you wish to gracefully close the listener after you have called socket.BeginReceive(), what functions do you call and how is it managed?

Upvotes: 0

Views: 2514

Answers (3)

I4V
I4V

Reputation: 35353

a) They are equal. Compiler will generate the same code for you

b) How about writing some extension methods for async calls and handle the exceptions as if they were sync calls without blocking the caller?

try
{
    await socket.ConnectTaskAsync("www.google.com", 80);

    await socket.SendTaskAsync(bytesToSend);

    byte[] buf = new byte[0x8000];
    var bytesRead = await socket.ReceiveTaskAsync(buf, 0, buf.Length);
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
}

public static class SocketExtensions
{
    public static Task ConnectTaskAsync(this Socket socket, string host, int port)
    {
        return Task.Factory.FromAsync(
                     socket.BeginConnect(host, port, null, null),
                     socket.EndConnect);
    }

    public static Task<int> ReceiveTaskAsync(this Socket socket, 
                                            byte[] buffer, 
                                            int offset, 
                                            int count)
    {
        return Task.Factory.FromAsync<int>(
           socket.BeginReceive(buffer, offset, count, SocketFlags.None, null, socket),
           socket.EndReceive);
    }


    public static Task SendTaskAsync(this Socket socket, byte[] buffer)
    {
        return Task.Factory.FromAsync<int>(
              socket.BeginSend(buffer,0,buffer.Length,SocketFlags.None, null, socket),
              socket.EndSend);
    }
}

Upvotes: 2

Erik
Erik

Reputation: 12858

The difference between the two calls is negligible and you can consider them equivalent. The second call will help the compiler do type inferencing for you, but that won't be very noticeable outside IntelliSense and auto-completion of code. I personally use the first format since it is more concise.

As for why the try/catch is more than just the Socket.EndReceive() call mostly has to do with the scope of other local variables.

Consider this:

var state = result.AsyncState as SocketStateObject;
var socket = state.Socket;

try
{
  var numberOfBytesRead = socket.EndReceive(result);
}
catch(SocketException ex)
{
  // Handle the exception here.
}

// numberOfBytesRead is not accessible out here!

try
{
  if(socket.Connected)
    socket.BeginReceive(...); // Receive again!
}
catch(SocketException ex)
{
  // Handle the exception here too.
}

As you can see here, there are a couple reasons why one, larger try/catch is preferable to two separate, smaller ones.

First off, local variables are only available within the scope of the try. You could solve that by defining it higher outside the try/catch block.

Secondly, and probably more importantly, has to do with reducing redundancy. Since you are more than likely going to call Socket.BeginReceive() again in your callback, it makes sense to put it under the same try/catch. Otherwise, you'd have to handle your potential SocketException in two places instead of just one.

As for closing a Socket gracefully, you can use the Socket.Close() method. Typically sockets are never reused so you can pass false for that. However, it is preferable to call Socket.Dispose() as well, after the Socket.Close() call. If you are holding the listener socket as a member variable of your class, make sure you implement the IDisposable interface and properly dispose of your socket.

Upvotes: 0

Parimal Raj
Parimal Raj

Reputation: 20575

socket.BeginReceive(readResult.Buffer, 0, SocketReadResult.BufferSize, 0, new AsyncCallback(ReadCallback), readResult);

and

socket.BeginReceive(readResult.Buffer, 0, SocketReadResult.BufferSize, 0, ReadCallback, readResult);

both are the same thing, its same thing as

        //both are the same thing
        button1.Click += new EventHandler(button1_Click);
        button1.Click += button1_Click;

Upvotes: 1

Related Questions