2011-06-04 8 views
6

Estoy desarrollando un pequeño programa cliente-servidor en Java.Java - redes - Mejores prácticas - Comandos síncronos/asíncronos mixtos

El cliente y el servidor están conectados a través de una conexión tcp. La mayoría de las partes de la comunicación son asíncronas (pueden suceder en cualquier momento) pero algunas partes quiero que sean sincrónicas (como ACK para un comando enviado).

Utilizo un subproceso que lee los comandos del InputStream del socket y genera un evento onCommand(). El Command en sí mismo es progresado por Command-Design-Pattern.

¿Cuál sería un enfoque de mejores prácticas (Java), para permitir la espera de un ACK sin perder otro, comandos que podrían aparecer al mismo tiempo?

con.sendPacket(new Packet("ABC")); 
// wait for ABC_ACK 

edit1

Piense en ello como un FTP-conexión, pero que tanto los datos de control y comandos están en la misma conexión. Quiero ver la respuesta a un comando de control, mientras se ejecuta el flujo de datos en segundo plano.

Edit2

Todo se envía en bloques para permitir múltiples (diferentes) transmissons más de la misma red TCP-Conexión (multiplexación)

Block: 
1 byte - block's type 
2 byte - block's payload length 
n byte - block's paylod 
+0

así que después de haber enviado el paquete, ¿desea el método para ** bloquear ** hasta que reciba el paquete ACK? – khellang

+0

quieres enviar "ABC" y esperar el ack, pero también ser capaz de enviar "EFG" sin tener que retrasar debido al ABC_ACK? – Cratylus

+0

sí a ambos.tengo comandos de control (como ABC) y flujo de datos en la misma conexión. el hilo de lectura los lee a ambos. entonces mi problema es cómo "entregar" el ABC_ACK del hilo de lectura al método (de bloqueo) que envió el ABC. – kazu

Respuesta

4

En principio, es necesario un registro de hilos bloqueados (o mejor, los bloqueos en los que están esperando), tecleados con algún identificador que será enviado por el lado remoto.

Para la operación asincrónica, simplemente envió el mensaje y continúa.

Para el funcionamiento síncrono, después de enviar el mensaje, su hilo de envío (o el hilo que inició esto) crea un objeto de bloqueo, lo agrega con alguna clave al registro y luego espera hasta que se notifique.

El hilo de lectura, cuando recibe alguna respuesta, busca en el registro el objeto de bloqueo, le agrega la respuesta y llama al notify(). Luego lee la siguiente entrada.

El trabajo duro aquí es la sincronización adecuada para evitar bloqueos muertos así como la pérdida de una notificación (porque vuelve antes de que nos agreguemos al registro).

Hice algo así cuando implementé el protocolo de llamada a método remoto para nuestra Fencing -applet. En principio, RMI funciona de la misma manera, simplemente sin los mensajes asincrónicos.

+0

Así que haría algo como: 'reader.waitForCmd (" ABC_ACK ")' que internamente agrega algo a una lista, espera y es notificado (reanudado) cuando el ABC_ACK llegó a través del hilo de lectura? – kazu

+0

Sí, alguien como este. Sin embargo, usaría un mapa en lugar de una lista (para permitir la recuperación eficiente del bloqueo correcto incluso si hay muchos hilos esperando). –

+0

funciona como un amuleto, gracias :) – kazu

1

@ La solución de Paulo es una que he usado antes. Sin embargo, puede haber una solución más simple.

Supongamos que no tiene una lectura de hilo de fondo como resultado de la conexión. Lo que puede hacer en su lugar es usar el hilo actual para leer cualquier resultado.

// Asynchronous call 
conn.sendMessage("Async-request"); 
// server sends no reply. 

// Synchronous call. 
conn.sendMessage("Sync-request"); 
String reply = conn.readMessage(); 
+0

Sí, utilicé su enfoque antes, pero esto solo funciona si sé qué paquete aparecerá a continuación. si hay un flujo de datos de fondo, el próximo paquete podría ser un paquete de datos y no el ACK que estoy esperando. ese es mi problema. el hilo de fondo maneja principalmente los paquetes de datos, ya que esto es fácil y no necesita ser sincronizado. – kazu

+0

Utilizaría una conexión separada para el flujo de datos en segundo plano, si tiene alguno. La otra opción es hacer que readMessage espere hasta que lea un requestId coincidente. Puede hacer que los mensajes de envío también verifiquen que se lean los datos. Esto solo es útil si se comprueba periódicamente la transmisión y no se usa bloqueo IO. –

+0

@PeterLawrey Como se refirió a la respuesta de Paulo (y parece que lo hace de manera similar), también me gustaría saber su opinión sobre mis comentarios a su respuesta :) – cobby