9

I am trying to understand the 'SocketAsyncEventArgs' class in C#. http://msdn.microsoft.com/en-us/library/system.net.sockets.socketasynceventargs.aspx

I am following this tutorial: http://www.codeproject.com/Articles/83102/C-SocketAsyncEventArgs-High-Performance-Socket-Cod

Now I am stuck into how I exactly should process data with my server. What I am trying to do is use a SocketAsyncEventArgs for a connected client with an allocated Buffer space of 512 bytes in the BufferManager. Then what I want to do is to decode the byte[] data into my own custom class (ClientPacket) which holds the byte[] for decoding and reading from.

This is all well but my server doesn't always respond with data the question is:

Do I use 1 SocketAsyncEventArgs for the receiving and loop around to process receive data, then allocate a SocketAsyncEventArgs from the pool whenever I need to send then return it on completion?

And how does the SocketAsyncEventArgs know when reading is complete? (when it is finished with the byte[] buffer before it over-writes it with new data) like how is it returned to the pool when done if I don't respond back?

1 Answers1

11

I only use one SocketAsyncEventArgs instance for all of my needs. I simply reset the buffer between each request (by setting it to a new Byte[]).

Once I have connected and have a reference to the Socket, I start listening like this:

public void StartListening(SocketAsyncEventArgs e)
{
    ResetBuffer(e);
    e.Completed += SocketReceive;

    socket.ReceiveAsync(e);
}

I have a helper function that resets the buffer:

private void ResetBuffer(SocketAsyncEventArgs e)
{
    var buffer = new Byte[SocketBufferSize];

    e.SetBuffer(buffer, 0, SocketBufferSize);
}

I process the data like:

private void SocketReceive(Object sender, SocketAsyncEventArgs e)
{
    ProcessData(e.Buffer, 0, e.BytesTransferred);

    ResetBuffer(e);

    socket.ReceiveAsync(e);
}

In ProcessData, you can use the byte array as needed to pull in the data. I use it to create a MemoryStream which I then deserialize into my class (similar to ClientPacket), as follows:

private void ProcessData(Byte[] data, Int32 count)
{
    using (var stream = new MemoryStream(data, 0, count))
    {
        var serializer = new XmlSerializer(typeof(ClientPacket));

        var packet = serializer.Deserialize(stream);

        // Do something with the packet
    }
}

As for your last question. The framework handles everything to do with the underlying TCP protocol, etc. so you can rely on the event handler being called whenever there is data to be processed. Use the e.BytesTransferred value to indicate the amount of data you actually received which may be smaller than but will never exceed your buffer size (SocketBufferSize in my code). If the message was larger than the buffer size, the TCP infrastructure will buffer the messages and send them to you in chunks based on SocketBufferSize (by raising the event once for each chunk). If this is a concern, simply increase SocketBufferSize until the majority of your message are received in one chunk.

The downside of the chunking is that messages may be merged by the infrastructure which means that you may need a way to tell when the first message has ended. Typical approaches include prefacing your message with a 4 byte integer that indicates the message length. I can elaborate more if needed.

Hope that helps.

SonOfPirate
  • 5,642
  • 3
  • 41
  • 97
  • You said by setting it to a new byte[], But I am sharing a large buffer block to all SocketAsyncEventArgs, If I read from that buffer block in a packet then the Buffer block could get over-written by the next incoming data, even If I copy the byte[] array it is going against using the Buffer Block to send/receive data? Also how would I send data with the same object when a receive operation could happen at any time? –  Jan 26 '12 at 15:44
  • 1
    You seem to be assuming a single socket connection between 2 machines. You can't reuse only a single SocketAsyncEventArgs when handling many concurrent connections, which is what I think the question was about. (And which is what SocketAsyncEventArgs is built for). – Clay Fowler Mar 30 '12 at 23:29
  • thanks SonOfPirate, It would be awesome if you can elaborate more on receiving large amount of data. especially clarify please if I must re-call ReceiveAsync each time before reaching the end ; or Framework will do it for me ? – a.boussema May 06 '13 at 21:14
  • The only thing different from the code above that we use when the messages are larger than the buffer size is in ProcessData - everything else stays the same. When chunking large payloads, we preface the payload with a 4 byte value that indicates the size of the payload. We use this number to determine if we have received all of the data. If not, we simply append the received bytes we what we already have. If so, we take the entire array of bytes from all messages received and create the MemoryStream. – SonOfPirate May 07 '13 at 17:03
  • 2
    By making a new Byte[] for each XxxAsync call, you are destroying and creating a new object. Isn't reducing the creation and garbage collection of objects one of the main points of SAEA? – Micah Epps Jun 09 '18 at 02:45