que - sockets en java ejemplos
¿Qué significa ''final de flujo'' cuando se trabaja con sockets? (4)
Cuando trabaje con Sockets en Java, ¿cómo puede saber si el cliente ha terminado de enviar todos los datos (binarios) antes de que pueda comenzar a procesarlos? Considere por ejemplo:
istream = new BufferedInputStream (socket.getInputStream());
ostream = new BufferedOutputStream(socket.getOutputStream());
byte[] buffer = new byte[BUFFER_SIZE];
int count;
while(istream.available() > 0 && (count = istream.read(buffer)) != -1)
{
// do something..
}
// assuming all input has been read
ostream.write(getResponse());
ostream.flush();
He leído publicaciones similares en SO como this , pero no pude encontrar una respuesta concluyente. Si bien mi solución anterior funciona, mi entendimiento es que nunca se puede saber si el cliente ha terminado de enviar todos los datos. Si, por ejemplo, el socket del cliente envía algunos fragmentos de datos y luego bloquea la espera de datos de otra fuente de datos antes de que pueda enviar más datos, el código anterior puede suponer que el cliente ha terminado de enviar todos los datos desde istream.available () devolverá 0 para el flujo actual de bytes.
Como algunas personas ya han dicho, no se puede evitar algún tipo de protocolo para la comunicación. Debería verse así por ejemplo:
En el lado del servidor tiene:
void sendMSG(PrintWriter out){
try {
//just for example..
Process p = Runtime.getRuntime().exec("cmd /c dir C:");
BufferedReader br = new BufferedReader(new InputStreamReader(
p.getInputStream()));
//and then send all this crap to the client
String s = "";
while ((s = br.readLine()) != null) {
out.println("MSG");
out.println(s);
}
} catch (Exception e) {
System.out.println("Command incorrect!");
}
out.println("END");
}
//You are not supposed to close the stream or the socket, because you might want to send smth else later..
En el lado del cliente usted tiene:
void recieveMSG(BufferedReader in) {
try {
while (in.readLine().equals("MSG")) {
System.out.println(in.readLine());
}
} catch (IOException e) {
System.out.println("Connection closed!");
}
}
Como dijo Nikita , esto es más de una tarea de protocolo. Puede ir por el encabezado y el enfoque del cuerpo o puede enviar un carácter especial o un símbolo para que el final del flujo interrumpa el ciclo de procesamiento. Algo así como si envías decir "[[FIN]] ''en el socket para indicar el final de la secuencia.
Creo que esta es la tarea más de un protocolo, asumiendo que usted es el hombre que escribe tanto el lado de transmisión como el de recepción de la aplicación. Por ejemplo, podría implementar algún protocolo lógico simple y dividir sus datos en paquetes. Luego divide los paquetes en dos partes: la cabeza y el cuerpo. Y luego decir que su cabeza consiste en una secuencia de inicio predefinida y contiene el número de bytes en el cuerpo. Olvídese de iniciar la secuencia y el número de transferencia simple de bytes en el bofy como primer byte del paquete. Entonces has podido solucionar tu problema.
Sí, tienes razón, usar available()
como esto no es confiable. Personalmente muy raramente utilizo available()
. Si desea leer hasta que llegue al final de la transmisión (según el título de la pregunta), siga llamando a read()
hasta que devuelva -1. Eso es lo fácil. Lo difícil es si no quieres el final de la transmisión, sino el final de "lo que el servidor quiere enviarte en este momento".
Como han dicho los demás, si necesita tener una conversación a través de un socket, debe hacer que el protocolo explique dónde terminan los datos. Personalmente prefiero la solución de "prefijo de longitud" a la solución de "token de fin de mensaje" siempre que sea posible: generalmente hace que el código de lectura sea mucho más simple. Sin embargo, puede hacer que el código de escritura sea más difícil, ya que necesita calcular la longitud antes de enviar algo. Esto es un dolor si podría estar enviando una gran cantidad de datos.
Por supuesto, puede mezclar y combinar soluciones; en particular, si su protocolo trata con datos binarios y de texto, recomendaría encarecidamente cadenas de prefijo de longitud en lugar de terminaciones nulas (o algo similar). La decodificación de datos de cadenas tiende a ser mucho más fácil si puede pasar al decodificador una serie completa de bytes y solo recuperar una cadena; por ejemplo, no tiene que preocuparse por leer hasta la mitad de un carácter. Puede usar esto como parte de su protocolo pero aún así tener "registros" generales (o lo que esté transmitiendo) con un registro de "fin de datos" para permitir que el lector procese los datos y responda.
Por supuesto, todo este diseño de protocolo es discutible si no tienes el control del protocolo :(