Server-Client Socket

By Carlos Quintana

Bueno y como se los debía, aquí les traigo una implementación de un cliente servidor para chat sencillo, donde hay intercambio de mensajes a 2 vías entre el cliente y el servidor.

la premisa que sigue el programa es: Tener separada la interfaz gráfica del código que hace la comunicación con el servidor. ¿Por qué?

La primera razón es para acercar el desarrollo a una arquitectura desarrollada en capas: Interfaz, negocio, Almacenamiento, en este caso solo se usa Interfaz y se podría denominar negocio a la parte del código que actúa por detrás de la interfaz.

La segunda razón es por cuestión de separación por subprocesos, la interfaz gráfica es un subproceso y hay un segundo subproceso que envía y recibe la comunicación. Si esto no se separara podría ver como se cuelga la aplicación cuando el programa intente conectarse al servidor y se podrá quedar esperando toda la vida hasta que detecte una conexión, esta separación es la mejor practica que se puede hacer.

Trabajé un poco con las librerías de SWING, para hacer unos pequeños clientes GUI, donde básicamente se tienen 2 áreas de texto, un botón, y el Frame contenedor de todos estos elementos. también se agruparon en paneles para dar una mejor organización. Lo único para resaltar con respecto a la GUI es un ActionListener para cuando se da click en el botón, y cuando se presiona la tecla Enter estando en el área de escritura, estos 2 elementos ejecutan el mismo método.

y ahora el código fuente del Thread de comunicación del servidor:



package Codigo;

import interfazGrafica.ServerGUI;
import java.io.*;
import java.net.*;

public class Aplicacion extends Thread{

        private ServerSocket servidor;
	private Socket conexion;
	private ObjectOutputStream OUT;
	private ObjectInputStream IN;
	private ServerGUI ig;
	private long i;
	public static String textoEnvio;

	public static void main(String[] args) {
			/*
		 * Inicio del Servidor de chat
		 *
 		 * -Se crea la interfáz gráfica
                 * -Se crea el subproceso de comunicación
		 * -Se inicia el subproceso.
		 */

		ServerGUI ui=new ServerGUI("Servidor chat");
		Aplicacion ap=new Aplicacion(ui);
		ap.start();
	}

	/*
	 * Método para impresión (Depuración).
	 */

	public static void imprimir(String x){
		System.out.println(x);
	}

	/*
	 * Constructor
	 * recibe la interfáz gráfica, puesto que se
	 * comunicará con ella
	 */	

        public Aplicacion(ServerGUI ig){
		textoEnvio="";
		this.ig=ig;
		i=0;
	}
		/*
	 * (non-Javadoc)
	 * @see java.lang.Thread#run()
	 *
 	 * Método del subproceso, todo esto se hará
	 * en segundo plano
	 */	

         public void run(){
		while (true){
                         try {
				servidor=new ServerSocket(5000);
			}catch (IOException e1) {} 

			while(true){
				try{

					conexion=servidor.accept();

			               /*
			                * Flujos de datos
			                * Envia y recibe
			                */

					OUT=new ObjectOutputStream(
						conexion.getOutputStream());
					enviar();

					IN=new ObjectInputStream(
						conexion.getInputStream());
					recibir();
					conexion.close();	

			       }catch(IOException e){
			       } catch (ClassNotFoundException e) {}
					System.out.println(i++);
			}
		}
	}

	/*
	 * Este método envia con un OutputStream
	 * los datos de la variable textoEnvío
	 * UTF = texto simple
	 *
 	 * la variable textoEnvio es cambiada
	 * desde la interfáz grafíca.
	 */

         public void enviar() throws IOException{
		OUT.writeUTF(textoEnvio);
		OUT.flush();
		textoEnvio="";
	}

		/*
	 * Este método recibe lo enviado por el cliente
	 * y lo manda hacia la interfáz gráfica mientras
	 * lo recibido no sea nulo.
	 */

	public void recibir() throws IOException,
 					ClassNotFoundException{

		String texto= IN.readUTF();
		if (!texto.trim().equals("")){
			ig.setRecibido("Cliente>> " + texto);
		}
	}
}

ahora, vamos con el Código fuente de la interfáz gráfica del servidor:





package interfazGrafica;

import java.awt.BorderLayout;
import java.awt.event.*;
import javax.swing.*;
import Codigo.Aplicacion;

@SuppressWarnings("serial")
public class ServerGUI extends JFrame{

        /*
	 * Elementos basicos para la ventana
	 */

	private JButton boton1;
        private JPanel panel1,panel2,panel3;
	private JTextArea texto,etiqueta1;
	private JScrollPane scroll;

 	public ServerGUI(String titulo) {
		/*
		 * 3 Contenedores de elementos
		 */

		panel1=new JPanel();
		panel2=new JPanel();
		panel3=new JPanel();

		/*
		 * Etiqueta es la zona de mensajes
		 * escritos por el cliente y por
		 * el servidor, se agrega posteriormente
		 * al contenedor 1
		 */

		etiqueta1= new JTextArea(10,30);
		etiqueta1.setEditable(false);
		etiqueta1.setLineWrap(true);
		scroll = new JScrollPane(etiqueta1);
		scroll.setVerticalScrollBarPolicy(
                         JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
		panel1.add(scroll);

		/*
		 * Este elemento contiene un area de texto
		 * todo el texto ingresado aqui
		 * sera el enviado al cliente
		 *
 		 * dandole un Clik o presionando la tecla
		 * Enter
		 *
 		 * se agrega al contenedor 3
		 */

		texto=new JTextArea(100,40);
		texto.setLineWrap(true);
		texto.setText("Escriba aqui");
		texto.addKeyListener(new KeyListener() {

                   public void keyPressed(KeyEvent arg0) {}

                   public void keyReleased(KeyEvent arg0) {

			if( arg0.getKeyCode() == KeyEvent.VK_ENTER ){
				eventoEnvio();
			}
		  }

                   public void keyTyped(KeyEvent arg0) {}
                });
		panel3.add(texto);
						/*
		 * Botón para opcionalmente dar click
		 * y enviar los datos, este llama el metodo
		 * eventoEnvio() así como la tecla Enter
		 * del textArea anterior
		 */

		boton1=new JButton();
		boton1.setText("Enviar");
		boton1.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				eventoEnvio();
			}
		});

		/*
		 * Metodos nativos del Frame
		 * esta clase esta extendida de un Frame
		 * por lo tanto no se ve por ninguna parte
		 * Frame m= new Frame()
		 *
 		 * add(elemento) para agregar los elementos
		 * al frame
		 *
 		 * location, posicion de la ventana
		 * titulo
		 * tamaño
		 * y finalmente se hace visible al usuario
		 * una vez que se instancia este objeto
		 */

		panel2.add(boton1);
		add(panel1,BorderLayout.NORTH);
		add(panel2,BorderLayout.SOUTH);
		add(panel3,BorderLayout.CENTER);
		setLocationRelativeTo(null);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setTitle(titulo);
		setSize(450,300);
		setVisible(true);

	}

        /*
	 * Metodo encargado de enviar los datos
	 * al otro subproceso, para que este los
	 * envíe por Streaming
	 */		

        public void eventoEnvio(){
		Aplicacion.textoEnvio=texto.getText();
		setRecibido("Server>>" + texto.getText());
		texto.setText("");
       }

	/*
	 * Hace posible la actualización
	 * del area de mensajes, desde el otro subproceso
	 */

	public void setRecibido(String text){
		this.etiqueta1.setText(etiqueta1.getText() + text);
	}
 }

Para el que siguió todo hasta aquí le digo que vamos a la mitad.
ahora vamos con el Cliente:

su Thread de comunicación va así:





package codigo;
import java.io.*;
import java.net.*;
import interfazGrafica.InterfazGrafica;

public class Cliente extends Thread{

        private InterfazGrafica ig;
	private Socket cliente;
	private ObjectInputStream IN;
	private ObjectOutputStream OUT;
	private long i;

	public static String textoEnvio;

        public static void main(String[] args) {

                /*
		 * Método de inicio del cliente de chat
		 *
 		 * -Se crea una interfáz gráfica
		 * -Se crea un subproceso de comunicación
		 * -Se inicia el subproceso
		 */

		InterfazGrafica i=new InterfazGrafica("Cliente Chat");
		Cliente c=new Cliente(i);
		c.start();
	}

		/*
	 * Al igual que el servidor, el subproceso
	 * recibe la referencia de la interfáz gráfica
	 */

	public Cliente(InterfazGrafica ig) {
		this.ig=ig;
		textoEnvio="";
	}

		/*
	 * (non-Javadoc)
	 * @see java.lang.Thread#run()
	 *
 	 * Metodo propio del subproceso
	 * ejecuta un ciclo de conexión
	 * para envío/recepción de datos
	 * mediante sockets
	 */

	public void run(){
		while (true){
			try {
				/*
				 * El socket se conecta a un servidor
				 * en la siguiente dirección IP
			         */

				cliente=new Socket("192.168.1.73",5000);

				/*
				 * Stream de datos
				 * recibe y luego envia
				 *
 				 * el orden no importa
				 */

				IN=new ObjectInputStream(
					cliente.getInputStream());
				recibir();
				OUT=new ObjectOutputStream(
                                        cliente.getOutputStream());
				enviar();

                       } catch (UnknownHostException e) {}
			  catch (IOException e) {}
			  catch (ClassNotFoundException e) {}
	  		  imprimir(i++ + "");
		}
	}
		/*
	 * Este método lee de un InputStream los datos
	 * enviados desde el servidor
	 * y si no son nulos los manda
	 * hacia la interfáz gráfica a la zona de mensajes
	 * recibe UTF= texto simple
	 */

	public void recibir() throws IOException,
 					ClassNotFoundException{

		String texto= IN.readUTF();
		if (!texto.trim().equals("")){
			ig.setRecibido("server>> " + texto);
	       }
	}

	/*
	 * Con un OutputStream, este método envía
	 * los datos de la variable textoEnvio
	 * hacia el servidor
	 *
 	 * al igual que el servidor, los datos de
	 * esta variable textoEnvio, son asignados
	 * por la interfaz gráfica.
	 */
         public void enviar() throws IOException{
		OUT.writeUTF(textoEnvio);
		OUT.flush();
		textoEnvio="";
          }

	/*
	 * Método para depurar por consola
	 */
	public void imprimir(String x){
		System.out.println(x);
	}
}

y por último..
la interfaz gráfica del cliente:




package interfazGrafica;

import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.*;

import codigo.Cliente;

@SuppressWarnings("serial")
public class InterfazGrafica extends JFrame{

        private JButton boton1;
	private JPanel panel1,panel2,panel3;
	public JTextArea texto,etiqueta1;
	private JScrollPane scroll;

	public InterfazGrafica(String titulo) {
		/*
		 * Crear 3 Contenedores
		 * que seran ubicados en diferentes
		 * partes del Frame principal
		 */

		panel1=new JPanel();
		panel2=new JPanel();
		panel3=new JPanel();

		/*
		 * Se crea 1 textArea para imprimir
		 * los mensajes enviados y recibidos desde
		 * el cliente, ademas se crea un Scroll
		 * que encierra el textArea
		 * y OJO!! se agrega el Scroll al contenedor,
		 * no el textArea
		 */

		etiqueta1= new JTextArea(10,30);
		etiqueta1.setEditable(false);
		etiqueta1.setAutoscrolls(true);
		scroll = new JScrollPane(etiqueta1);
		scroll.setVerticalScrollBarPolicy(
			        JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
		panel1.add(scroll);

		/*
		 * Se crea un segundo textArea, aquí se
		 * escribirá el texto para enviar hacia
		 * el servidor
		 *
		 * este tiene un KeyListener
		 * básicamente cuando se le da enter
		 * ejecuta un método de envío
		 *
 		 * se agrega al contenedor 3
		 */

		texto=new JTextArea(100,40);
		texto.setText("Escriba aqui");
		texto.setLineWrap(true);
		texto.addKeyListener(new KeyListener() {
                   public void keyPressed(KeyEvent arg0) {}

                   public void keyReleased(KeyEvent arg0) {
				if( arg0.getKeyCode() == KeyEvent.VK_ENTER ){
					/*
					 * LLama al metodo cuando se le da
 					 * enter
					 */
					eventoEnvio();
				}
			}

                  public void keyTyped(KeyEvent arg0) {}
              });
              panel3.add(texto);	

		/*
		 * Se crea un boton y  se le asigna la accion
		 * de enviar datos que actua igual al Enter
		 * del textArea anterior y se agrega al contenedor 2
		 */

		boton1=new JButton();
		boton1.setText("Enviar");
		boton1.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
				eventoEnvio();
			}
		});

		panel2.add(boton1);

		/*
		 * Ya que estan los 3 contenedores con sus objetos
		 * los agrego al Frame
		 *
 		 * ¿Donde esta el Frame?
		 *
 		 * esta clase hereda de Frame, por lo tanto
		 * solo llamo a los metodos de�
		 * add(elemento)  agregar elemento al frame
		 * su localización en pantalla
		 * si titulo
		 * su tamaño
		 * y finalmente lo hago visible al usuario
		 */

		add(panel1,BorderLayout.NORTH);
		add(panel3,BorderLayout.CENTER);
		add(panel2,BorderLayout.SOUTH);
		setLocationRelativeTo(null);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		setTitle(titulo);
		setSize(450,300);
		setVisible(true);

	}

        /*
	 * Este metodo es la comunicación con el otro subproceso
	 * se encarga de enviarle una cadena de texto
	 * a una variable estatica Cliente.textoEnvio
	 * ese texto será el enviado hacia el servidor
	 *
 	 * además la interfáz borrará el texto, y lo
	 * imprimirá en el área de mensajes.
         */		

         public void eventoEnvio(){
		setRecibido("Cliente>> " + texto.getText());
		Cliente.textoEnvio=texto.getText();
	        texto.setText("");
	}

        /*
	 * Este metodo hace posible que los mensajes
	 * recibidos desde el servidor sean impresos
	 * en el area de texto, será ejecutado siempre
	 * desde el otro subproceso.
	 */

	public void setRecibido(String text){
		this.etiqueta1.setText(etiqueta1.getText() + text);
	}
 }

Bueno ya que tenemos los 4 códigos, veamos como queda su estructura para que no haya confusión

y finalmente, veamos como queda funcionando:

Bueno y creo que por fin terminamos, espero les sirva de algo, saludos

  1. Fantastic items from you, man. I have have in mind your stuff prior to and you’re just too magnificent. I actually like what you have acquired right here, certainly like what you are stating and the way in which in which you assert it. You make it enjoyable and you continue to care for to stay it wise. I can not wait to learn far more from you. That is really a tremendous website.

  2. Do you mind if I quote a few of your articles as long as I provide credit and sources back to your weblog? My blog site is in the exact same area of interest as yours and my users would genuinely benefit from some of the information you provide here. Please let me know if this okay with you. Thanks a lot!

  3. Thanks a lot for sharing this with all of us you really know what you are talking approximately! Bookmarked. Kindly also consult with my site =). We will have a link change arrangement between us!

  4. Carma Sewell dice:

    What i do not realize is actually how you’re not actually much more well-liked than you might be now. You’re very intelligent. You realize therefore considerably relating to this subject, produced me personally consider it from a lot of varied angles. Its like men and women aren’t fascinated unless it’s one thing to accomplish with Lady gaga! Your own stuffs excellent. Always maintain it up!

  5. Cammie dice:

    Got it! Thanks a lot again for hleping me out!

  6. Lexus dice:

    Stay with this guys, you’re hlpeing a lot of people.

  7. cardaddy dice:

    I really respect what you’re writing here. Keep working that way. Take care!


eight + = 10