c# - threadstart - ¿Cuál es la mejor manera de monitorear un socket para nuevos datos y luego procesar esos datos?
thread function c# (3)
Perdón por favor mi estado de novato de C # .Net. Si esto es obvio y lo extrañé de los documentos, un enlace a la página relevante o código de muestra sería apreciado.
Estoy trabajando en una aplicación que aceptará una conexión de socket TCP desde una aplicación Java. (Sí, se requiere Java en esa parte. Es un dispositivo Sun SPOT y Java es la única opción). La aplicación Java escribirá periódicamente nuevos datos en el socket, y el trabajo de mi aplicación es tomar el byte [], convertir a una cadena, procesar los datos (actualizar la IU, etc.) y posiblemente reenviar los datos a otra computadora con una aplicación C # .NET similar.
Esto es lo que hice hasta ahora: ahora la aplicación crea un hilo en el lanzamiento que abre un socket. La aplicación Java se puede conectar con éxito al socket para que funcione. Estaba mirando el método beginRead
de NetworkStream y las dataAvailable
, length
y CanRead
, pero no estoy seguro de cómo saber cuándo he leído un paquete de datos, generalmente unos 512 bytes, pero podría variar.
Si la aplicación Java escribe datos en la transmisión o hay una acumulación de datos (la aplicación Java pasará datos rápidamente) ¿cómo puedo asegurarme de que estoy leyendo solo un paquete de datos a la vez? Si la aplicación Java nula termina los datos cuando se escribe, ¿será de ayuda? ¿Es suficiente?
Por último, el socket solo recibirá una conexión, pero debo mantenerla abierta hasta que haya un error o la conexión finalice. ¿Cuál es la forma más elegante de manejar este aspecto? No creo que cerrar y volver a abrir con cada paquete de datos funcione debido al aspecto de disparo rápido (casi en tiempo real) de la aplicación Java que se ejecuta en la estación base Sun SPOT. En este momento, cuando la estación base finaliza, mi aplicación muere de forma ruidosa y dolorosa. :)
Gracias por leer y por cualquier ayuda que pueda ofrecer.
Cuando está leyendo datos de transmisión, necesita algún carácter centinela / de terminación o un tamaño conocido de los datos para determinar cuándo dejar de leer y procesar la información. Los caracteres nulos o las nuevas líneas son comunes. Otra técnica es usar un encabezado de tamaño fijo que especifica la longitud del cuerpo.
El socket permanecerá abierto hasta que lo cierre o el otro lado finalice, en cuyo caso se producirá un error al leer desde el socket, que debe manejar. NetworkStream arroja una IOException cuando el socket se ha cerrado desde el lado remoto durante una operación de lectura o escritura.
"Si la aplicación Java escribe datos en la transmisión o hay una acumulación de datos (la aplicación Java pasará los datos con bastante rapidez) ¿cómo puedo asegurarme de que estoy leyendo solo un paquete de datos a la vez?"
Tenga cuidado de no asumir que tiene control sobre qué datos terminan en cada paquete. Si intentas enviar los datos de bytes { ''H'', ''e'', ''l'', ''l'', ''o'' }
no hay garantía de que todos estos datos se envíen en un solo paquete. Si bien es muy poco probable, aún es posible que cada paquete contenga solo un byte, por lo que recibiría los cinco bytes en 5 eventos diferentes. El punto es que no confíes en los paquetes de esta manera. En su lugar, defina sus propios terminadores de fin de mensaje y simplemente agote todos los datos entrantes en un búfer de bytes de algún tipo y tenga otra función entrante detectando si hay alguno de estos terminadores presentes. Si es así, lee ese terminador. Por ejemplo, digamos que llama al método de envío respectivo de su aplicación Java dos veces que contiene los siguientes datos:
{ ''H'', ''e'', ''l'', ''l'', ''o'', ''/0'' }
{ ''W'', ''o'', ''r'', ''l'', ''d'', ''/0'' }
Cómo debe prepararse su aplicación para recibir los datos debería ser algo como esto:
Server receives { ''H'', ''e'', ''l'' }
Data stored in byte buffer { ''H'', ''e'', ''l'' }
Check byte buffer for message terminator ''/0''. None found. Buffer unchanged, no message processed.
Server receives { ''l'', ''o'', ''/0'', ''W'' }
Data stored in byte buffer { ''H'', ''e'', ''l'', ''l'', ''o'', ''/0'', ''W'' }
Check byte buffer for message terminator ''/0''. 1 found, extracted message { ''H'', ''e'', ''l'', ''l'', ''o'' } and buffer updated { ''W'' }
Entonces, aunque esa no fue exactamente una respuesta a su pregunta original, creo que debería darle un empujón en la dirección correcta.
Una cosa con la que se puede encontrar es que simplemente no hay caracteres que no puedan ser datos en lugar de terminadores de mensajes. Por ejemplo, muchos archivos contienen los datos / 0 por lo que podrían arruinar la detección de su mensaje. Cómo se maneja esto normalmente es creando una especificación de encabezado para su protocolo y detectando si está esperando un encabezado (en cuyo caso buscar / 0 indicará el final de un mensaje) o si está esperando una cierta cantidad de datos (que podrían ser especificados por el último encabezado recibido.) Si esto no tiene sentido y cree que podría necesitar usar esta técnica, hágamelo saber y lo agregaré a esta respuesta.
La forma en que hemos resuelto esto es tener una combinación de caracteres centinela y campos de ancho fijo. Los primeros dos bytes son un encabezado de longitud para todo el paquete. Luego, leo el paquete en un búfer byte [] y luego confío en nuestra propia estructura de paquete para saber que, por ejemplo, los dos primeros bytes se deben interpretar individualmente como campos y luego los campos de cadena terminan con el carácter / n (0x0A si está anotando en casa). Luego, los campos de datos largos se procesan leyendo en 8 bytes consecutivos, etc. Parece que funciona bastante bien para nosotros, pero obviamente es una solución para una situación en la que uno tiene control sobre ambos extremos del socket y no donde uno solo está capaz de controlar un extremo Espero que esto ayude a alguien más, también.