0

I am working on a C# TCP client and monitoring my packets using Microsoft Network Monitor. There is a server that's basically a black box sends an N amount of packets (right now it's in the order of 10-20) with a delay of 0,1 ms to 1 ms between each of them and the next one.

The packets that are read by my client are in order and of course most arrive in larger chunks than appear on Network Monitor since TCP receives the stream. My problem is some packets fail to arrive (I did check the previous chunk of information to make sure it's not there. They aren't stored in the wrong order either.).

So is it possible that my client is missing some of the information somehow? Are the packets being sent too frequently? Sadly I cannot tamper with their frequency. I add here some of my code, if you could enlighten me as to why packets that arrive are not read and how to solve this I would be most grateful.

This is where I first call BeginReceive:

private static void AcceptCallback(IAsyncResult result)
{
    ConnectionInfo connection = new ConnectionInfo();
    MensajeRecibido msj = new MensajeRecibido();
    try
    {
        // Finish Accept

        Socket s = (Socket)result.AsyncState;
        connection.Socket = s.EndAccept(result);
        msj.workSocket = connection.Socket;
        connection.Socket.Blocking = false;
        connection.Buffer = new byte[255];
        lock (connections) connections.Add(connection);

        // Start Receive

        connection.Socket.BeginReceive(msj.buffer, 0,
        msj.buffer.Length, SocketFlags.None,
        new AsyncCallback(ReceiveCallback), msj);
        // Start new Accept
        serverSocket.BeginAccept(new AsyncCallback(AcceptCallback), result.AsyncState);
    }
    catch (SocketException exc)
    {
        //Log here
    }
    catch (Exception exc)
    {
        //Log here
    }
}

This is the callback:

private async static void ReceiveCallback(IAsyncResult result)
{
    MensajeRecibido mensaje = new MensajeRecibido();
    mensaje = (MensajeRecibido)result.AsyncState;

    try
    {
        mensaje.workSocket.EndReceive(result);
        mensaje.EstaCompleto();
        mensaje.workSocket.BeginReceive(mensaje.buffer, 0,
        mensaje.buffer.Length, SocketFlags.None,
        new AsyncCallback(ReceiveCallback), mensaje);
    }
    catch (SocketException)
    {
        //Log
    }
    catch (Exception)
    {
        //Log
    }
}

And this is the method EstaCompleto() which basically converts the message and adds it to a list. (It returns true or false because it's actually meant to go in an if clause but until I get rid of this problem that really serves no purpose)

public bool EstaCompleto()
{
    MensajeActual = Destuffing(ByteToFrame_Decoder(buffer)); //This translates the message to an understandable string
    Mensajes.Enqueue(MensajeActual);
    if(MensajeActual.Contains("<ETX>"))
    {
        return true;
    }
    else return false;
}

Edit 25/3/15: Here's the rest of the class MensajeRecibido.

 public class MensajeRecibido
{

    public Socket workSocket = null;
    // Size of receive buffer.
    public const int BufferSize = 25500; 
    // Receive buffer.
    public byte[] buffer = new byte[BufferSize];
    public string UltimoMensajeLeido = "0";
    public string MensajeActual = "0";
    public Queue<string> Mensajes = new Queue<string>();
    public IPAddress IPRack;


    //*******************************************************************

    public bool EstaCompleto()
    ///See code in the previous sample


    //*******************************************************************
    public string ByteToFrame_Decoder(byte[] frame)
    {
        string answer = null;
        UTF8Encoding ObjDecoder = new System.Text.UTF8Encoding();
        char[] array_chares = new char[frame.Length];
        string msj_neg = null;
        string titlemsg = "Atención";
        try
        {
            int cant = ObjDecoder.GetChars(frame, 0, frame.Length, array_chares, 0);
        }
        catch (EncoderFallbackException EncFbackEx)
        {
            msj_neg = "No hay comunicación";
            //   System.Windows.Forms.MessageBox.Show(msj_neg, titlemsg, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
        }
        answer = decode_string(array_chares);
        return answer;
    } // fin método ByteToFrame_Decoder()

    //*******************************************************************
    public string Destuffing(string msjstuff)
    {
        string destuffed = null;
        string matched = null;
        string original = null;
        int largo = msjstuff.Length;

        for (int i = 0; i < (largo - 1); i++)
        {
            matched = msjstuff.Substring(i, 2);
            original = msjstuff.Substring(i, 1);
            if (original != " ")
            {
                switch (matched)
                {
                    case "EX":
                        { original = "D"; i++; } break;

                    case "ex":
                        { original = "d"; i++; } break;

                    case "EE":
                        { original = "E"; i++; } break;

                    case "ee":
                        { original = "e"; i++; } break;

                    case "  ":
                        { original = ""; i += 2; } break;
                }
                destuffed = destuffed + original;
            }
            else
            {
                i++;
            }

        }
        destuffed = destuffed + ">";
        return destuffed;

    } //fin método Destuffing()
    //*******************************************************************
    public static string decode_string(char[] ArrChar)
    {
        string text = null;
        string reply = null;
        foreach (char letter in ArrChar)
        {
            int value = Convert.ToInt32(letter);
            string hexOutput = String.Format("{0:X}", value); // Convert the decimal value to a hexadecimal value in string form.
            switch (hexOutput)
            {
                case "20":
                    text = " ";
                    break;
                case "1":
                    text = "<SOH>";
                    break;
                case "2":
                    text = "<STX>";
                    break;
                case "3":
                    text = "<ETX>";
                    break;
                case "4":
                    text = "<EOT>";
                    reply = reply + text;
                    goto Finish;
                case "5":
                    text = "<ENQ>";
                    break;
                case "6":
                    text = "<ACK>";
                    break;
                case "15":
                    text = "<NAK>";
                    break;
                case "17":
                    text = "<ETB>";
                    break;
                case "1E":
                    text = "<RS>";
                    break;
                /*case "23":
                    text = "#";
                    break;
                case "24":
                    text = "$";
                    break;
                case "26":
                    text = "&";
                    break;*/
                default:
                    text = letter.ToString();
                    break;
            }
            reply = reply + text;
        }
    Finish: ; //salimos del foreach
        return reply;
    } //fin método decode_string()
    //*******************************************************************

}
Alex
  • 41
  • 8

1 Answers1

0

Without a good, minimal, complete code example that reliably demonstrates the problem, it's impossible to provide an exact fix for the bug.

However, from this statement in your ReceiveCallback() method, it's clear what the problem is:

mensaje.workSocket.EndReceive(result);

The EndReceive() method returns a byte count for a reason: there is no guarantee of the number of bytes that will be received.

Network programming novices generally complain about two different behaviors:

  1. Their code only receives part of a "packet" (or "message", or similar terminology).
  2. Their code fails to receive some of the "packets" which were sent.

Both problems stem from a single source: the failure to understand that in the TCP protocol, there is no such thing as a "packet".

It's up to the application to define a message boundary. All that TCP gives you is a guarantee that if the bytes sent are in fact received, they will be received in the same order in which they were sent, and if any given byte is received, all of the previously sent bytes were also received (i.e. no gaps in the data).

Problem #1 above happens when TCP delivers only part of what the novice programmer sent as a "packet". This is perfectly legal behavior on TCP's part, and it's up to the application to keep track of the data received so far and to figure out when it's received a whole "packet".

Problem #2 (which is what you're experiencing) happens when TCP delivers two or more "packets" in a single receive operation. Again, this is perfectly legal on TCP's part, and it's up to the application to process the received data and identify where one "packet" ends and the next begins.

The first step to doing all of this correctly is to actually copy the return value of the EndReceive() method to a variable, and then to use that value as part of processing the received data. Since your code example doesn't save the value anywhere, or even look at it at all, I can guarantee that you aren't correctly handling the "packet" boundaries in your data.

How should you handle those boundaries? I have no idea. It depends on how you are sending the data, and how you want to process the results. Without a complete code example (as described in the link I provided above), it's not possible to address that. Do note however that there are a large number of examples out there for how to do it correctly. Hopefully now that you know what to look for, you will be able to come up with a fix yourself. If not, feel free to create a good code example and post a new question asking for help with that.

Community
  • 1
  • 1
Peter Duniho
  • 68,759
  • 7
  • 102
  • 136
  • First of all, thank you for he minimal complete code link, I hadn't seen it before. It would be hard to reproduce he problem for someone who doesn't own the equipment I'm communicating with and I'm afraid I'm not allowed to share the IP to access it which is rather troublesome. I do store the information every time on these lines: MensajeActual = Destuffing(ByteToFrame_Decoder(buffer)); //This translates the message to an understandable string Mensajes.Enqueue(MensajeActual); And I then check the queue to see what has been read. I don't receive two messages together as I stated before. – Alex Mar 25 '15 at 12:53
  • _"It would be hard to reproduce he problem"_ -- nevertheless, for anyone to help with any sort of precise answer, you _must_ provide a reproducible case. If you can't, then this isn't the right forum to receive help. – Peter Duniho Mar 25 '15 at 13:57
  • _"I do store the information every time on these lines"_ -- no, not really. The fact that you don't use the return value of `EndReceive()` means that it is _impossible_ for the rest of your code to work correctly. A correct implementation of TCP will not lose "packets", period. If you think that's happening, then either you're dealing an incorrect implementation of TCP (i.e. in "the equipment"), or you are simply failing to recognize the combined "packets" in your received data. I find the latter a much more plausible scenario than the former. – Peter Duniho Mar 25 '15 at 13:57
  • The return value of EndReceive is the number of bytes written into the buffer. I don't use the offset to continue writing where it left off, so it writes again starting from 0. Each time it reaches EndReceive it converts everything in the buffer to a string and saves it into a queue. Do you mean to say my conversion is faulty and needs to take into account the number of bytes so it actually converts the whole buffer? (see original post) I debuged it once checking the amount of bytes read, it never exceeds my buffer size (not sure it can). I've even increased the buffer size and it won't help. – Alex Mar 25 '15 at 16:07
  • "return value of EndReceive is the number of bytes written" -- by the receive operation, yes. Note that this number can be anything between 1 and the length of the buffer; if you don't take into account the return value, and instead assume that the entire buffer is full of valid data, then you will get incorrect results. As I've written, without a good code example, it's not possible to say _exactly_ what's wrong. But failing to take into account the return value of `EndReceive()` does _guarantee_ that your code is wrong. – Peter Duniho Mar 25 '15 at 17:10