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()
//*******************************************************************
}
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: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:
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.