Foros aprenderaprogramar.com
Aprender a programar => C, C++, C#, Java, Visual Basic, HTML, PHP, CSS, Javascript, Ajax, Joomla, MySql y más => Mensaje iniciado por: Lorenzo31 en 15 de Septiembre 2015, 19:24
-
[Nota: el código se encuentra en la respuesta de César Krall a continuación]
Buenas de nuevo, he estado realizando una práctica con TCP. Iré ampliandola pero dejaré aquí lo que llevo hasta el momento.
Basicamente es un servidor y una aplicacion cliente. Para abrirla debeis usar dos BlueJ, en uno abrir el Host, y en el otro acto seguido el cliente.
El programa buscará cualquier archivo (txt, en principio otra cosa no la he probado) y si lo encuentra lo enviará a la dirección destino, que en este caso es fija por el momento (cambiar la dirección que yo he puesto por la de vuestro escritorio o archivo destino).
Las premisas son las siguientes en este ejercicio:
Ejercicio 1:Cread un servidor que abra un fichero solicitado por un cliente y se lo envíe a través de la red. El servidor aceptará el siguiente formato de peticiones:
Para proporcionar un fichero al cliente:
1 get "nombre_completo_fichero"
Para dar por finalizado el servicio:
2 bye
Espero sirva a otras personas y bueno también acepto comentarios, sugerencias o cualquier consejo para aprender más del tema. Este es mi granito de arena en el foro, ir colgando código que vea interesante. (aviso en la primera busqueda tarda, sobre todo en copiar y madar el archivo seleccionado, por la creación del espacio imagino, luego es más fluido. Tened paciencia)
Saludos, espero comentarios y sugerencias de los expertos también.
-
Hola Lorenzo!
Se trata de una aportación valiosa porque se practica con muchos conceptos interesantes. Con tu permiso voy a trasladar el código que has creado para que esté visible dentro del hilo del foro y las personas que lo vean puedan hacerse una idea sobre el código sin tener que descargar el fichero como adjunto.
Clase FicheroServidor:
import java.io.*;
import java.net.*;
/**
* Servidor envio archivos a usuarios
*
* @author Lorenzo31
* @version (a version number or a date)
*/
public class FicheroServidor
{
public static final int PORT = 4444;
public static void main (String args[]) throws IOException {
ServerSocket servidor = null;
Socket cliente;
BufferedReader entrada = null;
//PrintWriter salida = null;
OutputStream sendChannel = null;
String cadena = ""; String comando = "";
try{ servidor = new ServerSocket(PORT);
}catch(IOException e){System.out.println("Error al conectar con el servidor"); System.exit(-1);}
/* Se bloquea mientras escucha */
System.out.println("Servidor escuchando: " + servidor + " " + servidor.getInetAddress());
cliente = servidor.accept();
while(!cliente.isClosed()){ //mientras sea distinto a close, sigue escuchando al usuario
try{
//Establece canal de entrada
entrada = new BufferedReader (new InputStreamReader(cliente.getInputStream()));
//Establece canal envio archivos
sendChannel = cliente.getOutputStream();
//lectura entrada usuario
cadena = entrada.readLine();
} catch(IOException e ) { System.out.println(e.getMessage()); }
System.out.println("Buscando archivo solicitado en comando:" + cadena);
//bucle for quita espacios a la cadena de entrada y lo guarda en comando
for (int x = 0; x < cadena.length(); x++){
if(!cadena.substring(x, x+1).equals(" ") && !cadena.substring(x, x+1).equals("\"")){ comando = comando + cadena.substring(x, x+1); }/*final for*/ }
cadena = comando.substring(0, 3); //reasignamos a la cadena de entrada solo las 3 letras de la orden del usuario
switch(cadena){ //coge los tres caracteres primeros y compara si es bye, get o otro que en ese caso no seria valido
case "bye": System.out.println("Finalizada la conexion con el cliente."); cliente.close(); break;
case "get": //crea instancia de clase buscar archivo y invoca a la funcion buscador para encontrar el archivo solicitado
BuscarArchivo find = new BuscarArchivo();
File archivoEncontrado = find.buscador(comando.substring(3, comando.length() ), new File("C:\\"));
if(archivoEncontrado != null){ //si es null no encontró el archivo sino sí que lo manda
new HiloEnvio (cliente, archivoEncontrado).start(); } else { sendChannel.write(-1); }
cadena = ""; comando = "";
break;
default: sendChannel.write(-1); cadena = ""; comando = ""; //envio archivo vacio comando mal introducido
}
}
}
}
Clase HiloEnvio:
import java.net.*;
import java.io.*;
/**
* Write a description of class HiloEnvio here.
*
* @author (your name)
* @version (a version number or a date)
*/
public class HiloEnvio extends Thread
{
private File archivo;
private Socket socketCliente = null;
private FileInputStream fileChannel = null;
private BufferedInputStream lectorArchivo = null;
private OutputStream sendChannel = null;
public HiloEnvio(Socket socketCliente, File archivo){ this.socketCliente = socketCliente; this.archivo = archivo; }
public void run() {
// envio de file
try{
byte [] mybytearray = new byte [(int)archivo.length()];
fileChannel = new FileInputStream(archivo);
lectorArchivo = new BufferedInputStream(fileChannel);
lectorArchivo.read(mybytearray,0,mybytearray.length);
sendChannel = socketCliente.getOutputStream();
System.out.println("Sending " + archivo + "(" + mybytearray.length + " bytes)");
sendChannel.write(mybytearray,0,mybytearray.length);
sendChannel.flush();
System.out.println("Done.");
}catch (IOException | NullPointerException e) { System.out.println("Interrumpido. ");
interrupt();}
}
}
Clase BuscarArchivo
import java.io.*;
import java.net.*;
/**
* Write a description of class BuscarArchivo here.
*
* @author (your name)
* @version (a version number or a date)
*/
public class BuscarArchivo
{
File archivoEncontrado = null; //si retorna null es que no lo ha encontrado
public BuscarArchivo() { }
public File buscador(String nombre, File raiz){
File[] lista = raiz.listFiles();
if(lista != null) {
for(File elemento : lista) {
if (elemento.isDirectory()) { //si es directorio vuelve a llamarse a si misma
buscador(nombre, elemento);
} else if (nombre.equalsIgnoreCase(elemento.getName())) //sino, si es igual encontrado
{ archivoEncontrado = elemento; System.out.println("Archivo encontrado.");}
}
}
return archivoEncontrado;
} //cierre buscador
}
Clase FicheroCliente
import java.io.*;
import java.net.*;
/**
* Write a description of class FicheroCliente here.
*
* @author (your name)
* @version (a version number or a date)
*/
public class FicheroCliente
{
public static void main(String[] args) throws IOException {
int bytesRead;
OutputStream fos = null;
BufferedOutputStream bos = null;
Socket socketCliente = null;
PrintWriter salida = null;
String hostName = InetAddress.getLocalHost().getHostName();
/* Creamos un socket en el lado cliente, enlazado con un servidor que está en la misma máquina
que el cliente y que escucha en el puerto 4444 */
try{ socketCliente = new Socket(hostName, 4444);
System.out.println("servidor conectado:" + hostName);
//Obtenemos el canal de salida
salida = new PrintWriter( new BufferedWriter( new OutputStreamWriter(socketCliente.getOutputStream())),true);
}catch(IOException e){
System.err.println("No puede establecer conexion");
System.exit(-1); }
BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in));
String linea = "";
/*El programa cliente no analiza los mensajes enviados por el usuario, simplemente los
* reenvia al servidor hasta que este se despide con Adios*/
while(!linea.equalsIgnoreCase("bye")){
do{
System.out.println("Intrude comando válido:");
//Leo la entrada del usuario
linea = stdIn.readLine();
}while (!linea.matches("[a-z][a-z][a-z] \".*\"") && !linea.equalsIgnoreCase("bye"));
//La envia al servidor
salida.println(linea);
try {
//reciibr archivo
//Creamos array de bytes
byte [] mybytearray = new byte [66666];
//Creamos objeto InputStream que abre la cadena de entrada para lectura del fichero que mande servidor
InputStream cadenaReceptor = socketCliente.getInputStream();
fos = new FileOutputStream("C:/Documents and Settings/Admin/Desktop/llegada.txt");
bos = new BufferedOutputStream(fos); //lee donde va a escribir
bytesRead = cadenaReceptor.read(mybytearray,0,mybytearray.length);
if(bytesRead == 1){ //1 valor que toma como mínimo un archivo Outputstream, significa archivo no encontrado o no buscado
System.out.println("Archivo no encontrado o comando erroneo");
} else if(bytesRead == -1){ System.out.println("Sesion finalizada"); } //-1 valor que toma al no recibir ningun archivo erroneo ni correcto, solo con BYE
else { bos.write(mybytearray, 0 , bytesRead); //buffer escribe en el archivo asignado
bos.flush();
System.out.println("File " + "C:/Documents and Settings/Admin/Desktop/llegada.txt"
+ " downloaded (" + bytesRead + " bytes read)"); }
}catch(IOException e){ System.out.println("Error en la transmisión.");
if (fos != null) fos.close();
if (bos != null) bos.close();
if (socketCliente != null) socketCliente.close(); }
}
if (fos != null) fos.close();
if (bos != null) bos.close();
if (socketCliente != null) socketCliente.close();
}
}
-
Para orientar a quienes quieran probar el programa:
- En la clase FicheroCliente reemplazar C:/Documents and Settings/Admin/Desktop/llegada.txt por una ruta propia del pc donde estemos trabajando, por ejemplo C:/Users/Krall/Desktop/pruebas/llegada.txt
En esa ruta será donde se coloque el fichero enviado por el servidor (el fichero que vamos a descargar en el cliente)
Para hacer una prueba, crear un fichero por ejemplo denominado prueba.txt y ubicarlo en el escritorio (o en otro lugar).
Para comenzar, ejecutar el método main de la clase FicheroServidor. Es posible que salte una alerta de Firewall de Windows. En este caso elegir "Permitir acceso"
Por pantalla nos saldrá <<Servidor escuchando: ServerSocket[addr=0.0.0.0/0.0.0.0,localport=4444] 0.0.0.0/0.0.0.0
>>
Abrir otro entorno de ejecución Java y ejecutar el método main de la clase FicheroCliente
Por pantalla nos saldrá:
servidor conectado:NombreServer
Introduce comando válido:
Escribimos get "prueba.txt"
Con esto lo que hemos dicho es "busca el archivo prueba.txt dentro del servidor y descárgalo al cliente en la ruta C:/Users/Krall/Desktop/pruebas/llegada.txt" (o aquella ruta que hayamos indicado).
En la ventana de EnvioFicheros obtendremos al cabo de un par de minutos:
servidor conectado:Krall
Intrude comando válido:
get "prueba.txt"
File C:/Users/Krall/Desktop/pruebas/llegada.txt downloaded (66 bytes read)
Introduce comando válido:
Aquí como comando introduciremos bye para desconectar.
En la ventana EnvioFicheros obtendremos algo como esto
Servidor escuchando: ServerSocket[addr=0.0.0.0/0.0.0.0,localport=4444] 0.0.0.0/0.0.0.0
Buscando archivo solicitado en comando:get "prueba.txt"
Archivo encontrado.
Sending C:\Users\Krall\Desktop\prueba.txt(66 bytes)
Done.
Finalmente, como comprobación de que el archivo ha sido bajado desde el cliente al servidor iremos a la ruta donde indicamos que debía guardarse el archivo en el cliente del tipo C:/Users/Krall/Desktop/pruebas/llegada.txt y comprobaremos que el archivo se encuentra ahora en esa ruta
Una vez escribimos bye nos indicará que la sesión ha finalizado:
En la clase FicheroServidor
servidor conectado:Krall
Intrude comando válido:
get "prueba.txt"
File C:/Users/Krall/Desktop/pruebas/llegada.txt downloaded (66 bytes read)
Intrude comando válido:
bye
Sesion finalizada
En la clase EnvioFicheros:
Servidor escuchando: ServerSocket[addr=0.0.0.0/0.0.0.0,localport=4444] 0.0.0.0/0.0.0.0
Buscando archivo solicitado en comando:get "probandoLorenzo.txt"
Archivo encontrado.
Sending C:\Users\Krall\Desktop\prueba.txt(66 bytes)
Done.
Buscando archivo solicitado en comando:bye
Finalizada la conexion con el cliente.
Con esto hemos completado la ejecución del programa donde: hemos abierto una ejecución en el servidor, hemos abierto otra ejecución en el cliente, luego hemos pedido desde el cliente que busque un archivo en el servidor y lo descargue. Si lo encuentra tendremos el fichero en la ruta que hayamos indicado. Si no lo encuentra, o si hay algún problema, nos saldrá un mensaje de error.
Saludos!
-
No he revisado el código por ello voy solo a comentar algunas cosas, creo que sería bueno oir tu opinión o corrección.
- El cliente pide al servidor que le envíe un fichero que se encuentra en el servidor por ejemplo prueba.txt y que lo deposite en una ubicación del cliente por ejemplo C:/Users/Krall/Desktop/pruebas/llegada.txt
- En el servidor la búsqueda del fichero se hace mediante una búsqueda recursiva en todo el sistema de archivos. Sugerencia: esta búsqueda es muy costosa. ¿Por qué no defines una ubicación donde deba realizarse la búsqueda? (Por ejemplo dentro de una carpeta). De esa manera no tardaría tanto tiempo en realizar la búsqueda (tal y como está ahora puede requerir varios minutos)
- En el diseño los nombres de las clases no acaban de tener una representación clara de lo que son o lo que hacen. Por ejemplo no veo una clase Cliente ni una clase Servidor. Sería bueno oir una descripción del cometido de cada clase (que tendría que hacer quien ha creado el programa) y quizás también un rediseño para dar mejores nombres a las clases. Por ejemplo BuscarArchivo no es un nombre coherente para una clase. Podría serlo BuscadorDeArchivos o algo similar, pero no es común que el nombre de una clase sea el infinitivo de un verbo. Hay nombres que no veo claros por ejemplo la clase FicheroServidor ¿representa un fichero servidor? Por ejemplo no veo claro que un fichero pueda recibir órdenes por consola, eso no se ve coherente.
Ante todo gracias por la aportación!
-
Muy buenas César, ante todo gracias por las molestias de echarle un vistazo al proyecto.
Sobre el funcionamiento, es correcto todo lo que explicas, yo añadiría que para verificar si el comando introducido cumple los parametros solicitados he utilizado una funcion de la clase String muy interesante, que es matches().
while (!linea.matches("[a-z][a-z][a-z] \".*\"")
En este caso con unos parametros definidos de 3 letras de la a -z, seguido de una " inicial y una final (\") y con cualquier texto en medio de estas (.*) .
Sobre tu pregunta del nombre de las clases, bueno tienes toda la razón, no son nombres apropiados, el motivo es que en el mismo proyecto tengo 3 clases servidor más y tres clientes, de ahí ese nombre.
Para el proyecto final, lo documentaré mucho mejor y pondré nombres más clarificadores. El proyecto final tiene que aceptar multiples clientes (ya lo hago en uno anterior) y además añadir más funciones como modificar archivos etc...
Te adjuto el documento del que saqué los ejercicios y una breve parte teórica (insuficiente para lo que pide hacer luego). Pero si sirve para que otros practiquen el TCP genial. Es de la universidad Carlos III de madrid, una url.
http://www.it.uc3m.es/~celeste/docencia/cr/2003/PracticaSocketsTCP/
Cuando termine este ultimo proyecto colgaré todos los anteriores además de este.
Ah, y sobre la busqueda desde C: , sí esta claro que hace que tarde más, si no encuentra rápido el archivo, se podría pasar la carpeta en la que buscarlo, pero el ejercicio pedia expresamente que fuera busqueda en cualquier lugar y de ahí la forma de busqueda.
Cualquier otro comentario, encantado de leerte, me costó bastante entender la cantidad de cosas que hay que abrir, InputStream, Outputstream, BufferedInputStream, File además de array de bytes ... otras clases que aun no he probado son FileInputStream y FileOutputStream, si puedes explicarme brevemente la diferencia con las anteriores, te lo agradecería. Puede ser que permta el envio de archivos mayores o algo mas optimizado? aunque el tamaño va en funcion de los bytes declarados no del Input o Output.
Gracias César.
*Modifico para añadir, que pueden buscarse tantos archivos al a vez como se quiera, es decir get "test.txt", luego get "aprenderaprogramar.txt" etc.. y a partir de la segunda busqueda va muchisimo mas rápido.
Y una pregunta que me lleva un poco de cabeza, he probado a buscar y guardar una JPG, bien el servidor la encuentra y "dice que envia" 800.000 bytes (correcto) pero al recibirlo en el cliente, el InputStream.read() siempre coge un máximo de 65536 bytes.
No se porque, si le marco el length total, creo que tiene que ver con el OutputStream.write() que no llega a mandar el total, podrías añadirme alguna luz sobre este asunto, busco por las redes pero no encuentro respuesta. Gracias.
-
Hola chicos! Al probar el codigo me funciona bien pero tengo un problema resulta que despues de crearme el archivo en la ubicacion indicada de destino tiene el texto que había dentro pero unos instantes despues el archivo pasa a tener 0 bytes y el texto que tenia desaparece, es como si se sobreescribiera ¿que puede estar pasando?
-
Efectivamente en esta versión al lanzar el bye, no pasa por el while principal y ejecuta una transferencia de datos equivalente a 0.
Lo he corregido y adjunto el zip nuevo, pido a los moderadores sustituyan este por el que sale arriba de la página, disculpas toqué varias opciones no me di cuenta del fallo, gajes del oficio.
Gracias por darte cuenta Lola!
-
He trasladado el zip nuevo al mensaje inicial del hilo dejando de momento un solo zip (creo que así evitamos confusiones). Saludos.
-
Hola he creado un hilo comentando las diferencias entre InputStream y BufferedInputStream localiza en https://www.aprenderaprogramar.com/foros/index.php?topic=3240.0
Salu2
-
Genial! gracias Ogramar, en un rato me la leo detenidamente, muy utiles los hilos de teoría, para aclarar conceptos.
Thanks !
-
Como puedo descargar los codigos, para revisarlos
-
Buenas, en la respuesta de César Krall tienes los códigos. Pulsa en seleccionar y copia el código de cada clase. Salu2