studio scanner para libreria how example devices connectedthread java networking sockets apple-push-notifications

java - scanner - listview bluetooth android example



Java Sockets y Dropped Connections (3)

Después de muchos problemas con conexiones caídas, moví mi código para usar el formato mejorado , lo que significa que cambias tu paquete para que se vea así:

De esta forma, Apple no soltará una conexión si ocurre un error, sino que escribirá un código de retroalimentación en el socket.

¿Cuál es la forma más adecuada de detectar si un socket se ha caído o no? O si un paquete realmente fue enviado?

Tengo una biblioteca para enviar notificaciones de Apple Push a iPhones a través de Apple Gatways ( disponible en GitHub ). Los clientes necesitan abrir un socket y enviar una representación binaria de cada mensaje; pero lamentablemente Apple no devuelve ningún reconocimiento en absoluto. La conexión se puede reutilizar para enviar mensajes múltiples también. Estoy usando las conexiones simples de Java Socket. El código relevante es:

Socket socket = socket(); // returns an reused open socket, or a new one socket.getOutputStream().write(m.marshall()); socket.getOutputStream().flush(); logger.debug("Message /"{}/" sent", m);

En algunos casos, si se corta una conexión mientras se envía un mensaje o justo antes; Socket.getOutputStream().write() termina con éxito. Espero que se deba a que la ventana de TCP todavía no está agotada.

¿Hay alguna manera de saber con certeza si un paquete realmente ingresó en la red o no? Experimenté con las siguientes dos soluciones:

  1. Inserte una socket.getInputStream().read() adicional de socket.getInputStream().read() con un tiempo de espera de 250 ms. Esto obliga a una operación de lectura que falla cuando se desconectó la conexión, pero se bloquea de otra manera por 250 ms.

  2. configure el tamaño del búfer de envío de TCP (por ejemplo, Socket.setSendBufferSize() ) al tamaño binario del mensaje.

Ambos métodos funcionan, pero degradan significativamente la calidad del servicio; el rendimiento va de 100 mensajes / segundo a aproximadamente 10 mensajes / segundo como máximo.

¿Alguna sugerencia?

ACTUALIZAR:

Desafiado por respuestas múltiples cuestionando la posibilidad de lo descrito. Construí pruebas de "unidad" del comportamiento que estoy describiendo. Vea las cajas de unidades en Gist 273786 .

Ambas pruebas unitarias tienen dos hilos, un servidor y un cliente. El servidor se cierra mientras el cliente está enviando datos sin una IOException lanzada de todos modos. Aquí está el método principal:

public static void main(String[] args) throws Throwable { final int PORT = 8005; final int FIRST_BUF_SIZE = 5; final Throwable[] errors = new Throwable[1]; final Semaphore serverClosing = new Semaphore(0); final Semaphore messageFlushed = new Semaphore(0); class ServerThread extends Thread { public void run() { try { ServerSocket ssocket = new ServerSocket(PORT); Socket socket = ssocket.accept(); InputStream s = socket.getInputStream(); s.read(new byte[FIRST_BUF_SIZE]); messageFlushed.acquire(); socket.close(); ssocket.close(); System.out.println("Closed socket"); serverClosing.release(); } catch (Throwable e) { errors[0] = e; } } } class ClientThread extends Thread { public void run() { try { Socket socket = new Socket("localhost", PORT); OutputStream st = socket.getOutputStream(); st.write(new byte[FIRST_BUF_SIZE]); st.flush(); messageFlushed.release(); serverClosing.acquire(1); System.out.println("writing new packets"); // sending more packets while server already // closed connection st.write(32); st.flush(); st.close(); System.out.println("Sent"); } catch (Throwable e) { errors[0] = e; } } } Thread thread1 = new ServerThread(); Thread thread2 = new ClientThread(); thread1.start(); thread2.start(); thread1.join(); thread2.join(); if (errors[0] != null) throw errors[0]; System.out.println("Run without any errors"); }

[Por cierto, también tengo una biblioteca de pruebas de concurrencia, que hace que la configuración sea un poco mejor y más clara. Verifique la muestra en la esencia también].

Cuando se ejecuta obtengo el siguiente resultado:

Closed socket writing new packets Finished writing Run without any errors


Esto no será de mucha ayuda para usted, pero técnicamente ambas soluciones propuestas son incorrectas. OutputStream.flush () y cualquier otra llamada API que se te ocurra, no harán lo que necesites.

La única manera portátil y confiable de determinar si un paquete ha sido recibido por el par es esperar la confirmación del par. Esta confirmación puede ser una respuesta real o un cierre de socket elegante. Fin de la historia: realmente no hay otra manera, y esto no es específico de Java, es una programación de red fundamental.

Si no se trata de una conexión persistente, es decir, si solo envía algo y luego cierra la conexión, la forma en que lo hace es atrapar todas las excepciones IO (cualquiera de ellas indica un error) y realiza un apagado de socket correcto:

1. socket.shutdownOutput(); 2. wait for inputStream.read() to return -1, indicating the peer has also shutdown its socket


Si está enviando información utilizando el protocolo TCP / IP a apple, debe recibir acuses de recibo. Sin embargo, usted declaró:

Apple no devuelve ningún reconocimiento en absoluto

¿Qué quiere decir con esto? TCP / IP garantiza la entrega, por lo que el receptor DEBE acusar recibo. Sin embargo, no garantiza cuándo se realizará la entrega.

Si envía una notificación a Apple y rompe su conexión antes de recibir el ACK, no hay forma de saber si tuvo éxito o no, así que simplemente debe enviarlo nuevamente. Si presionar la misma información dos veces es un problema o el dispositivo no lo maneja correctamente, entonces hay un problema. La solución es arreglar el manejo del dispositivo de la notificación de inserción duplicada: no hay nada que puedas hacer en el lado de empuje.

@ Clarificación / Pregunta de Comentario

De acuerdo. La primera parte de lo que entiendes es tu respuesta a la segunda parte. Solo los paquetes que han recibido ACKS han sido enviados y recibidos correctamente. Estoy seguro de que podríamos pensar en un esquema muy complicado de hacer un seguimiento de cada paquete individual, pero se supone que TCP debe abstraer esta capa y manejarla por usted. Por su parte, simplemente tiene que lidiar con la multitud de fallas que podrían ocurrir (en Java si ocurre alguna de estas excepciones se genera una excepción). Si no hay excepciones, los datos que acaba de tratar de enviar se envían garantizados por el protocolo TCP / IP.

¿Hay una situación en la que los datos aparentemente "se envían" pero no se garantiza que se reciban cuando no se produce ninguna excepción? La respuesta debería ser no.

@Ejemplos

Buenos ejemplos, esto aclara bastante las cosas. Pensé que se lanzaría un error. En el ejemplo publicado, se genera un error en la segunda escritura, pero no en la primera. Este es un comportamiento interesante ... y no pude encontrar mucha información explicando por qué se comporta así. Sin embargo, explica por qué debemos desarrollar nuestros propios protocolos de nivel de aplicación para verificar la entrega.

Parece que tiene razón en que, sin un protocolo de confirmación, no hay garantía de que el dispositivo Apple reciba la notificación. Apple también solo pone en cola el último mensaje. Al mirar un poco el servicio, pude determinar que este servicio es más conveniente para el cliente, pero no se puede usar para garantizar el servicio y se debe combinar con otros métodos. Lo leí de la siguiente fuente.

http://blog.boxedice.com/2009/07/10/how-to-build-an-apple-push-notification-provider-server-tutorial/

Parece que la respuesta es no sobre si puedes o no estar seguro. Es posible que pueda utilizar un detector de paquetes como Wireshark para saber si se envió, pero esto aún no garantiza que se haya recibido y enviado al dispositivo debido a la naturaleza del servicio.