java - para - manual de programacion android pdf
Lectura de bloqueo de Java InputStream (8)
De acuerdo con Java api, InputStream.read()
se describe como:
Si no hay ningún byte disponible porque se ha alcanzado el final de la secuencia, se devuelve el valor -1. Este método bloquea hasta que los datos de entrada estén disponibles, se detecte el final de la secuencia o se genere una excepción.
Tengo un ciclo while(true)
haciendo una lectura y siempre obtengo -1 cuando no se envía nada a través de la transmisión. Eso es esperado.
Mi pregunta es cuándo leería () alguna vez bloquearía? Dado que si no obtiene ningún dato, devuelve -1. Esperaría una lectura de bloqueo para esperar hasta que se reciban los datos. Si ha llegado al final de la secuencia de entrada, no debería leer () simplemente espere los datos en lugar de devolver -1?
¿O solo lee () bloquear si hay otro subproceso que accede a la secuencia y su lectura () no puede acceder a la secuencia?
Lo que me lleva a mi siguiente pregunta. Solía tener un receptor de eventos (provisto por mi biblioteca) que me avisaba cuando había datos disponibles. Cuando recibí la notificación, llamaría while((aByte = read()) > -1)
almacenando el byte. Estaba desconcertado cuando obtenía DOS eventos en proximidad de tiempo muy cercano y no se mostraban todos mis datos. Parecía que solo se mostraría el final de los datos del segundo evento y el resto faltaba.
Eventualmente cambié mi código para que cuando obtuviera un evento llamara a if(inputStream.available() > 0) while((aByte = read()) > -1)
almacenara el byte. Ahora funcionó correctamente y se mostraron todos mis datos.
¿Alguien puede explicar este comportamiento? Se dice que InputStream.available()
devuelve el número de bytes que puede leer antes de bloquear al siguiente interlocutor (¿de la transmisión?). Incluso si no utilizo .available (), esperaría que la lectura del primer evento simplemente bloqueara la lectura del segundo evento, pero no borre ni consuma demasiados datos de transmisión. ¿Por qué hacer esto provocaría que no se muestren todos mis datos?
¡Sí! No te rindas en tu transmisión aún Jbu. Estamos hablando de comunicación serial aquí. Para las cosas en serie, se espera absolutamente que se pueda / devuelva -1 en las lecturas, y aún así se esperan datos en un momento posterior. El problema es que la mayoría de la gente está acostumbrada a tratar con TCP / IP, que siempre debe devolver un 0 a menos que TCP / IP se desconecte ... entonces sí, -1 tiene sentido. Sin embargo, con Serial no hay flujo de datos durante períodos prolongados de tiempo, ni "HTTP Keep Alive", ni latido TCP / IP, ni (en la mayoría de los casos) ningún control de flujo de hardware. Pero el enlace es físico, y todavía está conectado por "cobre" y sigue perfectamente en vivo.
Ahora, si lo que dicen es correcto, es decir: Serial debería estar cerrado en -1, entonces ¿por qué tenemos que mirar cosas como OnCTS, pmCarroerDetect, onDSR, onRingIndicator, etc.? Heck, si 0 significa que está ahí , y -1 significa que no, ¡entonces atornille todas esas funciones de detección! :-)
El problema que puedes enfrentar puede estar en otra parte.
Ahora, en detalles:
P: "Parecía que solo se mostraría el final de los datos del segundo evento y el resto faltaba".
R: Voy a adivinar que estaba en un bucle, reutilizando el mismo búfer de byte []. El primer mensaje entra, todavía no se muestra en la pantalla / log / std (porque está en el bucle), luego lee el segundo mensaje, reemplazando los datos del primer mensaje en el búfer. Una vez más, porque voy a adivinar que no almacena cuánto ha leído, y luego me aseguré de compensar el buffer de la tienda por la cantidad de lectura anterior.
P: "Finalmente cambié mi código para que cuando recibo un evento llame a if (inputStream.available ()> 0) while ((aByte = read ())> -1) almacene el byte."
R: Bravo ... eso es lo bueno que hay. Ahora, tu búfer de datos está dentro de una declaración IF, tu segundo mensaje no afectará tu 1er ... bueno, de hecho, probablemente fue solo un mensaje grande (er) en el 1er lugar. Pero ahora, lo leerá todo de una vez, manteniendo la información intacta.
C: "... condición de carrera ..."
A: ¡Ahhh, el bueno de atrapar a todos los chivos expiatorios! La condición de carrera ... :-) Sí, esto puede haber sido una condición de carrera, de hecho puede haber sido así. Pero, también podría ser simplemente la forma en que el RXTX borra la bandera. La eliminación de la ''bandera de datos disponibles'' puede no ocurrir tan rápido como uno espera. Por ejemplo, ¿alguien sabe la diferencia entre leer VS readLine en relación con borrar el búfer en el que se almacenaron previamente los datos y volver a establecer el indicador de eventos? Yo tampoco. :-) Tampoco puedo encontrar la respuesta todavía ... pero ... permítanme divagar un par de frases más. La programación impulsada por eventos todavía tiene algunos defectos. Déjame darte un ejemplo del mundo real con el que tuve que lidiar recientemente.
- Obtuve algunos datos TCP / IP, digamos, 20 bytes.
- Entonces recibo el evento OnEvent para los datos recibidos.
- Comienzo mi ''lectura'' incluso en los 20 bytes.
- Antes de terminar de leer mis 20 bytes ... obtengo otros 10 bytes.
- El TCP / IP sin embargo, parece avisarme, oh, ve que la bandera todavía está ESTABLECIDA, y no me notificará de nuevo.
- Sin embargo, termino de leer mis 20 bytes (disponible () dijo que había 20) ...
- ... y los últimos 10 bytes permanecen en la Q de TCP / IP ... porque no me notificaron sobre ellos.
Mira, la notificación se perdió porque la bandera todavía estaba activada ... a pesar de que había comenzado a leer los bytes. Si hubiera terminado los bytes, entonces la bandera se habría borrado, y habría recibido una notificación para los siguientes 10 bytes.
El opuesto exacto de lo que está sucediendo para ti ahora.
Así que sí, ve con un IF disponible () ... lee la longitud de datos devuelta. Luego, si está paranoico, configure un temporizador y llame de nuevo a disponible (), si todavía hay datos allí, luego haga una lectura no los datos nuevos. Si está disponible () devuelve 0 (o -1), entonces relájese ... siéntese ... y espere la próxima notificación de OnEvent.
Creo que puedes recibir toda la secuencia de datos si usas thread.sleep ()
Devuelve -1 si es el final de la secuencia. Si el flujo todavía está abierto (es decir, la conexión del socket) pero no ha llegado ningún dato al lado de la lectura (el servidor es lento, las redes son lentas, ...) los bloques de lectura ().
No necesita llamar disponible (). Me cuesta entender su diseño de notificaciones, pero no necesita ninguna llamada, excepto read (). El método disponible () está disponible solo por conveniencia.
Estoy usando eclipse Jetty 9.2.2. Me sorprende ver que el 10-12% de mi pedido demora más de 500+ milisegundos cada uno, mientras que otros se procesan en 2-3 milisegundos cada uno. porción del código que toma
timeBufferedReader br = new BufferedReader(new InputStreamReader(request.getInputStream()));
String line = null;
String postData = "";
while ((line = br.readLine()) != null){ // this is the line taking all time
postData+=line;
}
br.close();
La fuente de datos subyacente para algunas implementaciones de InputStream
puede indicar que se ha llegado al final de la transmisión y que no se enviarán más datos. Hasta que se reciba esta señal, las operaciones de lectura en dicha secuencia pueden bloquearse.
Por ejemplo, un InputStream
desde un socket Socket
bloqueará, en lugar de devolver EOF, hasta que se reciba un paquete TCP con el indicador FIN. Cuando se recibe EOF de dicho flujo, puede estar seguro de que todos los datos enviados en ese socket se han recibido de manera confiable, y no podrá leer más datos. (Si una lectura de bloqueo da como resultado una excepción, por otro lado, algunos datos pueden haberse perdido).
Otras secuencias, como las de un archivo en bruto o puerto serie, pueden carecer de un formato o protocolo similar para indicar que no habrá más datos disponibles. Tales flujos pueden devolver EOF (-1) inmediatamente en lugar de bloquear cuando no hay datos disponibles actualmente. Sin embargo, en ausencia de dicho formato o protocolo, no puede estar seguro de cuándo el otro lado está listo para enviar datos.
Con respecto a su segunda pregunta, parece que usted tuvo una condición de carrera. Sin ver el código en cuestión, supongo que el problema yace en su método de "visualización". Tal vez el intento de mostrarlo en la segunda notificación fue de alguna manera obstaculizar el trabajo realizado durante la primera notificación.
OK, esto es un poco complicado, así que lo primero que debemos aclarar es que el bloqueo InputStream.read () no tiene nada que ver con el multi-threading. Si tiene varios hilos que leen desde la misma secuencia de entrada y desencadena dos eventos muy cercanos entre sí, donde cada hilo intenta consumir un evento, entonces obtendrá corrupción: el primer hilo para leer obtendrá algunos bytes (posiblemente todos los bytes) y cuando se programa el segundo hilo, leerá el resto de los bytes. Si planea usar una sola secuencia de E / S en más de un hilo, siempre sincronizada en alguna restricción externa.
En segundo lugar, si puede leer desde su InputStream hasta que obtenga -1 y luego espere y pueda volver a leer más tarde, ¡entonces la implementación de InputStream que está utilizando está rota! El contrato de InputStream establece claramente que un InputStream.read () solo debe devolver -1 cuando ya no hay más datos para leer porque se ha llegado al final de la secuencia completa y NUNCA más datos estarán disponibles, como cuando lee de un archivo y llegas al final.
El comportamiento de "no más datos está disponible ahora, espere y obtendrá más" es para que read () bloquee y no vuelva hasta que haya algunos datos disponibles (o se produzca una excepción).
Por defecto, el comportamiento del RXTX InputStream proporcionado no es compatible.
Debe establecer el umbral de recepción en 1 y desactivar el tiempo de espera de recepción:
serialPort.enableReceiveThreshold(1);
serialPort.disableReceiveTimeout();
Fuente: conexión en serie RXTX: problema con el bloqueo de lectura ()
InputStream
es solo una clase abstracta , desafortunadamente la implementación decide qué sucede.
¿Qué sucede si no se encuentra nada?
Los zócalos (es decir,
SocketInputStream
) se bloquearán hasta que se reciban los datos (de forma predeterminada). Pero es posible establecer un tiempo de espera (ver:setSoTimeout
), luego laread
se bloqueará para x ms. Si todavía no se recibe nada, seSocketTimeoutException
unaSocketTimeoutException
.Pero con o sin tiempo de espera, la lectura de un
SocketInputStream
veces puede dar como resultado-1
. ( Por ejemplo, cuando varios clientes se conectan simultáneamente al mismohost:port
, aunque los dispositivos parezcan conectados, el resultado de unaread
podría volver a aparecer inmediatamente en-1
(nunca devolver datos)).La comunicación serial siempre devolverá
-1
; También puede establecer un tiempo de espera (usesetTimeoutRx
), laread
se bloqueará primero para x ms, pero el resultado seguirá siendo-1
si no se encuentra nada. (Observación: pero hay múltiples clases seriales disponibles, el comportamiento puede depender del proveedor).Los archivos (lectores o secuencias) darán como resultado una
EOFException
.
Trabajar para una solución genérica:
- Si ajusta cualquiera de las transmisiones anteriores en un
DataInputStream
, puede usar métodos comoreadByte
,readChar
, etc. Todos los valores-1
se convierten enEOFException
. (PD: si realiza muchas lecturas pequeñas, entonces es una buena idea envolverlo en unBufferedInputStream
primero) - Tanto
SocketTimeoutException
comoEOFException
extiendenIOException
, y hay varias otras posiblesIOException
. Es conveniente verificar siIOException
detecta problemas de comunicación.
Otro tema delicado es enrojecer. flush
en términos de "sockets" significa "enviarlo ahora", pero en términos de Serialio significa "descartar el buffer".