Filtro CPL Polarizado ventajas

Sockets e Hilos en Java (Programando un servidor de archivos multiusuario)

Bienvenidos a mi nuevo post!!

En esta ocasión les quiero compartir un programa hecho en Java, su función es servir a los clientes que se conecten a él, y lo haremos con Sockets e Hilos en consola.

Les compartiré dos servidores diferentes, bueno en otras palabras, habrá dos escenarios:

1.- El servidor atenderá a múltiples clientes de manera concurrente, todos a la vez, en este caso el servidor estará enviando archivos a cada uno de los usuarios conectados al mismo tiempo, para que se vea la concurrencia es recomendable correrlo con archivos mayores de 50 Megabytes.

2.- El servidor atenderá a múltiples clientes de manera secuencial, es decir, todos se conectarán al mismo tiempo, pero primero atenderá a uno, en cuanto termine de atender (enviar el archivo) de inmediato comenzara a atender al siguiente, y así sucesivamente hasta terminar con todos los clientes.

Podemos enviar cualquier tipo de archivos(.exe, .jpeg, .iso, .jar, .pdf, .bat, .dll,  etc).

La funcionalidad del cliente será la siguiente:
El cliente podrá funcionar en los dos tipos de servidores, así que será el mismo.
El cliente se conectará al servidor, y escribirá un archivo que quiera, de la siguiente manera; archivo.exe, archivo.iso, archivo.png, etc.
Y el Servidor buscará ese archivo en el directorio establecido, si lo encuentra mandará mensaje al cliente que si lo encontró y de inmediato iniciará la transferencia del archivo, de lo contrario enviará un mensaje que no lo encontró.

Bueno teniendo esto en cuenta, podemos pasar a ver el código.

Código del Servidor Concurrente


package demoserverconcurrente;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import javax.swing.JOptionPane;

/**
 *
 * @author Ivan Luis Jimenez
 */
public class concurrente {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) throws IOException, InterruptedException {
        Socket s = null;
        ServerSocket ss = null;
        Tarea ob;
        int id = 0;

        try {

            ss = new ServerSocket(5432);
            while(true){
            
                Tarea.escribir("Socket escuchando en puerto 5432");
                s = ss.accept();
                id++;
                JOptionPane.showMessageDialog(null, "Se conecto un nuevo cliente", "Cliente "+id, 2);
                Tarea.escribir("\nSe conecto el cliente No." + id + " desde la IP: " + s.getInetAddress());
                Tarea.escribir("**************************************************");
                ob = new Tarea(s, id);
                ob.start();                

            }

        } catch (IOException e) {
            Tarea.escribir(e.getMessage() + " [servidor]");
            System.exit(3);
        } finally {
        }

    }

    static class Tarea extends Thread {

        int id;
        Socket s = null;
        /*
         *Atributos para manejar salida y entrada de texto
         */
        ObjectInputStream ois = null;
        ObjectOutputStream oos = null;
        DataInputStream entradaCliente = null;
        DataOutputStream salidaCliente = null;

        /*
         *Atributos para manejar salida de archivos
         */
        File archivo = null;
        long tiempoinicio = 0;
        long tiempofinal= 0;
        long tiempo_total=0;
        long initialTime;
        private Tarea(Socket socket, int id) {
            this.s = socket;
            this.id = id;
        }

        boolean checar(String nombre) {
            archivo = new File("C:\\Users\\Jony\\Desktop\\TOOL\\Trimestre I UAM\\Sistemas Distribuidos\\2. Servidores multiprocesos, concurrentes y multihilos\\Demoserverconcurrente\\origen\\" + nombre);
            if (archivo.exists()) {
                return true;
            } else {
                return false;
            }
        }

        public void run() {
            
            
            try {
                entradaCliente = new DataInputStream(s.getInputStream());
                salidaCliente = new DataOutputStream(s.getOutputStream());
                String nombreArchivo = entradaCliente.readUTF();
                
                if (checar(nombreArchivo) == true) {
                    tiempoinicio=(System.currentTimeMillis() - this.initialTime);
                    System.out.println("El servidor comienza envio a cliente :" + id + " EN EL TIEMPO: "
                    + (System.currentTimeMillis() - this.initialTime)
                    + " milisegundos");
                    salidaCliente.writeBoolean(true);
                    salidaCliente.writeUTF("SI existe el archivo:" + nombreArchivo + " en el servidor");
                    salidaCliente.writeUTF("Tamaño del archivo:" + (archivo.length() / 1024) + " KB | Nombre:" + archivo.getName());
                    salidaCliente.writeInt((int) archivo.length());
                    salidaCliente.writeUTF(nombreArchivo);
                    escribir("Enviando archivo:" + nombreArchivo + " a " + s.getInetAddress());
                    FileInputStream entrada = new FileInputStream(archivo);
                    BufferedInputStream leerArch = new BufferedInputStream(entrada);
                    // Creamos el flujo de salida 
                    BufferedOutputStream salida = new BufferedOutputStream(s.getOutputStream());
                    // Creamos un array de tipo byte 
                    byte[] arreglo = new byte[(int) archivo.length()];
                    // Leemos el archivo y lo introducimos en el array de bytes 
                    leerArch.read(arreglo);
                    // Realizamos el envio de los bytes que conforman el archivo

                    for (int i = 0; i < arreglo.length; i++) {
                        salida.write(arreglo[i]);
                    }
                    tiempofinal=(System.currentTimeMillis() - this.initialTime);
                    tiempo_total=tiempofinal-tiempoinicio;
                    escribir("Archivo Enviado a cliente:" + id);

                    System.out.println("El servidor termino con cliente" + id + "  EN UN TIEMPO DE: "
                            + tiempo_total + " milisegundos");
                    System.out.println("Tiempo del cliente "+id +": ("+(System.currentTimeMillis() - this.initialTime)+") milisegundos");

                    salida.flush();
                    salida.flush();
                    salida.close();
                    entrada.close();
                }

                if (checar(nombreArchivo) == false) {
                    salidaCliente.writeBoolean(false);
                    salidaCliente.writeUTF("NO existe el archivo:" + nombreArchivo + " en el servidor");
                    escribir("se envio respuesta al cliente");
                }
            } catch (Exception ex) {
                escribir(ex.getMessage() + " id:" + id);
            } finally {
                try {
                    if (oos != null) {
                        oos.close();
                    }
                    if (ois != null) {
                        ois.close();
                    }
                    if (s != null) {
                        s.close();
                    }
                    System.out.println("Termino proceso para cliente: " + id);

                } catch (Exception e) {
                    System.out.println(e.getMessage() + " [servidor]");
                }
            }
        }

        public static void escribir(String txt) {
            System.out.println(txt);
        }
    }

}

Ahora trataré de explicarles el código: El servidor consta de dos clases, una llamada concurrente y la otra se llama tarea. El la clase concurrente sólo levantaremos el servidor, y pondremos a la escucha de los clientes con un ciclo infinito. en el método main declaramos las variables: El socket para recibir al cliente cuando se conecte. Y el ServerSocket para levantar el servidor. Con Tarea, creamos un objeto de la otra clase para llamar al método run(); Y un id para llevar el contador de clientes conectados.
Socket s = null;
ServerSocket ss= null;
Tarea ob;
int id = 0;
Ahora dentro de un try catch metemos todo el código de la escucha del servidor. Con ss = new ServerSocket(5432); le indicamos en que puerto estará a la escucha, y en se puerto se conectará el cliente. Con el while(true) trabajamos con un ciclo infinito. Con Tarea.escribir("String"); mandamos a llamar el método de la otra clase y le mandamos una cadena para que la imprima en pantalla. Esto para trabajar con el mismo método en ambas clases y no declarar otro método, esto nos ahorrará memoria. Con s = ss.accept(); estamos a la escucha del cliente, digamos que es un pause del servidor, no sigue hasta que un cliente se conecte a él, en el momento que se conecten, pasa a la siguiente linea. Con id++; aumentamos el contador; Y nuevamente con Tarea.escribir("Striing"); mandamos una cadena para avisar que ya se conecto un cliente desde la ip que sea. Con ob = new Tarea(s, id); creamos una nueva instancia, objeto, proceso, hilo o como mas le entiendan, y le manda el id y el socket que se recibió. Con ob.start(); Lanzamos el hilo, ejecutamos el hilo para que procese la petición del cliente. Algún error se captura en el Catch.
try {

            ss = new ServerSocket(5432);
            while(true){
            
                Tarea.escribir("Socket escuchando en puerto 5432");
                s = ss.accept();
                id++;
                Tarea.escribir("\nSe conecto el cliente No." + id + " desde la IP: " + s.getInetAddress());
                Tarea.escribir("**************************************************");
                ob = new Tarea(s, id);
                ob.start();                

            }

        } catch (IOException e) {
            Tarea.escribir(e.getMessage() + " [servidor]");
            System.exit(3);
        } finally {
        }
La otra clase Tarea hace todo el proceso,sólo recibe el id, y el socket. Se declara otro Socket dentro de esta clase, esto para pasarle el socket que se acaba de recibir. Declaramos objetos para mandar y recibir datos del servidor al clinte, y del cliente al servidor, tal como lo vemos en el constructor de la clase.
int id;
        Socket s = null;
        /*
         *Atributos para manejar salida y entrada de texto
         */
        ObjectInputStream ois = null;
        ObjectOutputStream oos = null;
        DataInputStream entradaCliente = null;
        DataOutputStream salidaCliente = null;

        /*
         *Atributos para manejar salida de archivos
         */
        File archivo = null;
        //atributos para manejar tiempos de los cliente
        long tiempoinicio = 0;
        long tiempofinal= 0;
        long tiempo_total=0;
        long initialTime;
        private Tarea(Socket socket, int id) {
            this.s = socket;
            this.id = id;
        }
El siguiente método sólo checa si existe el archivo en el directorio asignado. Si existe devuelve un true, sino un false.
 boolean checar(String nombre) {
            archivo = new File("C:\\Users\\Jony\\Desktop\\TOOL\\Trimestre I UAM\\Sistemas Distribuidos\\2. Servidores multiprocesos, concurrentes y multihilos\\Demoserverconcurrente\\origen\\" + nombre);
            if (archivo.exists()) {
                return true;
            } else {
                return false;
            }
        }
Y ahora el método run(). Éste método es el objeto de estudio del servidor. Los comentario los escribiré dentro del código para que no me sea complicado la edición. ;-)
public void run() {
            
            
            try {
                entradaCliente = new DataInputStream(s.getInputStream());// Flujo de entrada para recibir mensajes o algo del cliente.
                salidaCliente = new DataOutputStream(s.getOutputStream());// Flujo de salida, esto para enviar mensajes o algo al cliente.
                String nombreArchivo = entradaCliente.readUTF();//Con esta linea, estamos leyendo un mensaje del cliente.
                
                if (checar(nombreArchivo) == true) { // Si existe el archivo realiza todo el proceso
                    tiempoinicio=(System.currentTimeMillis() - this.initialTime);//inicia el contador del tiempo
                    System.out.println("El servidor comienza envio a cliente :" + id + " EN EL TIEMPO: "
                    + (System.currentTimeMillis() - this.initialTime)
                    + " milisegundos");// Se imprime en pantalla que ya inicio el el envio al cliente
                    salidaCliente.writeBoolean(true);// Se le envia true al cliente, esto para indicarle que si se encontró el archivo que busca.
                    salidaCliente.writeUTF("SI existe el archivo:" + nombreArchivo + " en el servidor");// Se le manda otra confirmación pero como texto.
                    salidaCliente.writeUTF("Tamaño del archivo:" + (archivo.length() / 1024) + " KB | Nombre:" + archivo.getName());// Se le envía el tamaño del archivo como mensaje(texto - String)
                    salidaCliente.writeInt((int) archivo.length());// Se le envía el tamaño como entero
                    salidaCliente.writeUTF(nombreArchivo);// Se le envía el nombre del archivo como cadena
                    escribir("Enviando archivo:" + nombreArchivo + " a " + s.getInetAddress());//se escribe en servidor que se esta enviando el archivo 
                    FileInputStream entrada = new FileInputStream(archivo);// creamos flujo para almacenar en él el archivo que se va a enviar
                    BufferedInputStream leerArch = new BufferedInputStream(entrada);//flujo para leer el archivo
                    // Creamos el flujo de salida 
                    BufferedOutputStream salida = new BufferedOutputStream(s.getOutputStream());
                    // Creamos un array de tipo byte para almacenar el archivo en un arreglo
                    byte[] arreglo = new byte[(int) archivo.length()];
                    // Leemos el archivo y lo introducimos en el array de bytes 
                    leerArch.read(arreglo);
                    // Realizamos el envio de los bytes que conforman el archivo
                    //con esto estamos enviando al cliente el archivo, byte por byte
                    for (int i = 0; i < arreglo.length; i++) {
                        salida.write(arreglo[i]);//escribiendo en cliente
                    }
                    tiempofinal=(System.currentTimeMillis() - this.initialTime);
                    tiempo_total=tiempofinal-tiempoinicio;//capturamos tiempo de fin de envio
                    escribir("Archivo Enviado a cliente:" + id);

                    System.out.println("El servidor termino con cliente" + id + "  EN UN TIEMPO DE: "
                            + tiempo_total + " milisegundos");// se imprime el tiempo que tardo con el cliente en especifico 
                    System.out.println("Tiempo del cliente "+id +": ("+(System.currentTimeMillis() - this.initialTime)+") milisegundos");// Se imprime el tiempo que lleva hasta el momento, tomando en cuenta el primero hasta el último cliente

                    salida.flush();//Se limpia la salida
                    salida.flush();// otra vez por si las moscas ;-)
                    salida.close();// se cierra el flujo
                    entrada.close();//se cierra el flujo
                }

                if (checar(nombreArchivo) == false) {// si no se encuentra el archivo 
                    salidaCliente.writeBoolean(false);// se manda al cliente un false
                    salidaCliente.writeUTF("NO existe el archivo:" + nombreArchivo + " en el servidor");//se envía al cliente un mensaje que no existe
                    escribir("se envio respuesta al cliente");//al final se imprime que se envío respuesta al cliente, esto sólo para asegurarnos ;-)
                }
            } catch (Exception ex) {
                escribir(ex.getMessage() + " id:" + id);// cualquier error se captura aquí
            } finally {
                try {
                    if (oos != null) {
                        oos.close();
                    }
                    if (ois != null) {
                        ois.close();
                    }
                    if (s != null) {
                        s.close();
                    }
                    System.out.println("Termino proceso para cliente: " + id);

                } catch (Exception e) {
                    System.out.println(e.getMessage() + " [servidor]");
                }
            }
        
Y bueno esto fue el Servidor Con concurrente. El servidor secuencial es lo mismo que el concurrente, sólo que agregaremos una linea. ;-) el método .join(); espera a que termine el hilo que se esta procesando, y una vez que termino, continua con el siguiente.
while(true){
            
                Tarea.escribir("Socket escuchando en puerto 5432");
                s = ss.accept();
                id++;
                Tarea.escribir("\nSe conecto el cliente No." + id + " desde la IP: " + s.getInetAddress());
                Tarea.escribir("**************************************************");
                ob = new Tarea(s, id);

                ob.start();
                ob.join();//----> ésta linea y listo ya es secuencial

            }
Ahora el Programa Cliente Será un poco mas breve para explicar el cliente.
package cliente;

/**
 *
 * @author Ivanovich
 */
import java.awt.Desktop;
import java.net.*;
import java.io.*;

public class Cliente {
    /*
     *atributos para manejar salida y entrada de texto
     */

    BufferedReader delTeclado;
    DataOutputStream alservidor;
    FileInputStream entrada;

    /*
     *atributos para manejar entrada de archivos
     */
    ObjectInputStream ois = null;
    ObjectOutputStream oos = null;
    FileOutputStream destino = null;
    BufferedOutputStream out = null;
    BufferedInputStream in = null;
    boolean respuesta = false;
    int tam = 0;
    String nombreArchivo = null;

    //Metodo iniciar el servidor cliente
    public void iniciar() {
        try {
            Socket yo = new Socket("127.0.0.1", 5432);// instanciamos al Socket, le mandamos la ip del servidor y el mismo puerto en el que esta escuchando el servidor.
/*
     *Declaramos los flujos para enviar y recibir mensajes del servidor.
     */
            delTeclado = new BufferedReader(new InputStreamReader(System.in));
            alservidor = new DataOutputStream(yo.getOutputStream());
            DataInputStream delServidor = new DataInputStream(yo.getInputStream());

            escribir("Teclee el nombre del archivo:");//Le indicamos al cliente que debe escribir el nombre del archivo que desea.
            String el = delTeclado.readLine();//Se lee la linea escrita por el cliente y se guarda en la variable el.
            alservidor.writeUTF(el);// con esta linea le estamos enviando la cadena al servidor
            respuesta = delServidor.readBoolean();//y se recibe la respuesta del servidor

            if (respuesta == true) {// si la respuesta que se recibió del servidor es true, se prepara para recibir el archivo
                escribir("[Servidor]" + delServidor.readUTF());//Se recibe la cadena que indica que SI existe el archivo
                escribir("[Servidor]:" + delServidor.readUTF());// Se recibe la cadena que indica el tamaño del archivo
                tam = delServidor.readInt();//se recibe tamaño del archivo como entero
                nombreArchivo = delServidor.readUTF();//se recibe el nombre del archivo

                destino = new FileOutputStream("C:\\Users\\Jony\\Desktop\\TOOL\\Trimestre I UAM\\Sistemas Distribuidos\\2. Servidores multiprocesos, concurrentes y multihilos\\Envioarchivo\\Cliente_concurrente_secuencial\\destino\\" + nombreArchivo);// le asignamos la ruta en la cual se guardará el archivo recibido.
                out = new BufferedOutputStream(destino);//creamos flujos como en el servidor para recibir el archivo
                in = new BufferedInputStream(yo.getInputStream());
                // Creamos el array de bytes para leer los datos del archivo

                byte[] buffer = new byte[tam];

                // Obtenemos el archivo mediante la lectura de bytes enviados
// leemos el archivo que se recibe
                for (int i = 0; i < buffer.length; i++) {
                    buffer[i] = (byte) in.read();
                }
                // Escribimos el archivo en la dirección corresondiente 
                out.write(buffer);

                escribir("Se recivio el archivo");
                //Limpiamos 
                out.flush();
                //Cerramos flujos
                //abrimos el archivo recivido
                abrir_archivo();
                // }
                in.close();
                out.close();
                yo.close();
            } else {
                escribir("[Servidor]" + delServidor.readUTF());

                yo.close();
            }
        } catch (Exception e) {
            System.out.println("error " + e.getMessage() + " cliente");
        }

    }
    // en el método main sólo mandamos a llamar el método iniciar();
    public static void main(String args[]) {
        Cliente ea = new Cliente();
        ea.iniciar();
    }
    // el metodo que imprime en pantalla 
    public static void escribir(String txt) {
        System.out.println(txt);
    }
    // el método que abre el archivo que se recibió
    public void abrir_archivo() {

        try {
            //indicamos la ruta
            File objetofile = new File("C:\\Users\\Jony\\Desktop\\TOOL\\Trimestre I UAM\\Sistemas Distribuidos\\2. Servidores multiprocesos, concurrentes y multihilos\\Envioarchivo\\Cliente_concurrente_secuencial\\destino\\" + nombreArchivo);
            Desktop.getDesktop().open(objetofile);//abrimos el archivo

        } catch (IOException ex) {

            System.out.println(ex);

        }
    }

}


Si observaron todo esta sincronizado, es decir si uno envía en otro tiene que recibir, y viceversa.


 Bueno esto sería el proyectito. Las capturas se las dejo en un archivo de PDF junto con el código de los programas vale. Link de descarga:

Descargar

http://raboninco.com/GDRv

Created By Ivan Luis Jimenez

Alguna duda, comentario o sugerencia, en la parte de abajo pueden escribirme.

Hilos en Java
Thread Java
Multihilos en Java
MultiThread Java

Comentarios

  1. una Duda como puedo enviar la lista de archivos del servidor al cliente para que escoja el cliente el archivo que desea descargar, intente con un ObjectOutputStream pero tuve problemas

    ResponderBorrar
  2. Buena pregunta, lo mas recomensable es hacer lectura de todos los archivos del directorio en donde este el repositorio de archivos que quieras compartir de la siguiente manera:
    Declaras un arreglo en el que guardarás todas las direcciones que encuentre
    Vector direcciones = new Vector();
    File dir = new File("c:\winnt");
    String[] ficheros = dir.list();
    if (ficheros == null)
    System.out.println("No hay ficheros en el directorio especificado");
    else {
    for (int x=0;x<ficheros.length;x++)
    direcciones.add(ficheros[x].toString());
    }

    y nadamas le envias el arreglo al cliente.
    Puedes agregarle al cliente un botón que refresque las direcciones (nombres de los archivos).
    Saludos!

    ResponderBorrar
  3. Hola, que tal estoy ejecutando el programa desde mi Mac, pero me dice que no encuentra el archivo he guardado las rutas del servidor y cliente en el código de la siguiente manera

    Servidor: /Users/Fabian/Desktop/servidor

    Cliente: /Users/Fabian/Desktop/cliente

    ResponderBorrar
    Respuestas
    1. En dónde es que te manda ese mensaje? a la hora de ejecutarlo? o de abrir el archivo enviado?

      Borrar
    2. Primero ejecuto el servidor y despues el cliente ambos en netbeans .. el cliente me.pide que ingrese el nombre del archivo y lo ingreso asi "foto.jpeg" y le doy enter y me dice que no se encontró el archivo en el destino.. y se acaba la ejecución

      Borrar
    3. Si, entiendo. Lo que pasa es que la ruta que se especifica en el programa es otra a la que estas trabajando, por tanto, tu debes cambiar la ruta que se indica en el programa, tal ruta se encuentra el el servidor en el método que se llama checar(), ahí tu debes poner la ruta de tus archivos que quieres que el cliente quiera descargar

      Borrar
    4. Si claro eso es lo que hice en un inicio la ruta del servidor es /Users/Fabian/Desktop/servidor donde servidor es una carpeta en el escritorio donde guardo los archivos. Pero el codigo de cliente me dice que los archivos no existen en el servidor

      Borrar
    5. Coloca la ruta absoluta de la carpeta, y coloca los archivos dentro de la carpeta. Debes escribisrla tal y como se llama el archivo, con su extensión, por ejemplo si es imagen.jpg, tienes que ponerle así al momento de buscar.

      Borrar
  4. Eso mismo he estado haciendo pero no funciona... las rutas absolutas son las que le puse en anteriores comentarios. :/ no se que puede ser.

    ResponderBorrar
  5. Intenta colocar, C:\\Users\\Fabian\\Desktop\\ tiene que funcionar

    ResponderBorrar

Publicar un comentario