InstallGentoo
InstallGentoo

Reputation: 345

C# async Begin Receive Call back method misses some packets

I send 2 successive packets from my client to the server who is listening using BeginReceive

The server always receives the first packet but not the other EXCEPT if I run the client in debug mode and slowly send the next packet after the other.

Here's a snippet from my sending function

if (soc.Connected)
{
    byte[] byData = new byte[System.Text.Encoding.ASCII.GetByteCount("Hi")];
    byData = System.Text.Encoding.ASCII.GetBytes("Hi");
    soc.Send(BitConverter.GetBytes(byData.Length));
    soc.Send(byData);
}

And here's is my call back function located inside of my server:

 private void Recieve(IAsyncResult iar)
 {
    int j = 0;

    Socket server_conn = (Socket)iar.AsyncState;
    server_conn.EndReceive(iar);

    if (!SocketConnected(server_conn))
    {   
        server_conn.Close();
        return;
    }

    if (g_bmsg.Length != 0)
    {
        logthis(server_conn.RemoteEndPoint.ToString() + ": " + Encoding.ASCII.GetString(g_bmsg, 0, g_bmsg.Length));
    }

    //Find out who sent this
    foreach (ClientData cls in clientlist)
    {
        if (server_conn.RemoteEndPoint == cls.clientsock.RemoteEndPoint)
        {
            j = cls.index;
            break;
        }
    }
     server_conn.BeginReceive(g_bmsg, 0, g_bmsg.Length, SocketFlags.None, new AsyncCallback(Recieve), server_conn);
}

Upvotes: 2

Views: 2160

Answers (1)

vgru
vgru

Reputation: 51224

When working with TCP/IP sockets (or pretty much any communication layer), you rarely have a guarantee that the entire message you intended to send will come in a single packet.

This means that you should always keep a FIFO buffer of your own, and parse it sequentially.

This is the general idea:

// fifo queue (for background thread parsing, make sure it's thread safe)
private readonly ConcurrentQueue<byte> _commQueue = new ConcurrentQueue<byte>();

In your Receive method, you should simply enqueue the data to the FIFO:

private void Receive(IAsyncResult iar)
{
    Socket server_conn = (Socket)iar.AsyncState;

    // check how many bytes we actually received
    var numBytesReceived = server_conn.EndReceive(iar);

    if (!SocketConnected(server_conn))
    {   
        server_conn.Close();
        return;
    }

    // get the received data from the buffer
    if (numBytesReceived > 0)
    {
        for (int i = 0; i < numBytesReceived; i++)
            _commQueue.Enqueue(g_bmsg[i]);

        // signal the parser to continue parsing        
        NotifyNewDataReceived();
    }

    // continue receiving
    server_conn.BeginReceive(g_bmsg, 0, g_bmsg.Length, SocketFlags.None, new AsyncCallback(Recieve), server_conn);
}

Upvotes: 1

Related Questions