1
votes

I am using C#'s async Sockets and use BeginReceive to read data from the Socket into a byte[]-Buffer of 8192 bytes. But what happens, when new packet come in before BeginReceive is called again? My current setup handles about 3 incoming messages before it stops. I'm assuming that the Socket must store the incoming data somewhere before it can be processed by BeginReceive.

Do I have any control over how much data the Socket buffers before it stops?

Do I have to rely on processing the incoming messages fast enough in order not to "miss" any?

What happen, when the ProcessMessageBuffer method in the example below takes so long (for some reason) that the incoming data starts to pile up in the Socket?

public void ReadCallback(IAsyncResult ar)
    {
        // We have a new TCP Packet!
        int bytesReceived = 0;

        try
        {
            // The amount of bytes we have just received
            bytesReceived = Socket.EndReceive(ar);
        }
        catch (SocketException ex)
        {
            // The client closed the connection
            OnSocketException(new SocketExceptionEventArgs(ex));
        }

        if (bytesReceived > 0)
        {
            // We have received some data. Write it to the MessageBuffer
            MessageBuffer.Write(ReceiveBuffer, 0, bytesReceived);

            // Process the Messages that may be stored in the MessageBuffer
            // What happens, if this takes too long?
            ProcessMessageBuffer(MessageBuffer.ToArray());

            // Get ready to receive more data
            Socket.BeginReceive(ReceiveBuffer, 0, ReceiveBuffer.Length, SocketFlags.None, new AsyncCallback(ReadCallback), null);
        }
    }
1

1 Answers

0
votes

Network I/O is buffered at every step of the way. So it's hard to know which "buffer" you are worried about.

From Socket.ReceiveBufferSize:

…The default is 8192.

A larger buffer size potentially reduces the number of empty acknowledgements (TCP packets with no data portion), but might also delay the recognition of connection difficulties. Consider increasing the buffer size if you are transferring large files, or you are using a high bandwidth, high latency connection (such as a satellite broadband provider.)

From your question:

Do I have any control over how much data the Socket buffers before it stops?

You have at least a few strategies available:

  • Modify the ReceiveBufferSize property value. This will change the size of the buffer in the socket object.
  • Use a larger buffer in your call to BeginReceive(). This will provide the Socket object with more space into which it can write before it can no longer empty its own buffer. Note that the buffer you pass to Socket will be pinned until the receive operation completes, which can have implications on memory heap management.
  • Issue multiple BeginReceive() calls. This has a similar effect as providing a larger buffer, but gives you finer granularity of control over the buffers. It comes with the complication that, due to how Windows schedules threads, you may wind up executing the callback for the receive operation completions in a different order than you called BeginReceive() in the first place. The data will be in the right order, according to the order of the BeginReceive() calls and each buffer you passed, but those buffers may appear to your code to get filled out of order (they aren't really, but the thread handling a later-filled buffer might get to run before the thread handling an earlier-filled buffer).

See socket buffer size: pros and cons of bigger vs smaller for some additional details.

Do I have to rely on processing the incoming messages fast enough in order not to "miss" any?

No. TCP is reliable. If you don't process data quickly enough, all that will happen is that the remote endpoint will have to wait to send more data.

That said, you should work very hard to make your socket I/O code work as quickly as possible. If you have some processing that is slow enough to delay receive operations, you should off-load that processing to a completely independent thread, buffering the received data yourself (e.g. with a MemoryStream, FileStream, a queue of some sort, etc.).

If you do it that way, then you likely won't have to do anything beyond the simple, default handling of socket. You'll be able to have a single BeginReceive() outstanding at once, you won't have to adjust the socket's buffer, and you'll be able to allocate "normal-sized" byte[] objects (or keep a single one around for reuse).