gentoo linux, java, software libre y otras hierbas
mar, 28 2008 - 11:53 am

[código] Conexiones Cliente-Servidor mediante sockets en Java

Por falta de tiempo me es imposible explicar este código detalladamente, tal como me gustaría, pero no tengo otra opción que simplemente ponerlo. Aún así es de recordar que pueden dejar todos los comentarios que deseen, con sus dudas y sugerencias (que no pedidos de tareas).

Aún así explico un poco el resultado del programa… el programa es un chat simple (muy simple), que usa Sockets para crear conexiones de red. Consta de un servidor (Servidor.java) y un cliente (Cliente.java), mediante los cuales se puede establecer una conversación. Si se escribe “TERMINAR” (sin comillas), la conexión se cierra.

El resultado

 

El código

El código fuente de Servidor.java:

import java.io.*;
import java.net.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class Servidor extends JFrame {
   private JTextField campoIntroducir;
   private JTextArea areaPantalla;
   private ObjectOutputStream salida;
   private ObjectInputStream entrada;
   private ServerSocket servidor;
   private Socket conexion;
   private int contador = 1;

   // configurar GUI
   public Servidor()
   {
      super( "Servidor" );

      Container contenedor = getContentPane();

      // crear campoIntroducir y registrar componente de escucha
      campoIntroducir = new JTextField();
      campoIntroducir.setEditable( false );
      campoIntroducir.addActionListener(
         new ActionListener() {

            // enviar mensaje al cliente
            public void actionPerformed( ActionEvent evento )
            {
               enviarDatos( evento.getActionCommand() );
               campoIntroducir.setText( "" );
            }
         }
      ); 

      contenedor.add( campoIntroducir, BorderLayout.NORTH );

      // crear areaPantalla
      areaPantalla = new JTextArea();
      contenedor.add( new JScrollPane( areaPantalla ),
         BorderLayout.CENTER );

      setSize( 300, 150 );
      setVisible( true );

   } // fin del constructor de Servidor

   // configurar y ejecutar el servidor
   public void ejecutarServidor()
   {
      // configurar servidor para que reciba conexiones; procesar las conexiones
      try {

         // Paso 1: crear un objeto ServerSocket.
         servidor = new ServerSocket( 12345, 100 );

         while ( true ) {

            try {
               esperarConexion(); // Paso 2: esperar una conexión.
               obtenerFlujos();        // Paso 3: obtener flujos de entrada y salida.
               procesarConexion(); // Paso 4: procesar la conexión.
            }

            // procesar excepción EOFException cuando el cliente cierre la conexión
            catch ( EOFException excepcionEOF ) {
               System.err.println( "El servidor terminó la conexión" );
            }

            finally {
               cerrarConexion();   // Paso 5: cerrar la conexión.
               ++contador;
            }

         } // fin de instrucción while

      } // fin del bloque try

      // procesar problemas con E/S
      catch ( IOException excepcionES ) {
         excepcionES.printStackTrace();
      }

   } // fin del método ejecutarServidor

   // esperar que la conexión llegue, después mostrar información de la conexión
   private void esperarConexion() throws IOException
   {
      mostrarMensaje( "Esperando una conexión\n" );
      conexion = servidor.accept(); // permitir al servidor aceptar la conexión
      mostrarMensaje( "Conexión " + contador + " recibida de: " +
         conexion.getInetAddress().getHostName() );
   }

   // obtener flujos para enviar y recibir datos
   private void obtenerFlujos() throws IOException
   {
      // establecer flujo de salida para los objetos
      salida = new ObjectOutputStream( conexion.getOutputStream() );
      salida.flush(); // vaciar búfer de salida para enviar información de encabezado

      // establecer flujo de entrada para los objetos
      entrada = new ObjectInputStream( conexion.getInputStream() );

      mostrarMensaje( "\nSe recibieron los flujos de E/S\n" );
   }

   // procesar la conexión con el cliente
   private void procesarConexion() throws IOException
   {
      // enviar mensaje de conexión exitosa al cliente
      String mensaje = "Conexión exitosa";
      enviarDatos( mensaje );

      // habilitar campoIntroducir para que el usuario del servidor pueda enviar mensajes
      establecerCampoTextoEditable( true );

      do { // procesar los mensajes enviados por el cliente

         // leer el mensaje y mostrarlo en pantalla
         try {
            mensaje = ( String ) entrada.readObject();
            mostrarMensaje( "\n" + mensaje );
         }

         // atrapar problemas que pueden ocurrir al tratar de leer del cliente
         catch ( ClassNotFoundException excepcionClaseNoEncontrada ) {
            mostrarMensaje( "\nSe recibió un tipo de objeto desconocido" );
         }

      } while ( !mensaje.equals( "CLIENTE>>> TERMINAR" ) );

   } // fin del método procesarConexion

   // cerrar flujos y socket
   private void cerrarConexion()
   {
      mostrarMensaje( "\nFinalizando la conexión\n" );
      establecerCampoTextoEditable( false ); // deshabilitar campoIntroducir

      try {
         salida.close();
         entrada.close();
         conexion.close();
      }
      catch( IOException excepcionES ) {
         excepcionES.printStackTrace();
      }
   }

   // enviar mensaje al cliente
   private void enviarDatos( String mensaje )
   {
      // enviar objeto al cliente
      try {
         salida.writeObject( "SERVIDOR>>> " + mensaje );
         salida.flush();
         mostrarMensaje( "\nSERVIDOR>>> " + mensaje );
      }

      // procesar problemas que pueden ocurrir al enviar el objeto
      catch ( IOException excepcionES ) {
         areaPantalla.append( "\nError al escribir objeto" );
      }
   }

   // método utilitario que es llamado desde otros subprocesos para manipular a
   // areaPantalla en el subproceso despachador de eventos
   private void mostrarMensaje( final String mensajeAMostrar )
   {
      // mostrar mensaje del subproceso de ejecución despachador de eventos
      SwingUtilities.invokeLater(
         new Runnable() {  // clase interna para asegurar que la GUI se actualice apropiadamente

            public void run() // actualiza areaPantalla
            {
               areaPantalla.append( mensajeAMostrar );
               areaPantalla.setCaretPosition(
                  areaPantalla.getText().length() );
            }

         }  // fin de la clase interna

      ); // fin de la llamada a SwingUtilities.invokeLater
   }

   // método utilitario que es llamado desde otros subprocesos para manipular a
   // campoIntroducir en el subproceso despachador de eventos
   private void establecerCampoTextoEditable( final boolean editable )
   {
      // mostrar mensaje del subproceso de ejecución despachador de eventos
      SwingUtilities.invokeLater(
         new Runnable() {  // clase interna para asegurar que la GUI se actualice apropiadamente

            public void run()  // establece la capacidad de modificar a campoIntroducir
            {
               campoIntroducir.setEditable( editable );
            }

         }  // fin de la clase interna

      ); // fin de la llamada a SwingUtilities.invokeLater
   }

   public static void main( String args[] )
   {
      JFrame.setDefaultLookAndFeelDecorated(true);
      Servidor aplicacion = new Servidor();
      aplicacion.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
      aplicacion.ejecutarServidor();
   }

}  // fin de la clase Servidor

El código fuente de Cliente.java:

import java.io.*;
import java.net.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class Cliente extends JFrame {
   private JTextField campoIntroducir;
   private JTextArea areaPantalla;
   private ObjectOutputStream salida;
   private ObjectInputStream entrada;
   private String mensaje = "";
   private String servidorChat;
   private Socket cliente;

   // inicializar servidorChat y configurar GUI
   public Cliente( String host )
   {
      super( "Cliente" );

      servidorChat = host; // establecer el servidor al que se va a conectar este cliente

      Container contenedor = getContentPane();

      // crear campoIntroducir y registrar componente de escucha
      campoIntroducir = new JTextField();
      campoIntroducir.setEditable( false );
      campoIntroducir.addActionListener(
         new ActionListener() {

            // enviar mensaje al servidor
            public void actionPerformed( ActionEvent evento )
            {
               enviarDatos( evento.getActionCommand() );
               campoIntroducir.setText( "" );
            }
         }
      ); 

      contenedor.add( campoIntroducir, BorderLayout.NORTH );

      // crear areaPantalla
      areaPantalla = new JTextArea();
      contenedor.add( new JScrollPane( areaPantalla ),
         BorderLayout.CENTER );

      setSize( 300, 150 );
      setVisible( true );

   } // fin del constructor de Cliente

   // conectarse al servidor y procesar mensajes del servidor
   private void ejecutarCliente()
   {
      // conectarse al servidor, obtener flujos, procesar la conexión
      try {
         conectarAServidor(); // Paso 1: crear un socket para realizar la conexión
         obtenerFlujos();      // Paso 2: obtener los flujos de entrada y salida
         procesarConexion(); // Paso 3: procesar la conexión
      }

      // el servidor cerró la conexión
      catch ( EOFException excepcionEOF ) {
         System.err.println( "El cliente termino la conexión" );
      }

      // procesar los problemas que pueden ocurrir al comunicarse con el servidor
      catch ( IOException excepcionES ) {
         excepcionES.printStackTrace();
      }

      finally {
         cerrarConexion(); // Paso 4: cerrar la conexión
      }

   } // fin del método ejecutarCliente

   // conectarse al servidor
   private void conectarAServidor() throws IOException
   {
      mostrarMensaje( "Intentando realizar conexión\n" );

      // crear Socket para realizar la conexión con el servidor
      cliente = new Socket( InetAddress.getByName( servidorChat ), 12345 );

      // mostrar la información de la conexión
      mostrarMensaje( "Conectado a: " +
         cliente.getInetAddress().getHostName() );
   }

   // obtener flujos para enviar y recibir datos
   private void obtenerFlujos() throws IOException
   {
      // establecer flujo de salida para los objetos
      salida = new ObjectOutputStream( cliente.getOutputStream() );
      salida.flush(); // vacíar búfer de salida para enviar información de encabezado

      // establecer flujo de entrada para los objetos
      entrada = new ObjectInputStream( cliente.getInputStream() );

      mostrarMensaje( "\nSe recibieron los flujos de E/S\n" );
   }

   // procesar la conexión con el servidor
   private void procesarConexion() throws IOException
   {
      // habilitar campoIntroducir para que el usuario del cliente pueda enviar mensajes
      establecerCampoTextoEditable( true );

      do { // procesar mensajes enviados del servidor

         // leer mensaje y mostrarlo en pantalla
         try {
            mensaje = ( String ) entrada.readObject();
            mostrarMensaje( "\n" + mensaje );
         }

         // atrapar los problemas que pueden ocurrir al leer del servidor
         catch ( ClassNotFoundException excepcionClaseNoEncontrada ) {
            mostrarMensaje( "\nSe recibió un objeto de tipo desconocido" );
         }

      } while ( !mensaje.equals( "SERVIDOR>>> TERMINAR" ) );

   } // fin del método procesarConexion

   // cerrar flujos y socket
   private void cerrarConexion()
   {
      mostrarMensaje( "\nCerrando conexión" );
      establecerCampoTextoEditable( false ); // deshabilitar campoIntroducir

      try {
         salida.close();
         entrada.close();
         cliente.close();
      }
      catch( IOException excepcionES ) {
         excepcionES.printStackTrace();
      }
   }

   // enviar mensaje al servidor
   private void enviarDatos( String mensaje )
   {
      // enviar objeto al servidor
      try {
         salida.writeObject( "CLIENTE>>> " + mensaje );
         salida.flush();
         mostrarMensaje( "\nCLIENTE>>> " + mensaje );
      }

      // procesar los problemas que pueden ocurrir al enviar el objeto
      catch ( IOException excepcionES ) {
         areaPantalla.append( "\nError al escribir el objeto" );
      }
   }

   // método utilitario que es llamado desde otros subprocesos para manipular a
   // areaPantalla en el subproceso despachador de eventos
   private void mostrarMensaje( final String mensajeAMostrar )
   {
      // mostrar mensaje del subproceso de ejecución de la GUI
      SwingUtilities.invokeLater(
         new Runnable() {  // clase interna para asegurar que la GUI se actualice apropiadamente

            public void run() // actualiza areaPantalla
            {
               areaPantalla.append( mensajeAMostrar );
               areaPantalla.setCaretPosition(
                  areaPantalla.getText().length() );
            }

         }  // fin de la clase interna

      ); // fin de la llamada a SwingUtilities.invokeLater
   }

   // método utilitario que es llamado desde otros subprocesos para manipular a
   // campoIntroducir en el subproceso despachador de eventos
   private void establecerCampoTextoEditable( final boolean editable )
   {
      // mostrar mensaje del subproceso de ejecución de la GUI
      SwingUtilities.invokeLater(
         new Runnable() {  // clase interna para asegurar que la GUI se actualice apropiadamente

            public void run()  // establece la capacidad de modificar campoIntroducir
            {
               campoIntroducir.setEditable( editable );
            }

         }  // fin de la clase interna

      ); // fin de la llamada a SwingUtilities.invokeLater
   }

   public static void main( String args[] )
   {
      JFrame.setDefaultLookAndFeelDecorated(true);
      Cliente aplicacion;

      if ( args.length == 0 )
         aplicacion = new Cliente( "127.0.0.1" );
      else
         aplicacion = new Cliente( args[ 0 ] );

      aplicacion.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
      aplicacion.ejecutarCliente();
   }

} // fin de la clase Cliente

Descargar código fuente

Los ejercicios utilizados en este post están basados en ejemplos del libro Cómo programar en Java de Deitel, y por lo tanto están bajo la licencia que esta editorial disponga.

122 Comentarios | deja el tuyo

2 enlaces entrantes

120 Comentarios en “[código] Conexiones Cliente-Servidor mediante sockets en Java”

  1. luisa Garces dice:

    Muchas gracias por tu contesta Cristian, si entiendo el codigo y bueno tratare de modificarlo a ver si puedo hacerlo distribuido. Me estoy leyendo lo de los hilos….; )

  2. luisa garces dice:

    Hola a todos otra vez, ahora tambien estoy tratando de hacer un recomendador de Musica en python, podrian darme una mano donde puedo encontrar ejemplo de este tipo. Gracias

  3. Cristian dice:

    ¿a qué te refieres con recomendador?

  4. ronald dice:

    Hola dime como podria hacer esto pero esta ves usa una base de datos??

  5. Javi dice:

    Hola a todos, solo quería comentar que parece una web muy, muy, muy buena, irdar las gracias porque me está sirviendo de mucho.

    Tambien quería preguntar si alguien supiera algo sobre SSL Sockets, socket seguros, si existieran clases predefinidas como las que se usan en este ejemplo, o información en algún sitio.

    Muchisimas gracias!!!

  6. Cristian dice:

    Hola Javi.

    Por supuesto que hay, aquí tienes un ejemplo:

    http://www.exampledepot.com/egs/javax.net.ssl/Server.html

    Un saludo!

  7. javi dice:

    Muchisimas gracias Cristian,

    voy ha probarlo y luego os cuento

  8. lizbeth dice:

    Muchas gracias me sirvio de mucho……
    Puedo hacer que el servidor haga operaciones que el cliente lemanda. x ejemplo el cliente manda 3 luego + luego 3 y el se vidor regresaria 6.

  9. Jaime Luis dice:

    muy bueno el codigo, pero quisiera saber como hacer para q varios clientes se conecten con el servidor, y los mensajes enviados se muestren, tanto como en el servidor y los cliente, algo parecido como el msn, cuando hablas con varios amigos al mismo tiempo en una sola ventana :p

  10. Jaime Luis dice:

    muy bueno el codigo, pero quisie………si tienes la solucion a este problema, x favor me lo puedes enviar a este msn rayo19_90@hotmail.com

  11. Jaime dice:

    como hago q funcione con MultiServerThread, para q varios clientes interactuen, no uno solo

  12. Juan Carlos dice:

    Saludos,
    Me ha parecido muy buena esta entrada, me ha servido mucho para lo que estoy desarrollando.
    Ahora me ha surgido una duda.
    Si Java en sí, tiene librerías para manejo de Bluetooth y OBEX.
    Digamos que, con base en esta estructura, quisiera transmitir y recibir de un móvil, pero no encuentro librerías que me sirvan para hacer el envío.

    Agradecería sus respuetas

  13. f-005@hotmail.com dice:

    Agregame a f-005@hotmail.com, me gusttaria preguntarte algunas dudas

  14. sebastian dice:

    Quiero hacer algo asi en php, ¿algun consejo?

    • c dice:

      en php??
      php por defecto no lleva interfaces graficas… (las hay, pero no es el caso)
      Si lo que quieres es hacer una pagina web que sea una sala de chat en php te recomiendo que aprendas algo de ajax para enviar peticiones al servidor sin recargar la página.
      hay mucho material x ahi…

¡Déjanos tu comentario!