Pérdida de datos TCP IP C#
networking serialization (2)
¿Cómo me aseguro de que se hayan recibido todos mis datos antes de intentar construirlos y hacer algo con ellos?
Tienes que implementar algún protocolo para que lo sepas.
Si bien TCP es confiable, no garantiza que los datos de escritura única en un extremo del socket aparezcan como una sola lectura en el otro extremo: los reintentos, la fragmentación de paquetes y la MTU pueden llevar a que los datos sean recibidos en unidades de diferentes tamaños por El receptor. Obtendrá los datos en el orden correcto.
Por lo tanto, debe incluir cierta información al enviar que le permita al receptor saber cuándo tiene el mensaje completo. También recomendaría incluir qué tipo de mensaje y qué versión de los datos (esto formará la base de poder admitir diferentes versiones de cliente y servidor).
Entonces el remitente envía: - Tipo de mensaje - Versión del mensaje - Tamaño del mensaje (en bytes)
Y el receptor realizará un bucle, realizando una lectura con un búfer y anexándolo a un búfer maestro ( MemoryStream
es bueno para esto). Una vez que se recibe el encabezado completo, sabe cuándo se han recibido los datos completos.
(Otra ruta es incluir algún patrón como un marcador de "fin de mensaje", pero luego debe manejar la misma secuencia de bytes que se produce en el contenido; es difícil de hacer si los datos son binarios en lugar de texto).
Aquí está mi código:
private void OnReceive(IAsyncResult result)
{
NetStateObject state = (NetStateObject)result.AsyncState;
Socket client = state.Socket;
int size = client.EndReceive(result);
byte[] data = state.Buffer;
object data = null;
using (MemoryStream stream = new MemoryStream(data))
{
BinaryFormatter formatter = new BinaryFormatter();
data = formatter.Deserialize(stream);
}
//todo: something with data
client.BeginReceive(
state.Buffer,
0,
NetStateObject.BUFFER_SIZE,
SocketFlags.None,
OnReceive,
state
);
}
state.Buffer tiene un tamaño máximo de NetStateObject.BUFFER_SIZE (1024). En primer lugar, ¿es esto demasiado grande o demasiado pequeño? En segundo lugar, si envío algo más grande que eso, mi deserialización se arruina porque el objeto que intenta deserializar no tiene toda la información (porque no se enviaron todos los datos). ¿Cómo me aseguro de que se hayan recibido todos mis datos antes de intentar construirlos y hacer algo con ellos?
Código de trabajo completado
private void OnReceive(IAsyncResult result)
{
NetStateObject state = (NetStateObject)result.AsyncState;
Socket client = state.Socket;
try
{
//get the read data and see how many bytes we received
int bytesRead = client.EndReceive(result);
//store the data from the buffer
byte[] dataReceived = state.Buffer;
//this will hold the byte data for the number of bytes being received
byte[] totalBytesData = new byte[4];
//load the number byte data from the data received
for (int i = 0; i < 4; i++)
{
totalBytesData[i] = dataReceived[i];
}
//convert the number byte data to a numan readable integer
int totalBytes = BitConverter.ToInt32(totalBytesData, 0);
//create a new array with the length of the total bytes being received
byte[] data = new byte[totalBytes];
//load what is in the buffer into the data[]
for (int i = 0; i < bytesRead - 4; i++)
{
data[i] = state.Buffer[i + 4];
}
//receive packets from the connection until the number of bytes read is no longer less than we need
while (bytesRead < totalBytes + 4)
{
bytesRead += state.Socket.Receive(data, bytesRead - 4, totalBytes + 4 - bytesRead, SocketFlags.None);
}
CommandData commandData;
using (MemoryStream stream = new MemoryStream(data))
{
BinaryFormatter formatter = new BinaryFormatter();
commandData = (CommandData)formatter.Deserialize(stream);
}
ReceivedCommands.Enqueue(commandData);
client.BeginReceive(
state.Buffer,
0,
NetStateObject.BUFFER_SIZE,
SocketFlags.None,
OnReceive,
state
);
dataReceived = null;
totalBytesData = null;
data = null;
}
catch(Exception e)
{
Console.WriteLine("***********************");
Console.WriteLine(e.Source);
Console.WriteLine("***********************");
Console.WriteLine(e.Message);
Console.WriteLine("***********************");
Console.WriteLine(e.InnerException);
Console.WriteLine("***********************");
Console.WriteLine(e.StackTrace);
}
}
TCP es un protocolo de transmisión. No tiene concepto de paquetes. Se puede enviar una sola llamada de escritura en varios paquetes, y se pueden poner varias llamadas de escritura en el mismo paquete. Por lo tanto, debe implementar su propia lógica de paquetización sobre TCP.
Hay dos formas comunes de paquetizar:
- Los caracteres del delimitador, esto se usa generalmente en los protocolos de texto, siendo la nueva línea una opción común
- Prefijo la longitud de cada paquete, generalmente una buena opción con los protocolos binarios.
Almacena el tamaño de un paquete lógico al comienzo de ese paquete. Luego, lee hasta que recibe suficientes bytes para completar el paquete y comienza a deserializar.