2009-02-25 10 views
6

Esto es una especie de rama de mi other question. Léelo si quieres, pero no es necesario.¿Cómo detectar cuándo se recibe un mensaje de buffer de protocolo?

Básicamente, me di cuenta de que para utilizar efectivamente BeginReceive() de C# en mensajes grandes, necesito (a) leer primero la longitud del paquete, luego leer exactamente tantos bytes o (b) usar un final de - delimitador de paquetes. Mi pregunta es, ¿alguno de estos está presente en los buffers de protocolo? No los he usado aún, pero repasando la documentación no parece que haya un encabezado de longitud o un delimitador.

Si no, ¿qué debo hacer? ¿Debo simplemente construir el mensaje, prefijo/sufijo con el encabezado de longitud/delimitador EOP?

Respuesta

14

Debe incluir el tamaño o marcador final en su protocolo. No hay nada integrado en sockets basados ​​en flujo (TCP/IP) que no sea compatible con una secuencia indefinida de octetos divididos arbitrariamente en paquetes separados (y los paquetes también se pueden derramar en tránsito).

Un enfoque simple sería que cada "mensaje" tenga un encabezado de tamaño fijo, incluya una versión de protocolo y un tamaño de carga útil y cualquier otro dato fijo. Luego el contenido del mensaje (carga).

Opcionalmente se podría agregar un pie de página de mensaje (tamaño fijo) con una suma de comprobación o incluso una firma criptográfica (según sus requisitos de fiabilidad/seguridad).

Conocer el tamaño de la carga útil le permite seguir leyendo un número de bytes que será suficiente para el resto del mensaje (y si se completa con menos, haciendo otra lectura para los bytes restantes hasta que se reciba el mensaje completo))

Tener un indicador de mensaje final también funciona, pero hay que definir cómo manejar el mensaje que contiene la misma secuencia de octetos ...

1

TCP/IP, así como UDP, los paquetes incluyen alguna referencia a su tamaño . El IP header contiene un campo de 16 bits que especifica la longitud de los datos del encabezado IP y en bytes. El TCP header contiene un campo de 4 bits que especifica el tamaño del encabezado TCP en palabras de 32 bits. El UDP header contiene un campo de 16 bits que especifica la longitud de los datos del encabezado UDP y en bytes.

Aquí está la cosa.

Utilizando los sockets estándar de Windows, ya sea que esté utilizando el espacio de nombres System.Net.Sockets en C# o el contenido Winsock nativo en Win32, nunca verá los encabezados IP/TCP/UDP . Estos encabezados se eliminan de modo que lo que obtienes cuando lees el socket es la carga real, es decir, los datos que se enviaron.

El patrón típico de todo lo que he visto y hecho utilizando sockets es que define un encabezado de nivel de aplicación que precede a los datos que desea enviar. Como mínimo, este encabezado debe incluir el tamaño de los datos a seguir. Esto le permitirá leer cada "mensaje" en su totalidad sin tener que adivinar su tamaño. Puede obtener todo lo elegante que desee, por ejemplo, patrones de sincronización, CRC, versión, tipo de mensaje, etc., pero el tamaño del "mensaje" es todo lo que necesita .

Y por lo que vale, sugeriría utilizar un encabezado en lugar de un delimitador de fin de paquete. No estoy seguro de si hay una desventaja significativa para el delimitador de EOP, pero el encabezado es el enfoque utilizado por la mayoría de los protocolos de IP que he visto.Además, me parece más intuitivo procesar un mensaje desde el principio en lugar de esperar a que aparezca algún patrón en mi transmisión para indicar que mi mensaje está completo.

EDIT: Acabo de enterarme del proyecto Google Protocol Buffers. Por lo que puedo decir, es un esquema de serialización/deserialización binario para WCF (estoy seguro de que es una simplificación excesiva). Si está utilizando WCF, no tiene que preocuparse por el tamaño de los mensajes que se envían porque la plomería WCF se encarga de esto detrás de escena, que es probablemente la razón por la que no ha encontrado nada relacionado con la longitud del mensaje en el Protocolo Documentación de Buffers Sin embargo, en el caso de los enchufes, saber el tamaño ayudará enormemente como se discutió anteriormente. Supongo que serializará sus datos usando los Buffers de Protocolo y luego insertará cualquier encabezado de aplicación que se le ocurra antes de enviarlo. En el lado de recepción, sacará el encabezado y luego deserializará el resto del mensaje.

+0

Si se refiere a protobuf-net, no, no es solo para WCF; hay ejemplos de sockets en el proyecto. –

3

Estoy de acuerdo con Matt en que un encabezado es mejor que un pie de página para Protocol Buffers, por la razón principal de que, como PB es un protocolo binario, es problemático crear un pie de página que no sea una secuencia de mensajes válida. Muchos protocolos basados ​​en el pie de página (por lo general, los de EOL) funcionan porque el contenido del mensaje se encuentra en un rango definido (normalmente 0x20 - 0x7F ASCII).

Un enfoque útil es tener el código de nivel más bajo solo leer buffers fuera del socket y presentarlos a una capa de encuadre que ensambla mensajes completos y recuerda los parciales (presento un enfoque asíncrono a esto (usando el CCR) here, aunque para un protocolo de línea).

Para mayor coherencia, siempre puede definir su mensaje como un mensaje PB con tres campos: un fijo-int como la longitud, una enumeración como el tipo y una secuencia de bytes que contiene los datos reales. Esto mantiene su protocolo de red completo transparente.

6

Disculpas por llegar tarde a la fiesta. Soy el autor de protobuf-net, una de las implementaciones de C#. Para el uso de la red, debe considerar los métodos "[De] SerializeWithLengthPrefix"; de esta forma, manejará automáticamente las longitudes para usted. Hay ejemplos en la fuente.

No entraré en gran detalle en una publicación anterior, pero si quiere saber más, agregue un comentario y me pondré en contacto con usted.

Cuestiones relacionadas