Está en la página 1de 28

FACULTAD DE INGENERÍA DE SISTEMAS Y

ELECTRÓNICA
Escuela de Ingeniería de Sistemas e Informática

Z226 SISTEMAS DISTRIBUIDOS


Junior Arturo Barboza Vidarte 1614216

Chat con Sockets en Java


El chat que se muestra en esta publicación utiliza sockets para la
comunicación, se desarrolló utilizando Java 1.8 y NetBeans 8.2. El ejemplo
está formado por dos aplicaciones, el cliente y el servidor.

Cómo funciona el chat


Para que el chat funcione correctamente, debe ejecutarse primero la
aplicación servidor, en la que el cliente debe ingresar el puerto por el cual el
servidor escuchara las conexiones de los clientes que participen en el chat.
Puede dejarse el puerto por defecto o puede indicarse uno diferente, que no
esté siendo utilizado por ninguna otra aplicación.

Si el servidor inicia correctamente se mostrará una ventana como la


siguiente.

1
Junior Arturo Barboza Vidarte 1614216

En este punto ya podremos ejecutar aplicaciones cliente que se conecten al


chat, al ejecutar la aplicación cliente nos pedirá la IP de la máquina en la
que se ejecuta la aplicación servidor, por defecto tiene la dirección
de localhost, en este caso se utiliza esta porque el servidor y todos los
clientes se están ejecutando en la misma máquina. Además de la dirección
IP, se debe ingresar el puerto por el que el servidor escucha las conexiones
de los clientes y el nombre del usuario que se desea conectar.

Si la conexión es correcta, entonces se muestra una ventana que tiene


una JTextArea en la que se muestra el historial de las conversaciones,
un JComboBox que muestra el listado de los usuarios conectados a los que
se puede enviar mensajes, un JTextField en el que se puede ingresar los
mensajes a enviar y un JButton para enviar el mensaje.

2
Junior Arturo Barboza Vidarte 1614216

Si solamente está conectado un cliente, no habrá nadie a quien se le pueda


enviar mensajes, si el cliente intentara enviar uno se mostraría el siguiente
mensaje de información

Al ejecutar más de un cliente estos ya podrán comunicarse entre sí.

3
Junior Arturo Barboza Vidarte 1614216

Luego de que los clientes envíen y reciban múltiples mensajes se tiene algo
como lo siguiente.

Si se cierran las ventanas, la aplicación cliente considerará que el usuario


está cerrando sesión y enviará una notificación al servidor para elimine a
este cliente de la lista de clientes y notifique al resto de los usuarios
conectados que deben eliminarlo de su lista de contactos.
En la aplicación servidor se muestra un log, en el que se informa cuando un
usuario inicia o cierra sesión.

4
Junior Arturo Barboza Vidarte 1614216

Si el servidor se cierra repentinamente, mientras aún hay clientes


conectados, estas aplicaciones cliente se cerrarán luego de mostrar el
siguiente mensaje.

Si se intenta ejecutar una aplicación cliente sin haber ejecutado antes la


aplicación servidor o se ingresa una dirección IP o un puerto equivocado se
mostrará el siguiente mensaje.

5
Junior Arturo Barboza Vidarte 1614216

Lógica del chat


El chat consiste de dos aplicaciones, el cliente y el servidor, el servidor se
ejecuta una vez y el cliente se ejecuta N veces, donde N es el número de
usuarios que participarán en el chat, todos los mensajes que los usuarios del
chat se envían entre sí pasan a través del servidor, cuando un cliente quiere
enviar un mensaje a otro, le envía el mensaje al servidor indicándole su
destinatario y el servidor se encarga de reenviar este mensaje, el servidor
también se encarga de indicarle a todos los usuarios cuando un usuario nuevo
se conecta al chat, para que puedan incluirlo en su lista de contactos y por
ende en la conversación. Asimismo, cuando un cliente se desconecta, el
servidor se encarga de informar a todos los usuarios que deben eliminar al
cliente que cerró su sesión de la lista de contactos porque ya no podrán
enviarle mensajes. No hay comunicación directa entre clientes, el servidor
siempre es intermediario entre la comunicación de los clientes. Si se ejecutara
el servidor y luego se ejecutaran cuatro clientes que se conectaran a dicho
servidor, la comunicación sería como se muestra en la siguiente imagen.

Funcionamiento del servidor


El servidor tiene tres clases, que se describen a continuación:

 VentanaS: esta clase gestiona la interfaz gráfica del servidor, que


básicamente muestra un log de las principales acciones del servidor
(conexión y desconexión de clientes).
 Servidor: Esta clase gestiona a los clientes que se conectan al servidor,
es un hilo que tiene como principal función escuchar constantemente
en caso de que algún nuevo cliente quiera conectarse al chat.
 HiloCliente: Cada vez que un nuevo cliente se conecta, dentro de la
clase servidor se instancia un nuevo HiloCliente y se agrega a la lista
de clientes. Este hilo cuenta con un socket con el que puede enviar y
recibir mensajes del cliente con el que está conectado y tiene como
principal función escuchar constantemente en caso de que el cliente
envíe mensajes.

A continuación, se muestra una imagen que ilustra el funcionamiento de la


aplicación servidor.

6
Junior Arturo Barboza Vidarte 1614216

Funcionamiento del cliente

El cliente tiene dos clases, que se describen a continuación:

 VentanaC: esta clase gestiona la interfaz gráfica del servidor, que


básicamente muestra una lista de contactos, un historial de
conversación, una caja de texto para ingresar nuevos mensajes y un
botón que permite enviar mensajes.
 Cliente: Cuando el cliente se instancia y se conecta con el servidor, se
crea un hilo que está escuchar constantemente en caso de que el
servidor envíe mensajes. Este cliente cuenta con un socket con el que
puede enviar y recibir mensajes del servidor con el que está conectado.

A continuación, se muestra una imagen que ilustra el funcionamiento de la


aplicación cliente.

7
Junior Arturo Barboza Vidarte 1614216

Programa Servidor
1. HiloCliente.java:

/*
* Practica Calificada 2
* Junior Barboza Vidarte
* Mayo 2019
*/

package chatservidor;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.LinkedList;

/**
* Los objetos de esta clase son hilos que al correr escuchan
* permanentemente lo que los clientes puedan decir, hay un hilo para
* cada cliente que se conecta al servidor y dicho hilo tiene como
* función escuchar solamente a ese cliente.
* @author JuniorBarboza
*/
public class HiloCliente extends Thread{
/**
* Socket que se utiliza para comunicarse con el cliente.
*/
private final Socket socket;
/**
* Stream con el que se envían objetos al servidor.
*/
private ObjectOutputStream objectOutputStream;
/**
* Stream con el que se reciben objetos del servidor.
*/
private ObjectInputStream objectInputStream;
/**
* Servidor al que pertenece este hilo.
*/
private final Servidor server;
/**
* Identificador único del cliente con el que este hilo se
*comunica.
*/
private String identificador;
/**
* Variable booleana que almacena verdadero cuando este hilo está
* escuchando lo que el cliente que atiende está diciendo.
*/
private boolean escuchando;
/**
* Método constructor de la clase hilo cliente.
* @param socket
* @param server
*/

public HiloCliente(Socket socket,Servidor server) {

8
Junior Arturo Barboza Vidarte 1614216

this.server=server;
this.socket = socket;
try {
objectOutputStream = new
objectOutputStream(socket.getOutputStream());
objectInputStream =
new ObjectInputStream(socket.getInputStream());
} catch (IOException ex) {
System.err.println("Error en la inicialización del
ObjectOutputStream y el ObjectInputStream");
}
}
/**
* Método encargado de cerrar el socket con el que se esta
* comunicando.
*/
public void desconnectar() {
try {
socket.close();
escuchando=false;
} catch (IOException ex) {
System.err.println("Error al cerrar el socket de
comunicación con el cliente.");
}
}
/**
* Sobreescritura del método de Thread, es acá en donde se monta
* el ciclo infinito.
*/
public void run() {
try{
escuchar();
} catch (Exception ex) {
System.err.println("Error al llamar al método readLine del
hilo del cliente.");
}
desconnectar();
}

/**
* Método que constantemente está escuchando todo lo que es
* enviado por el cliente que se comunica con él.
*/
public void escuchar(){
escuchando=true;
while(escuchando){
try {
Object aux=objectInputStream.readObject();
if(aux instanceof LinkedList){
ejecutar((LinkedList<String>)aux);
}
} catch (Exception e) {
System.err.println("Error al leer lo enviado por el
cliente.");
}
}
}
/**
* Método que realiza determinadas acciones dependiendo de lo que
* el socket haya recibido y lo que este le envie el método, en
* él se manejan una serie de códigos.

9
Junior Arturo Barboza Vidarte 1614216

* @param lista
*/
public void ejecutar(LinkedList<String> lista){
// 0 - El primer elemento de la lista es siempre el tipo
String tipo=lista.get(0);
switch (tipo) {
case "SOLICITUD_CONEXION":
// 1 - Identificador propio del nuevo usuario
confirmarConexion(lista.get(1));
break;
case "SOLICITUD_DESCONEXION":
// 1 - Identificador propio del nuevo usuario
confirmarDesConexion();
break;
case "MENSAJE":
// 1 - Cliente emisor
// 2 - Cliente receptor
// 3 - Mensaje
String destinatario=lista.get(2);
server.clientes
.stream()
.filter(h ->
(destinatario.equals(h.getIdentificador())))
.forEach((h) -> h.enviarMensaje(lista));
break;
default:
break;
}
}
/**
* Método para enviar un mensaje al cliente atraves del socket.
* @param lista
*/
private void enviarMensaje(LinkedList<String> lista){
try {
objectOutputStream.writeObject(lista);
} catch (Exception e) {
System.err.println("Error al enviar el objeto al
cliente.");
}
}
/**
* Una vez conectado un nuevo cliente, este método notifica a
* todos los clientes conectados que hay un nuevo cliente para que
* lo agreguen a sus contactos.
* @param identificador
*/
private void confirmarConexion(String identificador) {
Servidor.correlativo++;
this.identificador=Servidor.correlativo+" - "+identificador;
LinkedList<String> lista=new LinkedList<>();
lista.add("CONEXION_ACEPTADA");
lista.add(this.identificador);
lista.addAll(server.getUsuariosConectados());
enviarMensaje(lista);
server.agregarLog("\nNuevo cliente: "+this.identificador);
// enviar a todos los clientes el nombre del nuevo usuario
// conectado excepto a él mismo
LinkedList<String> auxLista=new LinkedList<>();
auxLista.add("NUEVO_USUARIO_CONECTADO");
auxLista.add(this.identificador);

10
Junior Arturo Barboza Vidarte 1614216

server.clientes
.stream()
.forEach(cliente -> cliente.enviarMensaje(auxLista));
server.clientes.add(this);
}
/**
* Método que retorna el identificador único del cliente dentro
* del chat.
* @return
*/
public String getIdentificador() {
return identificador;
}
/**
* Método que se invoca cuando el usuario al que atiende este
* hilo decide desconectarse, si eso pasa, se tiene que informar
* al resto de los usuarios que ya no pueden enviarle mensajes y
* que deben quitarlo de su lista de contactos.
*/
private void confirmarDesConexion() {
LinkedList<String> auxLista=new LinkedList<>();
auxLista.add("USUARIO_DESCONECTADO");
auxLista.add(this.identificador);
server.agregarLog("\nEl cliente \""+this.identificador+"\" se
ha desconectado.");
this.desconnectar();
for(int i=0;i<server.clientes.size();i++){
if(server.clientes.get(i).equals(this)){
server.clientes.remove(i);
break;
}
}
server.clientes
.stream()
.forEach(h -> h.enviarMensaje(auxLista));
}
}

11
Junior Arturo Barboza Vidarte 1614216

2. Servidor.java:

/*
* Practica Calificada 2
* Junior Barboza Vidarte
* Mayo 2019
*/
package chatservidor;

import java.net.ServerSocket;
import java.net.Socket;
import java.util.LinkedList;
import javax.swing.JOptionPane;

/**
* Clase en la que se maneja la comunicación del lado del servidor.
* @author JuniorBarboza
*/
public class Servidor extends Thread{
/**
* Socket servidor que tiene como principal función escuchar
* cuando los clientes se conectan para incluirlos en el chat.
*/
private ServerSocket serverSocket;
/**
* Lista de todos los hilos de comunicación, para cada cliente se
* instancia uno de estos hilos ya que cada hilo está escuchando
* permanentemente lo que dicho cliente envía al servidor.
*/
LinkedList<HiloCliente> clientes;
/**
* Variable que almacena la ventana que gestiona la interfaz
* gráfica del servidor.
*/
private final VentanaS ventana;
/**
* Variable que almacena el puerto que el servidor usará para
* escuchar.
*/
private final String puerto;
/**
* Correlativo para diferenciar a los múltiples clientes que se
* conectan, si se conectaran, por ejemplo, dos usuarios con el
* mismo nombre, se podrían diferenciar por este correlativo.
*/
static int correlativo;
/**
* Constructor del servidor.
* @param puerto
* @param ventana
*/
public Servidor(String puerto, VentanaS ventana) {
correlativo=0;
this.puerto=puerto;
this.ventana=ventana;
clientes=new LinkedList<>();
this.start();
}

12
Junior Arturo Barboza Vidarte 1614216

/**
* Método sobre el que corre el bucle infinito que tiene como
* función escuchar permenentemente en espera de conexiones de
* nuevos clientes.
*/
public void run() {
try {
serverSocket = new ServerSocket(Integer.valueOf(puerto));
ventana.addServidorIniciado();
while (true) {
HiloCliente h;
Socket socket;
socket = serverSocket.accept();
System.out.println("Nueva conexion entrante:"+socket);
h=new HiloCliente(socket, this);
h.start();
}
} catch (Exception e) {
JOptionPane.showMessageDialog(ventana,
"El servidor no se ha podido
iniciar, \n"
+ "puede que haya ingresado
un puerto
incorrecto.\n"
+ "Esta aplicación se
cerrará.");
System.exit(0);
}
}
/**
* Ciclo que devuelve una lista con los identificadores de todos
* los clientes conectados.
* @return
*/
LinkedList<String> getUsuariosConectados() {
LinkedList<String>usuariosConectados=new LinkedList<>();
clientes.stream().forEach(c ->
usuariosConectados.add(c.getIdentificador()));
return usuariosConectados;
}
/**
* Método que agrega una linea al log de la interfaz gráfica del
* servidor.
* @param texto
*/
void agregarLog(String texto) {
ventana.agregarLog(texto);
}
}

13
Junior Arturo Barboza Vidarte 1614216

3. VentanaS.java

/*
* Practica Calificada 2
* Junior Barboza Vidarte
* Mayo 2019
*/
package chatservidor;

import java.awt.GridLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;

/**
* Clase que gestiona la interfaz gráfica del servidor, que muestra un
* log de las conexiones y desconexiones de los diferentes clientes,
* así como un mensaje de confirmación de que el servidor está
* corriendo correctamente.
* @author JuniorBarboza
*/
public class VentanaS extends javax.swing.JFrame {
private final String DEFAULT_PORT="10101";
private final Servidor servidor;

/**
* Creates new form VentanaS
*/
public VentanaS() {
initComponents();
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
String puerto=getPuerto();
servidor=new Servidor(puerto, this);
}

/**
* This method is called from within the constructor to initialize
* the form.
* WARNING: Do NOT modify this code. The content of this method is
* always regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">
private void initComponents() {

jScrollPane1 = new javax.swing.JScrollPane();


txtClientes = new javax.swing.JTextArea();

setDefaultCloseOperation(javax.swing.WindowConstants
.EXIT_ON_CLOSE);
setTitle("Servidor");

txtClientes.setColumns(20);
txtClientes.setRows(5);
txtClientes.setBorder(javax.swing.BorderFactory
.createTitledBorder("Log del servidor"));
txtClientes.setCursor(newjava.awt.Cursor
(java.awt.Cursor.TEXT_CURSOR));

14
Junior Arturo Barboza Vidarte 1614216

jScrollPane1.setViewportView(txtClientes);

javax.swing.GroupLayout layout = new


javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.
swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(jScrollPane1, javax.swing
.GroupLayout.PREFERRED_SIZE, 264,
javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE,
Short.MAX_VALUE))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.
Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(21, 21, 21)
.addComponent(jScrollPane1, javax.swing.GroupLayout
.PREFERRED_SIZE, 310, javax.swing
.GroupLayout.PREFERRED_SIZE)
.addContainerGap(25, Short.MAX_VALUE))
);

pack();
setLocationRelativeTo(null);
}// </editor-fold>

/**
* @param args the command line arguments
*/
public static void main(String args[]) {
/* Set the Nimbus look and feel */
//<editor-fold defaultstate="collapsed" desc=" Look and feel
//setting code (optional) ">
/* If Nimbus (introduced in Java SE 6) is not available, stay
* with the default look and feel.
* For details see
* http://download.oracle.com/javase/tutorial/uiswing/
* lookandfeel/plaf.html
*/
try {
for (javax.swing.UIManager.LookAndFeelInfo info :
javax.swing.UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
javax.swing.UIManager.setLookAndFeel(
info.getClassName());
break;
}
}
} catch (ClassNotFoundException ex) {
java.util.logging.Logger.getLogger(VentanaS
.class.getName()).log(java.util.logging.Level.SEVERE,
null, ex);
} catch (InstantiationException ex) {
java.util.logging.Logger.getLogger(VentanaS
.class.getName()).log(java.util.logging.Level.SEVERE,
null, ex);

15
Junior Arturo Barboza Vidarte 1614216

} catch (IllegalAccessException ex) {


java.util.logging.Logger.getLogger(VentanaS
.class.getName()).log(java.util.logging.Level.SEVERE,
null, ex);
} catch (javax.swing.UnsupportedLookAndFeelException ex) {
java.util.logging.Logger.getLogger(VentanaS.class
.getName()).log(java.util.logging.Level.SEVERE, null, ex);
}
//</editor-fold>

/* Create and display the form */


java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new VentanaS().setVisible(true);
}
});
}

// Variables declaration - do not modify


private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JTextArea txtClientes;
// End of variables declaration

/**
* Método que agrega una línea de texto al log.
* @param texto
*/
void agregarLog(String texto) {
txtClientes.append(texto);
}
/**
* Método que abre una ventana para que el usuario ingrese el
* puerto que desea utilizar para que el servidor escuche.
* @return
*/
private String getPuerto() {
String p=DEFAULT_PORT;
JTextField puerto = new JTextField(20);
puerto.setText(p);
JPanel myPanel = new JPanel();
myPanel.setLayout(new GridLayout(2, 1));
myPanel.add(new JLabel("Puerto de la conexión:"));
myPanel.add(puerto);
int result = JOptionPane.showConfirmDialog(null, myPanel,
"Configuraciones de la comunicación",
JOptionPane.OK_CANCEL_OPTION);
if (result == JOptionPane.OK_OPTION) {
p=puerto.getText();
}else{
System.exit(0);
}
return p;
}
/**
* Método que agrega un mensaje de confirmación al log cuando el
* servidor está corriendo correctamente.
*/
void addServidorIniciado() {
txtClientes.setText("Inicializando el servidor... [Ok].");
}
}

16
Junior Arturo Barboza Vidarte 1614216

Programa Cliente
1. Cliente.java:

/*
* Practica Calificada 2
* Junior Barboza Vidarte
* Mayo 2019
*/
package chatcliente;

import java.io.*;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.LinkedList;
import javax.swing.JOptionPane;

/**
* Clase en la que se maneja la comunicación del lado del cliente.
* @author JuniorBarboza
*/
public class Cliente extends Thread {
/**
* Socket utilizado para comunicarse con el servidor.
*/
private Socket socket;
/**
* Stream utilizado para el envío de objetos al servidor.
*/
private ObjectOutputStream objectOutputStream;
/**
* Stream utilizado para el envío de objetos al servidor.
*/
private ObjectInputStream objectInputStream;
/**
* Ventana utilizada para la interfaz gráfica del cliente.
*/
private final VentanaC ventana;
/**
* Identificador único del cliente dentro del chat.
*/
private String identificador;
/**
* Variable que determina si el cliente escucha o no al servidor,
* una vez que se arranca el hilo de comunicación del cliente.
*/
private boolean escuchando;
/**
* Variable que almacena la IP del host en el que se ejecuta el
* servidor.
*/
private final String host;
/**
* Varable que almacena el puerto por el cual el servidor escucha
* las conexiones de los diversos clientes.
*/
private final int puerto;
/**
* Constructor de la clase cliente.
* @param ventana

17
Junior Arturo Barboza Vidarte 1614216

* @param host
* @param puerto
* @param nombre
*/
Cliente(VentanaC ventana, String host, Integer puerto, String
nombre) {
this.ventana=ventana;
this.host=host;
this.puerto=puerto;
this.identificador=nombre;
escuchando=true;
this.start();
}
/**
* Método run del hilo de comunicación del lado del cliente.
*/
public void run (){
try {
socket=new Socket(host, puerto);
objectOutputStream=new ObjectOutputStream(socket
.getOutputStream());
objectInputStream=new ObjectInputStream(socket
.getInputStream());
System.out.println("Conexion exitosa!!!!");
this.enviarSolicitudConexion(identificador);
this.escuchar();
} catch (UnknownHostException ex) {
JOptionPane.showMessageDialog(ventana, "Conexión rehusada,
servidor desconocido, \n"
+ "puede que haya ingresado
una ip incorrecta\n"
+ "o que el servidor no este
corriendo.\n"
+ "Esta aplicación se
cerrará.");
System.exit(0);
} catch (IOException ex) {
JOptionPane.showMessageDialog(ventana, "Conexión rehusada,
error de Entrada/Salida,\n"
+ "puede que haya ingresado
una ip o un puerto\n"
+ "incorrecto, o que el
servidor no este
corriendo.\n"
+ "Esta aplicación se
cerrará.");
System.exit(0);
}

/**
* Método que cierra el socket y los streams de comunicación.
*/
public void desconectar(){
try {
objectOutputStream.close();
objectInputStream.close();
socket.close();
escuchando=false;
} catch (Exception e) {

18
Junior Arturo Barboza Vidarte 1614216

System.err.println("Error al cerrar los elementos de


comunicación del cliente.");
}
}
/**
* Método que envía un determinado mensaje hacia el servidor.
* @param cliente_receptor
* @param mensaje
*/
public void enviarMensaje(String cliente_receptor, String
mensaje){
LinkedList<String> lista=new LinkedList<>();
//tipo
lista.add("MENSAJE");
//cliente emisor
lista.add(identificador);
//cliente receptor
lista.add(cliente_receptor);
//mensaje que se desea transmitir
lista.add(mensaje);
try {
objectOutputStream.writeObject(lista);
} catch (IOException ex) {
System.out.println("Error de lectura y escritura al enviar
mensaje al servidor.");
}
}

/*
* Método que escucha constantemente lo que el servidor dice.
*/
public void escuchar() {
try {
while (escuchando) {
Object aux = objectInputStream.readObject();
if (aux != null) {
if (aux instanceof LinkedList) {
//Si se recibe una LinkedList entonces se
//procesa
ejecutar((LinkedList<String>)aux);
} else {
System.err.println("Se recibió un Objeto
desconocido a través del
socket");
}
} else {
System.err.println("Se recibió un null a través
del socket");
}
}
} catch (Exception e) {
JOptionPane.showMessageDialog(ventana, "La comunicación
con el servidor se ha\n"
+ "perdido, este chat
tendrá que finalizar.\n"
+ "Esta aplicación se
cerrará.");
System.exit(0);
}
}

19
Junior Arturo Barboza Vidarte 1614216

/**
* Método que ejecuta una serie de instrucciones dependiendo del
* mensaje que el cliente reciba del servidor
* @param lista
*/
public void ejecutar(LinkedList<String> lista){
// 0 - El primer elemento de la lista es siempre el tipo
String tipo=lista.get(0);
switch (tipo) {
case "CONEXION_ACEPTADA":
// 1 - Identificador propio del nuevo usuario
// 2 .. n - Identificadores de los clientes conectados
// actualmente
identificador=lista.get(1);
ventana.sesionIniciada(identificador);
for(int i=2;i<lista.size();i++){
ventana.addContacto(lista.get(i));
}
break;
case "NUEVO_USUARIO_CONECTADO":
// 1 - Identificador propio del cliente que se
// acaba de conectar
ventana.addContacto(lista.get(1));
break;
case "USUARIO_DESCONECTADO":
// 1 - Identificador propio del cliente que se
// acaba de conectar
ventana.eliminarContacto(lista.get(1));
break;
case "MENSAJE":
// 1 - Cliente emisor
// 2 - Cliente receptor
// 3 - Mensaje
ventana.addMensaje(lista.get(1), lista.get(3));
break;
default:
break;
}
}
/**
* Al conectarse el cliente debe solicitar al servidor que lo
* agregue a la lista de clientes, para ello se ejecuta este
* método.
* @param identificador
*/
private void enviarSolicitudConexion(String identificador) {
LinkedList<String> lista=new LinkedList<>();
//tipo
lista.add("SOLICITUD_CONEXION");
//cliente solicitante
lista.add(identificador);
try {
objectOutputStream.writeObject(lista);
} catch (IOException ex) {
System.out.println("Error de lectura y escritura al enviar
mensaje al servidor.");
}
}
/**
* Cuando se cierra una ventana cliente, se debe notificar al
* servidor que el cliente se ha desconectado para que lo elimine

20
Junior Arturo Barboza Vidarte 1614216

* de la lista de clientes y todos los clientes lo eliminen de su


* lista de contactos.
*/
void confirmarDesconexion() {
LinkedList<String> lista=new LinkedList<>();
//tipo
lista.add("SOLICITUD_DESCONEXION");
//cliente solicitante
lista.add(identificador);
try {
objectOutputStream.writeObject(lista);
} catch (IOException ex) {
System.out.println("Error de lectura y escritura al enviar
mensaje al servidor.");
}
}
/**
* Método que retorna el identificador del cliente que es único
* dentro del chat.
* @return
*/
String getIdentificador() {
return identificador;
}
}

21
Junior Arturo Barboza Vidarte 1614216

2. VentanaC.java:
/*
* Practica Calificada 2
* Junior Barboza Vidarte
* Mayo 2019
*/
package chatcliente;

import java.awt.GridLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;

/**
* Clase que maneja la interfaz gráfica del cliente.
* @author JuniorBarboza
*/
public class VentanaC extends javax.swing.JFrame {

/**
* Constructor de la ventana.
*/

public VentanaC() {
initComponents();
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
String ip_puerto_nombre[]=getIP_Puerto_Nombre();
String ip=ip_puerto_nombre[0];
String puerto=ip_puerto_nombre[1];
String nombre=ip_puerto_nombre[2];
cliente=new Cliente(this, ip, Integer.valueOf(puerto),nombre);
}

/**
* This method is called from within the constructor to initialize
* the form.
* WARNING: Do NOT modify this code. The content of this method is
* always regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">
private void initComponents() {

jScrollPane2 = new javax.swing.JScrollPane();


txtHistorial = new javax.swing.JTextArea();
cmbContactos = new javax.swing.JComboBox<>();
btnEnviar = new javax.swing.JButton();
jLabel1 = new javax.swing.JLabel();
txtMensaje = new javax.swing.JTextField();

setDefaultCloseOperation(javax.swing.WindowConstants
.EXIT_ON_CLOSE)
addWindowListener(new java.awt.event.WindowAdapter() {
public void windowClosing(java.awt.event.WindowEvent evt){
formWindowClosing(evt);
}
});

22
Junior Arturo Barboza Vidarte 1614216

txtHistorial.setColumns(20);
txtHistorial.setRows(5);
jScrollPane2.setViewportView(txtHistorial);

btnEnviar.setText("Enviar");
btnEnviar.addActionListener(new java.awt.event.
ActionListener() {
public void actionPerformed(java.awt.event.
ActionEvent evt) {
btnEnviarActionPerformed(evt);
}
});

jLabel1.setText("Destinatario");

javax.swing.GroupLayout layout = new javax.swing.GroupLayout


(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout
.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(48, 48, 48)
.addGroup(layout.createParallelGroup(javax.swing
.GroupLayout.Alignment.LEADING, false)
.addGroup(javax.swing.GroupLayout.Alignment
.TRAILING, layout.createSequentialGroup()
.addComponent(txtMensaje, javax.swing
.GroupLayout.PREFERRED_SIZE, 148,
javax.swing.GroupLayout
.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle
.ComponentPlacement.RELATED,
javax.swing.GroupLayout
.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(btnEnviar))
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING,
Layout.createSequentialGroup()
.addComponent(jLabel1)
.addPreferredGap(javax.swing.LayoutStyle
.ComponentPlacement.RELATED,javax
.swing.GroupLayout.DEFAULT_SIZE,
Short.MAX_VALUE)
.addComponent(cmbContactos, javax.swing.GroupLayout
.PREFERRED_SIZE, 140, javax
.swing.GroupLayout.PREFERRED_SIZE))
.addComponent(jScrollPane2, javax.swing.GroupLayout
.Alignment.TRAILING, javax
.swing.GroupLayout.PREFERRED_SIZE, 291,
javax.swing.GroupLayout.PREFERRED_SIZE))
.addContainerGap(59, Short.MAX_VALUE))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout
.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(52, 52, 52)
.addComponent(jScrollPane2, javax.swing.GroupLayout
.PREFERRED_SIZE, 268, javax.swing
.GroupLayout.PREFERRED_SIZE)
.addGap(36, 36, 36)

23
Junior Arturo Barboza Vidarte 1614216

.addGroup(layout.createParallelGroup(javax.swing
.GroupLayout.Alignment.BASELINE)
.addComponent(jLabel1)
.addComponent(cmbContactos, javax.swing
.GroupLayout.PREFERRED_SIZE, javax
.swing.GroupLayout.DEFAULT_SIZE,
javax.swing.GroupLayout
.PREFERRED_SIZE))
.addGap(18, 18, 18)
.addGroup(layout.createParallelGroup(javax.swing
.GroupLayout.Alignment.BASELINE)
.addComponent(btnEnviar)
.addComponent(txtMensaje, javax.swing.GroupLayout
.PREFERRED_SIZE, javax.swing
.GroupLayout.DEFAULT_SIZE, javax
.swing.GroupLayout.PREFERRED_SIZE))
.addContainerGap(47, Short.MAX_VALUE))
);

pack();
setLocationRelativeTo(null);
}// </editor-fold>

private void btnEnviarActionPerformed(java.awt.event.ActionEvent


evt) {
//Si no hay más clientes del chat con quien comunicarse.
if(cmbContactos.getSelectedItem()==null){
JOptionPane.showMessageDialog(this, "Debe escoger un
destinatario válido, si no \n"
+ "hay uno, espere a que otro
usuario se conecte\n"
+ "para poder chatear con él.");
return;
}
String cliente_receptor=cmbContactos.getSelectedItem()
.toString();
String mensaje=txtMensaje.getText();
cliente.enviarMensaje(cliente_receptor, mensaje);
//se agrega en el historial de la conversación lo que el
//cliente ha dicho
txtHistorial.append("## Yo -> "+cliente_receptor+ " ## : \n" +
mensaje+"\n");
txtMensaje.setText("");
}

/**
* Cuando la ventana se este cerrando se notifica al servidor que
* el cliente se ha desconectado, por lo que los demás clientes
* del chat no podrán enviarle más mensajes.
* @param evt
*/
private void formWindowClosing(java.awt.event.WindowEvent evt) {
cliente.confirmarDesconexion();
}

/**
* @param args the command line arguments
*/
public static void main(String args[]) {
/* Set the Nimbus look and feel */
//<editor-fold defaultstate="collapsed" desc=" Look and feel

24
Junior Arturo Barboza Vidarte 1614216

//setting code (optional) ">


/* If Nimbus (introduced in Java SE 6) is not available, stay
*with the default look and feel.
* For details see http://download.oracle.com/javase/tutorial
* /uiswing/lookandfeel/plaf.html
*/
try {
for (javax.swing.UIManager.LookAndFeelInfo info :
javax.swing.UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
javax.swing.UIManager.setLookAndFeel(info
.getClassName());
break;
}
}
} catch (ClassNotFoundException ex) {
java.util.logging.Logger.getLogger(VentanaC
.class.getName()).log(java.util.logging.Level.SEVERE,
null, ex);
} catch (InstantiationException ex) {
java.util.logging.Logger.getLogger(VentanaC
.class.getName()).log(java.util.logging.Level.SEVERE,
null, ex);
} catch (IllegalAccessException ex) {
java.util.logging.Logger.getLogger(VentanaC
.class.getName()).log(java.util.logging.Level.SEVERE,
null, ex);
} catch (javax.swing.UnsupportedLookAndFeelException ex) {
java.util.logging.Logger.getLogger(VentanaC
.class.getName()).log(java.util.logging.Level.SEVERE,
null, ex);
}
//</editor-fold>

/* Create and display the form */


java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new VentanaC().setVisible(true);
}
});
}

// Variables declaration - do not modify


private javax.swing.JButton btnEnviar;
private javax.swing.JComboBox<String> cmbContactos;
private javax.swing.JLabel jLabel1;
private javax.swing.JScrollPane jScrollPane2;
private javax.swing.JTextArea txtHistorial;
private javax.swing.JTextField txtMensaje;
// End of variables declaration

/**
* Constante que almacena el puerto por defecto para la aplicación.
*/
private final String DEFAULT_PORT="10101";
/**
* Constante que almacena la IP por defecto (localhost) para el
* servidor.
*/
private final String DEFAULT_IP="127.0.0.1";

25
Junior Arturo Barboza Vidarte 1614216

/**
* Constante que almacena el cliente, con el cual se gestiona la
* comunicación con el servidor.
*/
private final Cliente cliente;
/**
* Agrega un contacto al JComboBox de contactos.
* @param contacto
*/
void addContacto(String contacto) {
cmbContactos.addItem(contacto);
}
/**
* Agrega un nuevo mensaje al historial de la conversación.
* @param emisor
* @param mensaje
*/
void addMensaje(String emisor, String mensaje) {
txtHistorial.append("##### "+emisor + " ##### : \n" +
mensaje+"\n");
}
/**
* Se configura el título de la ventana para una nueva sesión.
* @param identificador
*/
void sesionIniciada(String identificador) {
this.setTitle(" --- "+identificador+" --- ");
}
/**
* Método que abre una ventana para que el usuario ingrese la IP
* del host en el que corre el servidor, el puerto con el que
* escucha y el nombre con el que quiere participar en el chat.
* @return
*/
private String[] getIP_Puerto_Nombre() {
String s[]=new String[3];
s[0]=DEFAULT_IP;
s[1]=DEFAULT_PORT;
JTextField ip = new JTextField(20);
JTextField puerto = new JTextField(20);
JTextField usuario = new JTextField(20);
ip.setText(DEFAULT_IP);
puerto.setText(DEFAULT_PORT);
usuario.setText("Usuario");
JPanel myPanel = new JPanel();
myPanel.setLayout(new GridLayout(3, 2));
myPanel.add(new JLabel("IP del Servidor:"));
myPanel.add(ip);
myPanel.add(new JLabel("Puerto de la conexión:"));
myPanel.add(puerto);
myPanel.add(new JLabel("Escriba su nombre:"));
myPanel.add(usuario);
int result = JOptionPane.showConfirmDialog(null, myPanel,
"Configuraciones de la comunicación",
JOptionPane.OK_CANCEL_OPTION);
if (result == JOptionPane.OK_OPTION) {
s[0]=ip.getText();
s[1]=puerto.getText();
s[2]=usuario.getText();
}else{
System.exit(0);

26
Junior Arturo Barboza Vidarte 1614216

}
return s;
}
/**
* Método que elimina cierto cliente de la lista de contactos,
* este se llama cuando cierto usuario cierra sesión.
* @param identificador
*/
void eliminarContacto(String identificador) {
for (int i = 0; i < cmbContactos.getItemCount(); i++) {

if(cmbContactos.getItemAt(i).toString().equals(identificador)){
cmbContactos.removeItemAt(i);
return;
}
}
}
}

27

También podría gustarte