PDA

Ver la versión completa : Problema al leer de un socket



Txospo
26-02-2012, 15:36
El caso es que estoy haciendo una aplicación que se conecta a un Servidor FTP, en este caso a uno que he creado con Filezilla Server, y cuando quiero leer todo el contenido del socket hasta que este este vacío, este se queda como esperando en lugar de darse cuenta de que no hay datos. He estado leyendo y parece ser que el readLine() del BufferedReader es bloqueante y bloquea el socket. Quisiera saber que tengo que hacer. Coloco un ejemplo en java de lo que quiero hacer:



import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;

public class Cliente {
public static void main(String[] args) {
try{
Socket sControl = new Socket("192.168.1.14", 21);
System.out.println("Establecida conexión de control.");

PrintStream salidaControl = new PrintStream(sControl.getOutputStream(),true);
BufferedReader entradaControl = new BufferedReader(new InputStreamReader(sControl.getInputStream()));

System.out.println(entradaControl.readLine());

salidaControl.println("USER yo");
System.out.println(entradaControl.readLine());

salidaControl.println("PASS 4488");
System.out.println(entradaControl.readLine());

salidaControl.println("HELP");

String line;
while(true){
line = entradaControl.readLine(); //Aquí se queda esperando que le llegue algo para leer
if(line != null)
System.out.println(line);
else
break;
}

System.out.println("FUERA");

}catch (IOException e) {
System.out.println("Error en el cliente");
}
}
}



Espero que me puedan ayudar. Un saludo.

hystd
27-02-2012, 00:21
Por lo que cuentas, quieres convertir una comunicación síncrona en asíncrona para así evitar el bloqueo de readLine()... esto lo puedes conseguir reordenando un poco tu bucle, de la siguiente manera:



while (true){
while ((line=entradaControl.readLine()) != null){
System.out.println(line);
}
}


Pruébalo y comenta.

Un saludo.

Txospo
27-02-2012, 10:56
Si mi idea es esa, pero sigue igual que antes, se queda esperando leer algo.

hystd
27-02-2012, 19:19
Ok, pues entonces la forma más fácil es que te implementes una solución multihilo, haciendo uso de la clase Thread de Java, de tal forma que al llamarlo, se mantenga un segundo hilo realizando el bucle while(true). Así el hilo se mantendrá "bloqueado" a la espera de recibir un caracter, y el hilo asociado al main de tu proceso, quede liberado de esta tarea.

Un saludo.

Markitos1024
28-02-2012, 01:41
Aca te dejo una solucion:

cliente.class


import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;

public class cliente {
public static BufferedReader entradaControl;
public static void main(String[] args) {
try{
System.out.println("Iniciando conexión de control...");
Socket sControl = new Socket("192.168.1.14", 21);
System.out.println("Establecida conexión de control.");
PrintStream salidaControl = new PrintStream(sControl.getOutputStream(),true);
entradaControl = new BufferedReader(new InputStreamReader(sControl.getInputStream()));
pantalla p = new pantalla();
p.start();

salidaControl.println("USER yo");

salidaControl.println("PASS 4488");

salidaControl.println("HELP");
salidaControl.println("STAT");

// Si usas system out capaz se vean antes por el tema del sicro

}catch (IOException e) {
System.out.println("Error en el cliente");
}
}
}

pantalla.class


import java.io.IOException;

public class pantalla extends Thread
{
public void run()
{

while(true){
try {
System.out.println(cliente.entradaControl.readLine ());
} catch (IOException e) {
e.printStackTrace();
}

}

}
}

endpoint
28-02-2012, 17:12
Hola, estoy haciendo algo parecido y me surge una duda, ¿por qué en pantalla.class declara el metodo run y luego en el main se llama a p.start()? ¿no sería más logico llamar a p.run()?

chewarrior
28-02-2012, 17:35
Algun dia tendre que aprender algo de JAVA se ve todo tan limpio, que bonito.

hystd
28-02-2012, 20:21
Hola, estoy haciendo algo parecido y me surge una duda, ¿por qué en pantalla.class declara el metodo run y luego en el main se llama a p.start()? ¿no sería más logico llamar a p.run()?

Si llamas al método run() de la clase Thread de java, lo que harás será tener un hilo que se ejecuta secuencialmente, por lo tanto, el resultado final será una ejecución síncrona.

Llamando al método start() de esta misma clase, lo que haces es que el hilo se lance en paralelo, logrando por tanto una ejecución asíncrona.

En una clase que herede de Thread o implemente Runnable, sobreescribes el método run(), añadiendo la función que deseas ejecutar en paralelo. Al llamar a start(), éste ya se encarga de llamar a run(), para ejecutar esa función, por eso no es necesario sobreescribir start().



Algun dia tendre que aprender algo de JAVA se ve todo tan limpio, que bonito.

Apréndelo, pero por favor, no lo utilices si no es por obligación :sss. Hoy en dia existen miles de tecnologías, mucho más potentes que java, mucho más rápidas, eficientes y fáciles de aprender, entender y mantener.

Si Java se utiliza para un nuevo proyecto, entonces se puede garantizar, casi con certeza, que los desarrolladores implicados en ese proyecto, que hayan aceptado hacerlo en java, son reacios a aprender un nuevo lenguaje, porque piensan que con java se puede hacer todo... y claro, ¿para qué aprender algo nuevo si con lo que ya sé, puedo hacerlo igual?. Créeme, "igual" no!!! El tiempo de desarrollo que inviertes picando código java, lo inviertes aprendiendo otras tecnologías, y lo que en java son 10 - 20 líneas, probablemente en esa otra tecnología sea 1 sola línea... y no te exagero, 1 linea tal cual...

Otros piensan que si hacen software libre en java, tendrá más adeptos porque casi el 90% de los programadores, saben java y esta es en realidad una buena razón para utilizar java... aunque muy triste.

En la mayoría de los casos, se sigue utilizando java porque simplemente es por necesidades del cliente... un producto fue en su dia desarrollado en java, y actualmente migrar a otra tecnología es más costoso que seguir dando mantenimiento en java, y probablemente este sea el problema de por qué se sigue utilizando hoy dia.

Un saludo.

Txospo
29-02-2012, 02:12
Muchas gracias por contestar y por las soluciones. Mañana lo miro.

Un saludo.

Txospo
29-02-2012, 10:27
Encontre otra posible forma de solucionarlo, sería estableciendo el tiempo de espera del readLine() con .setSocketTimeout() y así si después de esperar no llega nada elimina el bloqueo:



import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.UnknownHostException;

public class Cliente {
PrintStream salidaControl = null;
BufferedReader entradaControl = null;

public void run(){
try{
Socket sControl = new Socket("192.168.1.14", 21);//;
sControl.setSoTimeout(6000); //Digo cuál será el tiempo de espera
System.out.println("Establecida conexión de control.");

salidaControl = new PrintStream(sControl.getOutputStream(),true);
entradaControl = new BufferedReader(new InputStreamReader(sControl.getInputStream()), 8192);

System.out.println(entradaControl.readLine());

salidaControl.println("USER yo");
System.out.println(entradaControl.readLine());

salidaControl.println("PASS 4488");
System.out.println(entradaControl.readLine());

salidaControl.println("HELP");
leer();
System.out.println("SALGO");
}catch (IOException e) {
System.out.println("Error en el cliente");
}
}

public void leer(){
try {
String line;
while(true){
line = entradaControl.readLine();

if(line != null)
System.out.println(line);
else
break;
}
} catch (IOException e) {
System.out.println("Se agoto el tiempo de espera de lectura.");
} //Aquí se queda esperando que le llegue algo para leer
}


public static void main(String[] args) {
new Cliente().run();
}
}

Esta forma sería valida no? Es decir, funcionar funciona porque lo miré, pero será muy sucio hacer esto, será un apaño, o sería una forma correcta de hacerlo?

Un saludo.