CÓDIGO DEL CLIENTE /* CODIGO DEL CLIENTE NOMBRE DE LA CLASE Client.java */ //LISTA DE PAQUETES A IMPORTAR import java.io.*; import java.net.*; import java.util.*; import javax.swing.*; import java.awt.*; //ESTA CLASE PERMITE CREAR EL INTERFAZ DEL CHAT HACIA EL USUARIO Y CREAR UNA HEBRA O HIJO DENTRO //DE LA MISMA QUE SE ENCARGA DE LA COMUNICACION CON EL SERVIDOR public class Client extends javax.swing.JFrame { //CONSTRUCTOR DE LA CLASE "Client" public Client() { nombreCorrecto=false; //PARA CONTROLAR SI EL USUARIO SE HA DADO DE ALTA initComponents(); //INICIALIZA EL FORMULARIO /*PARA QUE LA VENTANA SE AJUSTE A LA MEDIDA ELEGIDA Y AL DISEÑO DE SUS SUBCOMPONENTES. SI LA VENTANA Y/O SUS SUBCOMPONENTES NO ESTAN TODAVIA DESPLEGADOS, AMBOS SON DESPLEGADOS ANTES DE CALCULAR EL TAMAÑO ELEGIDO. LA VENTANA SERA CONFIRMADA DESPUES DE QUE EL TAMAÑO ELEGIDO ES 1 CALCULADO*/ pack(); //CREA UN HIJO O HEBRA PARA GESTIONAR EL PASO DE MENSAJES CON EL SERVIDOR hebra=new objetoCliente(this); //LANZA LA EJECUCION DE LA HEBRA hebra.start(); }//FIN CONSTRUCTOR Client //ESTE METODO ES LLAMADO DESDE DENTRO DEL CONSTRUCTOR PARA INICIALIZAR EL FORMULARIO private void initComponents() { // timer1 = new org.netbeans.examples.lib.timerbean.Timer(); // timer2 = new org.netbeans.examples.lib.timerbean.Timer(); //DECLARACION DE OBJETOS //ESTE OBJETO REPRESENTA UN PANEL O MARCO DONDE PODEMOS INSERTAR OTROS OBJETOS, INCLUSO NUEVOS //"JPanel" jPanel1 = new javax.swing.JPanel(); jPanel10 = new javax.swing.JPanel(); //(ESTE OBJETO REPRESENTA UNA ETIQUETA "Usuario") jLabel2 = new javax.swing.JLabel(); //ESTE OBJETO REPRESENTA UNA CAJA DE EDICION DONDE PODEMOS ESCRIBIR TEXTO (En esta concretamente //introduciremos el nombre de usuario al iniciar la ejecucion del programa y posteriormente sera //deshabilitada) nombreUsuario = new javax.swing.JTextField(); jPanel11 = new javax.swing.JPanel(); jPanel12 = new javax.swing.JPanel(); 2 jPanel17 = new javax.swing.JPanel(); jPanel18 = new javax.swing.JPanel(); //ESTE OBJETO REPRESENTA UN BOTON AL QUE NORMALMENTE SE LE ASOCIA UN EVENTO AL SER PULSADO (Este //(boton utilizado para terminar la ejecución del programa "SALIR") jButton1 = new javax.swing.JButton(); //(boton utilizado para realizar un privado "PRIVADO") jButton2 = new javax.swing.JButton(); jPanel19 = new javax.swing.JPanel(); jPanel2 = new javax.swing.JPanel(); jPanel6 = new javax.swing.JPanel(); //("Mensajes") jLabel1 = new javax.swing.JLabel(); jPanel7 = new javax.swing.JPanel(); //ESTE OBJETO REPRESENTA UN MARCO NORMALMENTE USADO PARA INSERTAR OBJETOS EN EL COMO POR EJEMPLO //UN "JList" jScrollPane2 = new javax.swing.JScrollPane(); //REPRESENTA UNA VENTANA DONDE SE PUEDE AÑADIR TEXTO MEDIANTE EL METODO CORRESPONDIENTE (En esta //(concretamente apareceran los mensajes enviados y recibidos al servidor) messages = new javax.swing.JList(); jPanel8 = new javax.swing.JPanel(); //(En esta concretamente introduciremos los mensajes a enviar al servidor) text = new javax.swing.JTextField(); jPanel13 = new javax.swing.JPanel(); //PAG 8−7 jPanel15 = new javax.swing.JPanel(); 3 jPanel16 = new javax.swing.JPanel(); jPanel5 = new javax.swing.JPanel(); jScrollPane1 = new javax.swing.JScrollPane(); //(Almacena una lista de los usuarios activos en el sistema) users = new javax.swing.JList(); jPanel9 = new javax.swing.JPanel(); //(Este boton permite enviar mensajes al servidor "ENVIAR") send = new javax.swing.JButton(); jPanel3 = new javax.swing.JPanel(); //("Lista de usuarios") jLabel3 = new javax.swing.JLabel(); jPanel4 = new javax.swing.JPanel(); jPanel14 = new javax.swing.JPanel(); jPanel20 = new javax.swing.JPanel(); jPanel21 = new javax.swing.JPanel(); //ASIGNACION DE FONDOS Y BORDES DE LOS OBJETOS jPanel1.setBackground( new java.awt.Color(255,175,175) ); jPanel2.setBackground( new java.awt.Color(255,175,175) ); jPanel3.setBackground( new java.awt.Color(255,175,175) ); jPanel4.setBackground( new java.awt.Color(255,175,175) ); jPanel5.setBackground( new java.awt.Color(255,175,175) ); jPanel6.setBackground( new java.awt.Color(255,175,175) ); jPanel7.setBackground( new java.awt.Color(255,175,175) ); jPanel8.setBackground( new java.awt.Color(255,175,175) ); jPanel9.setBackground( new java.awt.Color(255,175,175) ); jPanel10.setBackground( new java.awt.Color(255,175,175) ); 4 jPanel11.setBackground( new java.awt.Color(255,175,175) ); jPanel12.setBackground( new java.awt.Color(255,175,175) ); jPanel13.setBackground( new java.awt.Color(255,175,175) ); jPanel14.setBackground( new java.awt.Color(255,175,175) ); jPanel15.setBackground( new java.awt.Color(255,175,175) ); jPanel16.setBackground( new java.awt.Color(255,175,175) ); jPanel17.setBackground( new java.awt.Color(255,175,175) ); jPanel18.setBackground( new java.awt.Color(255,175,175) ); jPanel19.setBackground( new java.awt.Color(255,175,175) ); jPanel20.setBackground( new java.awt.Color(255,175,175) ); jPanel21.setBackground( new java.awt.Color(255,175,175) ); jButton1.setBackground( new java.awt.Color(255,255,0) ); jButton2.setBackground( new java.awt.Color(255,255,0) ); send.setBackground( new java.awt.Color(255,255,0) ); jLabel1.setBackground( new java.awt.Color(255,175,175) ); jLabel2.setBackground( new java.awt.Color(255,175,175) ); jLabel3.setBackground( new java.awt.Color(255,175,175) ); jScrollPane1.setBackground( new java.awt.Color(255,175,175) ); jScrollPane2.setBackground( new java.awt.Color(255,175,175) ); messages.setBorder( new javax.swing.border.EtchedBorder( javax.swing.border.EtchedBorder.LOWERED, new java.awt.Color(0,255,255), new java.awt.Color(0,255,0) ) ); users.setBorder( new javax.swing.border.EtchedBorder( javax.swing.border.EtchedBorder.LOWERED, new java.awt.Color(0,255,255), new java.awt.Color(0,255,0) ) ); nombreUsuario.setBorder( new javax.swing.border.EtchedBorder( javax.swing.border.EtchedBorder.LOWERED, new java.awt.Color(0,0,255), new java.awt.Color(0,255,255) ) ); text.setBorder( new javax.swing.border.EtchedBorder( javax.swing.border.EtchedBorder.LOWERED, new java.awt.Color(0,0,255), new java.awt.Color(0,255,255) ) ); //¿DESHABILITA EL PODER MAXIMIZAR LA VENTANA O MODIFICAR SU TAMAÑO, SOLO 5 PODEMOS MINIMIZARLA? setResizable(false); //ESPECIFICAMOS UN EVENTO QUE SE DEBE PRODUCIR CUANDO CAMBIE EL ESTADO DE LA VENTANA DEL //FORMULARIO, CONCRETAMENTE EN ESTE CASO CUANDO LA VENTANA SE CIERRE SE LLAMA AL METODO //"exitForm" CUYA IMPLEMENTACION SE ENCUENTRA MAS ABAJO addWindowListener (new java.awt.event.WindowAdapter() { public void windowClosing(java.awt.event.WindowEvent evt) { exitForm(evt); } } ); //DIVIDIMOS EL PANEL EN 5 PARTES (CENTRO, NORTE, SUR, ESTE Y OESTE) jPanel1.setLayout (new java.awt.BorderLayout ()); //ESTABLECEMOS UN TAMAÑO PARA EL PANEL jPanel1.setPreferredSize (new java.awt.Dimension (200, 100)); //PARA QUE NO NOS MODIFIQUE EL TAMAÑO ESTABLECIDO jPanel1.setMaximumSize (jPanel1.getPreferredSize ()); jPanel1.setMinimumSize (jPanel1.getPreferredSize ()); //PERMITE INTRODUCIR CAJAS DENTRO DE UN PANEL Y AGRUPAR EN ESTAS VARIOS COMPONENTES jPanel10.setLayout (new javax.swing.BoxLayout (jPanel10,0)); jPanel10.setMaximumSize (jPanel10.getPreferredSize ()); jPanel10.setMinimumSize (jPanel10.getPreferredSize ()); //ASIGNAMOS AL CAMPO "text" DEL "JLabel" UNA CADENA DE TEXTO 6 jLabel2.setText ("Usuario"); //AÑADIMOS AL jPanel10 el jLabel2 jPanel10.add (jLabel2); nombreUsuario.setPreferredSize (new java.awt.Dimension(100, 18)); nombreUsuario.setMaximumSize (nombreUsuario.getPreferredSize ()); nombreUsuario.setMinimumSize (nombreUsuario.getPreferredSize ()); //AÑADIMOS AL jPanel10 EL JTextField DECLARADO ANTERIORMENTE IDENTIFICADO COMO "nombreUsuario" jPanel10.add (nombreUsuario); //AÑADIMOS EL jPanel10 EN EL CENTRO DEL jPanel1 jPanel1.add (jPanel10, java.awt.BorderLayout.CENTER); //PAG 8−8 jPanel11.setPreferredSize (new java.awt.Dimension(400, 10)); jPanel11.setMaximumSize (jPanel11.getPreferredSize ()); jPanel11.setMinimumSize (jPanel11.getPreferredSize ()); jPanel1.add (jPanel11, java.awt.BorderLayout.NORTH); jPanel12.setPreferredSize (new java.awt.Dimension (400, 10)); jPanel12.setMaximumSize (jPanel12.getPreferredSize ()); jPanel12.setMinimumSize (jPanel12.getPreferredSize ()); jPanel1.add (jPanel12, java.awt.BorderLayout.SOUTH); jPanel17.setPreferredSize (new java.awt.Dimension (100, 10)); jPanel17.setMaximumSize (jPanel17.getPreferredSize ()); jPanel17.setMinimumSize (jPanel17.getPreferredSize ()); jPanel1.add (jPanel17, java.awt.BorderLayout.WEST); jPanel18.setLayout (new java.awt.BorderLayout ()); //ASIGNAMOS AL CAMPO "text" DEL "JButton1" UNA CADENA DE TEXTO jButton1.setText("SALIR"); 7 //HACE LO MISMO QUE EL ANTERIOR jButton1.setLabel("SALIR"); //ESPECIFICAMOS UN EVENTO QUE SE DEBE PRODUCIR CUANDO CAMBIE EL ESTADO DEL BOTON IDENTIFICADO //COMO "jButton1", CONCRETAMENTE CUANDO PINCHEMOS EN EL CON EL RATON SE LLAMA AL METODO //ESPECIFICADO CUYA IMPLEMENTACION SE ENCUENTRA MAS ABAJO jButton1.addMouseListener (new java.awt.event.MouseAdapter () { public void mouseClicked (java.awt.event.MouseEvent evt) { jButton1MouseClicked (evt); } } ); //ASIGNAMOS AL CAMPO "text" DEL "JButton2" UNA CADENA DE TEXTO jButton2.setText("PRIVADO"); //ESPECIFICAMOS UN EVENTO QUE SE DEBE PRODUCIR CUANDO CAMBIE EL ESTADO DEL BOTON IDENTIFICADO //COMO "jButton2", CONCRETAMENTE CUANDO PINCHEMOS EN EL CON EL RATON SE LLAMA AL METODO //ESPECIFICADO CUYA IMPLEMENTACION SE ENCUENTRA MAS ABAJO jButton2.addMouseListener (new java.awt.event.MouseAdapter () { public void mouseClicked (java.awt.event.MouseEvent evt) { jButton2MouseClicked (evt); } 8 } ); jPanel19.setPreferredSize (new java.awt.Dimension (30, 5)); jPanel19.setMaximumSize (jPanel19.getPreferredSize ()); jPanel19.setMinimumSize (jPanel19.getPreferredSize ()); jPanel18.add(jPanel19, java.awt.BorderLayout.CENTER); jPanel20.setLayout (new java.awt.BorderLayout ()); jPanel21.setPreferredSize (new java.awt.Dimension (20, 20)); jPanel21.setMaximumSize (jPanel21.getPreferredSize ()); jPanel21.setMinimumSize (jPanel21.getPreferredSize ()); jPanel20.add (jButton1, java.awt.BorderLayout.EAST); jPanel20.add (jPanel21, java.awt.BorderLayout.CENTER); jPanel20.add (jButton2, java.awt.BorderLayout.WEST); jPanel18.add(jPanel20, java.awt.BorderLayout.NORTH); jPanel1.add(jPanel18, java.awt.BorderLayout.EAST); //INTRODUCIMOS EN LA PARTE NORTE DE LA VENTANA O FORMULARIO PRINCIPAL EL jPanel1 getContentPane().add (jPanel1, java.awt.BorderLayout.NORTH); //PAG 8−9 jPanel2.setLayout (new java.awt.BorderLayout ()); jLabel1.setText("Mensajes"); jLabel1.setHorizontalAlignment(javax.swing.SwingConstants.CENTER); jPanel6.add(jLabel1); jPanel2.add (jPanel6, java.awt.BorderLayout.NORTH); jPanel7.setPreferredSize (new java.awt.Dimension (300, 300)); jPanel7.setMaximumSize (jPanel17.getPreferredSize ()); jPanel7.setMinimumSize (jPanel17.getPreferredSize ()); 9 jScrollPane2.setPreferredSize (jPanel7.getPreferredSize()); jScrollPane2.setMaximumSize (jPanel7.getPreferredSize ()); jScrollPane2.setMinimumSize (jPanel7.getPreferredSize ()); //ASIGNAMOS AL jScrollPane2 EL jList DECLARADO ANTERIORMENTE DE NOMBRE "messages" jScrollPane2.setViewportView (messages); jPanel7.add (jScrollPane2); jPanel2.add (jPanel7, java.awt.BorderLayout.CENTER); jPanel8.setLayout (new java.awt.BorderLayout ()); //AÑADIMOS AL jPanel8 EL JTextField DECLARADO ANTERIORMENTE DE NOMBRE "text" jPanel8.add (text, java.awt.BorderLayout.CENTER); jPanel2.add (jPanel8, java.awt.BorderLayout.SOUTH); getContentPane ().add (jPanel2, java.awt.BorderLayout.CENTER); jPanel13.setPreferredSize (new java.awt.Dimension (20, 300)); jPanel13.setMaximumSize (jPanel13.getPreferredSize ()); jPanel13.setMinimumSize (jPanel13.getPreferredSize ()); getContentPane ().add (jPanel13, java.awt.BorderLayout.WEST); //PAG 8−10 jPanel15.setPreferredSize (new java.awt.Dimension (400, 20)); jPanel15.setMaximumSize (jPanel15.getPreferredSize ()); jPanel15.setMinimumSize (jPanel15.getPreferredSize ()); getContentPane ().add (jPanel15, java.awt.BorderLayout.SOUTH); jPanel16.setLayout (new java.awt.BorderLayout ()); jPanel5.setPreferredSize (new java.awt.Dimension (100, 325)); jPanel5.setMaximumSize (jPanel5.getPreferredSize ()); jPanel5.setMinimumSize (jPanel5.getPreferredSize ()); jScrollPane1.setPreferredSize (new java.awt.Dimension(90,300)); 10 jScrollPane1.setMaximumSize (jScrollPane1.getPreferredSize ()); jScrollPane1.setMinimumSize (jScrollPane1.getPreferredSize ()); users.setPreferredSize (new java.awt.Dimension(80,230)); users.setMaximumSize (users.getPreferredSize ()); users.setMinimumSize (users.getPreferredSize ()); jScrollPane1.setViewportView (users); jPanel5.add(jScrollPane1); jPanel16.add (jPanel5, java.awt.BorderLayout.CENTER); //DETERMINA EL COMANDO DE ACCION PARA ESTE BOTON send.setActionCommand ("ENVIAR"); //ASIGNAMOS AL CAMPO "Label" DEL "send" UNA CADENA DE TEXTO send.setLabel ("ENVIAR"); //ESPECIFICAMOS UN EVENTO QUE SE DEBE PRODUCIR CUANDO CAMBIE EL ESTADO DEL BOTON IDENTIFICADO //COMO "send", CONCRETAMENTE CUANDO PINCHEMOS EN EL CON EL RATON SE LLAMA AL METODO //ESPECIFICADO CUYA IMPLEMENTACION SE ENCUENTRA MAS ABAJO send.addMouseListener (new java.awt.event.MouseAdapter () { public void mouseClicked (java.awt.event.MouseEvent evt) { SendMouseClicked(evt); } } ); jPanel9.add (send); //PAG 8−11 11 jPanel16.add (jPanel9, java.awt.BorderLayout.SOUTH); jPanel3.setPreferredSize (new java.awt.Dimension (15, 220)); jPanel3.setMaximumSize (jPanel3.getPreferredSize ()); jPanel3.setMinimumSize (jPanel3.getPreferredSize ()); jPanel16.add (jPanel3, java.awt.BorderLayout.WEST); jLabel3.setText("Lista de usuarios"); jPanel4.setPreferredSize (new java.awt.Dimension (20, 25)); jPanel4.setMaximumSize (jPanel4.getPreferredSize ()); jPanel4.setMinimumSize (jPanel4.getPreferredSize ()); jPanel4.add (jLabel3); jPanel16.add (jPanel4, java.awt.BorderLayout.NORTH); jPanel14.setPreferredSize (new java.awt.Dimension (15, 220)); jPanel14.setMaximumSize (jPanel14.getPreferredSize ()); jPanel14.setMinimumSize (jPanel14.getPreferredSize ()); jPanel16.add(jPanel14,java.awt.BorderLayout.EAST); getContentPane().add(jPanel16, java.awt.BorderLayout.EAST); }//FINAL METODO initComponents //DECLARACION DEL EVENTO QUE SE DEBE PRODUCIR TRAS LA PULSACION DEL BOTON jButton1 (SALIR) private void jButton1MouseClicked (java.awt.event.MouseEvent evt) { String texto=".s"; hebra.enviar(texto); }//FINAL jButton1MouseClicked //DECLARACION DEL EVENTO QUE SE DEBE PRODUCIR TRAS LA PULSACION DEL BOTON jButton2 (PRIVADO) private void jButton2MouseClicked (java.awt.event.MouseEvent evt) 12 { int posicion; posicion=users.getSelectedIndex(); if (posicion==−1) //TEXTO A MOSTRAR SI NO HAY NINGUN USUARIO SELECCIONADO { text.setText(".p<usuario>@"); } else //TEXTO HA MOSTRAR SI HAY ALGUN USUARIO SELECCIONADO { text.setText(".p".concat(users.getSelectedValue().toString()).concat("@")); users.clearSelection(); } } //DECLARACION DEL EVENTO QUE SE DEBE PRODUCIR TRAS LA PULSACION DEL BOTON send (ENVIAR) private void SendMouseClicked (java.awt.event.MouseEvent evt) { //CASO EN QUE EL USUARIO AUN NO SE HA DADO DE ALTA if ( !nombreCorrecto) { String nomb=nombreUsuario.getText(); //CASO EN QUE SE HA INTRODUCIDO EL NOMBRE DEL USUARIO POR PRIMERA VEZ if (nomb.length()>0) { hebra.enviar(nomb); nombreUsuario.setEditable(false); nombreCorrecto=true; 13 //CASO EN QUE EL USUARIO NO HA INTRODUCIDO NINGUN NOMBRE }else {//MENSAJE DE ALERTA INDICANDO QUE AUN NO SE HA ASIGNADO NOMBRE AL USUARIO //PAG 8−12 //MUESTRA MENSAJE DE ALERTA EN TERMINAL O ENTORNO MS−DOS System.out.println("ERROR: No tienes nombre asignado"); //MUESTRA MENSAJE DE ALERTA EN FORMULARIO O INTERFAZ DEL CLIENTE JOptionPane mensajeError=new JOptionPane ("No ha introducido el nombre de usuario", JOptionPane.WARNING_MESSAGE); JDialog dialogo = mensajeError.createDialog(this, "Mensaje de Error"); dialogo.show(); } //EL USUARIO SE HA DADO YA DE ALTA, ENVIAMOS EL MENSAJE CORRESPONDIENTE }else { System.out.println("Correcto: Puedo procesar la linea de texto"); //OBTENEMOS EL MENSAJE DEL FORMULARIO String texto=text.getText(); //ENVIAMOS EL MENSAJE AL SERVIDOR hebra.enviar(texto); text.setText(""); } }//FINAL SendMouseClicked //DECLARACION DEL EVENTO QUE SE DEBE PRODUCIR AL CERRAR LA VENTANA DEL FORMULARIO private void exitForm(java.awt.event.WindowEvent evt) { 14 String texto=".s";//CADENA ENVIADA PARA INFORMAR AL SERVIDOR DE NUESTRA SALIDA hebra.enviar(texto); }//FINAL exitForm //METODOS PARA ACCEDER RESPECTIVAMENTE A LAS VARIABLES DE TIPO JList "messages" Y "users" public javax.swing.JList getMessagesComponent() { return(messages); } public javax.swing.JList getUsersComponent() { return(users); } //CLASE PRINCIPAL public static void main (String args[]) { new Client().show(); }//FIN main //PAG 8−13 //DECLARACION DE VARIABLES //private org.netbeans.examples.lib.timerbean.Timer timer1; //private org.netbeans.examples.lib.timerbean.Timer timer2; private javax.swing.JPanel jPanel1; private javax.swing.JPanel jPanel10; private javax.swing.JLabel jLabel2; private javax.swing.JTextField nombreUsuario; private javax.swing.JPanel jPanel11; 15 private javax.swing.JPanel jPanel12; private javax.swing.JPanel jPanel17; private javax.swing.JPanel jPanel18; private javax.swing.JButton jButton1; private javax.swing.JButton jButton2; private javax.swing.JPanel jPanel19; private javax.swing.JPanel jPanel2; private javax.swing.JPanel jPanel6; private javax.swing.JLabel jLabel1; private javax.swing.JPanel jPanel7; private javax.swing.JScrollPane jScrollPane2; private javax.swing.JList messages; private javax.swing.JPanel jPanel8; private javax.swing.JTextField text; private javax.swing.JPanel jPanel13; private javax.swing.JPanel jPanel15; private javax.swing.JPanel jPanel16; private javax.swing.JPanel jPanel5; private javax.swing.JScrollPane jScrollPane1; private javax.swing.JList users; private javax.swing.JPanel jPanel9; private javax.swing.JButton send; private javax.swing.JPanel jPanel3; private javax.swing.JLabel jLabel3; private javax.swing.JPanel jPanel4; private javax.swing.JPanel jPanel14; 16 private javax.swing.JPanel jPanel20; private javax.swing.JPanel jPanel21; //FIN DECLARACION DE VARIABLES private objetoCliente hebra; private boolean nombreCorrecto; //DECLARACION DE LA CLASE CUYOS OBJETOS (HEBRAS) SE ENCARGAN DE LA COMUNICACION CON EL SERVIDOR //PARA PODER CREAR HEBRAS HEREDAMOS DE LA CLASE Thread class objetoCliente extends Thread { //CONSTRUCTOR DE LA CLASE public objetoCliente(Client cliente) { //LA HEBRA O HIJO QUE VAMOS A CREAR TOMA EL CODIGO DEL PADRE QUE VIENE PASADO POR PARAMETRO EN //LA VARIABLE "cliente" this.cliente=cliente; //PAG 8−14 //CREAMOS UN VECTOR PARA ALMACENAR LOS MENSAJES ENVIADOS Y RECIBIDOS POR EL USUARIO listaMensajes=new Vector(); //CREAMOS UN VECTOR PARA ALMACENAR UNA LISTA DE LOS USUARIOS ACTIVOS EN EL SISTEMA listaUsuarios=new Vector(); }//FIN objetoCliente //METODO PARA LANZAR LA HEBRA public void run() { 17 try{ //CREAMOS UN SOCKET PARA ESTABLECER LA COMUNICACION CON EL SERVIDOR EN EL PUERTO INDICADO cli_socket=new Socket("192.168.0.1",12325); System.out.println("Conexion con el servidor en el puerto: 12325"); //DEFINIMOS UN BUFFER O CANAL POR EL QUE RECIBIR LOS MENSAJES DEL SERVIDOR recibido= new BufferedReader(new InputStreamReader(cli_socket.getInputStream())); //DEFINIMOS UN BUFFER O CANAL POR EL QUE ENVIAR LOS MENSAJES AL SERVIDOR salida= new BufferedWriter(new OutputStreamWriter(cli_socket.getOutputStream())); while (seguir) { //LEEMOS EL MENSAJE ENVIADO POR EL SERVIDOR String mens=recibido.readLine(); System.out.println("Recibido: "+mens); //ANALIZAMOS EL MENSAJE Y ACTUAMOS EN CONSECUENCIA compruebaMensaje(mens,recibido); } //CONTROLAMOS EL CASO DE NO PODER ESTABLECER CONEXION CON EL SERVIDOR }catch(Exception e) { System.out.println("No puedo conectar con el servidor"); } cerrar_socket(); }//FIN run //METODO PARA COMPROBAR EL TIPO DE MENSAJE RECIBIDO private void compruebaMensaje(String mens, BufferedReader recibido) { int leido=0; //PARA CONTROLAR SI ES UN MENSAJE NORMAL System.out.println("compruebaMensaje: "+mens); 18 try { if (mens.equals(CADENA_SALIR)) //MENSAJE DE SALIDA DEL SISTEMA { seguir=false; cerrar_socket(); System.exit(0); leido=1; }else { if (mens.equals(CADENA_LISTA_USU)) //MENSAJE DE LISTADO DE USUARIOS { //RECIBIMOS EL NUMERO DE USUARIOS Integer num=new Integer(recibido.readLine()); //PAG 8−15 System.out.println("Numero de usuarios: "+num.intValue()); //LEEMOS TANTAS VECES COMO NOS INDIQUE num listaUsuarios.removeAllElements(); //BORRAMOS LISTADO DE USUARIOS ACTUAL for (int i=0 ; i<num.intValue() ; i++) { String us=recibido.readLine(); System.out.println("Usuario: "+us); listaUsuarios.addElement(us); } javax.swing.JList mes=cliente.getUsersComponent(); mes.setListData(listaUsuarios); //INTRODUCIMOS EL NUEVO LISTADO DE USUARIOS leido=1; 19 //cliente.users.setListData(listaUsuarios); }else { if (mens.equals(CADENA_AYUDA)) //MENSAJE DE AYUDA { //TRAS RECIBIR ESTE MENSAJE EL SERVIDOR NOS ENVIARA LA INFORMACION SOLICITADA QUE SE MOSTRARA //EN LA VENTANA DE MENSAJES GLOBAL AL SER TRATADA COMO UN MENSAJE NORMAL System.out.println("Mensaje de ayuda"); leido=1; }else { if ( mens.equals(CADENA_PRIVADO)) //MENSAJE DE PRIVADO { System.out.println("Mensaje de privado"); leido=1; }else { if (mens.length() > 9) { //MENSAJE DE SOLICITUD DE CONEXION (Nuevo usuario conectado) //SOLO COMPARAMOS LOS 10 PRIMEROS CARACTERES DE LA CADENA RECIBIDA if ( mens.substring(0,10).equals("Conectado:") ) { System.out.println("Debo meter en la lista el Usuario: "+mens.substring(11)); //INTRODUCE EL NUEVO USUARIO EN EL VECTOR DE USUARIOS listaUsuarios.addElement(mens.substring(11)); 20 javax.swing.JList mes=cliente.getUsersComponent(); //ACTUALIZA LA LISTA DE USUARIOS DEL FORMULARIO CON LA NUEVA LISTA mes.setListData(listaUsuarios); leido=1; } } //MENSAJE DE SOLICITUD DE DESCONEXION (Usuario conectado se da de baja) if (mens.length() > 12) { //SOLO COMPARAMOS LOS 13 PRIMEROS CARACTERES DE LA CADENA RECIBIDA if (mens.substring(0,13).equals("Desconectado:")) { System.out.println("Debo quitar de la lista el Usuario: ("+mens.substring(14)+")"); //ELIMINA DE LA LISTA DE USUARIOS EL USUARIO RECIBIDO listaUsuarios.removeElement(mens.substring(14)); javax.swing.JList mes=cliente.getUsersComponent(); //ACTUALIZA LA LISTA DE USUARIOS DEL FORMULARIO CON LA NUEVA LISTA mes.setListData(listaUsuarios); leido=1; } } } } } } if (leido == 0) //MENSAJE NORMAL DE DATOS 21 { System.out.println("Mensaje added: "+mens); //AÑADIMOS EL NUEVO MENSAJE A LA LISTA DE MENSAJES listaMensajes.addElement(mens); javax.swing.JList mes=cliente.getMessagesComponent(); //ACTUALIZAMOS LA LISTA DE MENSAJES DEL FORMULARIO CON LA NUEVA LISTA mes.setListData(listaMensajes); } }catch(Exception e) { System.out.println("Error en mensaje recibido"); } }//FIN compruebaMensaje //METODO PARA ENVIAR UN MENSAJE AL SERVIDOR public void enviar(String cadena) //PAG 8−16 { System.out.println("Envio: "+cadena); try{ cadena=cadena.trim(); //QUITAMOS EL ESPACIO FINAL cadena=cadena+"\n"; //LA CADENA DEBE TERMINAR CON "\n" PARA SER LEIDA CON "readLine" salida.write(cadena,0,cadena.length()); salida.flush(); //CREO QUE NO HARIA FALTA PUES AL DARNOS DE BAJA EL SERVIDOR NOS MANDA ESTA CADENA Y HAY PONEMOS //SALIR A "false" // if ( cadena.equals(".s") ) 22 // seguir=false; }catch(Exception e) { System.out.println("Error en el envio"); seguir=false; } }//FIN enviar //PERMITE CERRAR EL SOCKET Y LOS CANALES DE ENVIO Y RECEPCION DE DATOS public void cerrar_socket() { try{ if (cli_socket!=null) { cli_socket.close(); recibido.close(); salida.close(); } }catch(IOException e) {;} }//FIN cerrar_socket //PERMITE ELIMINAR EL SOCKET public void destroy() { cerrar_socket(); } //DECLARACION DE VARIABLES MIEMBRO DE LA CLASE private Client cliente; 23 private BufferedReader recibido; private BufferedWriter salida; private Socket cli_socket; private String cadena_leida; private boolean seguir=true; private Vector listaMensajes; private Vector listaUsuarios; private final String CADENA_SALIR=new String(".s"); private final String CADENA_LISTA_USU=new String(".u"); private final String CADENA_AYUDA=new String(".h"); private final String CADENA_PRIVADO=new String(".p"); private int pos; //PARA SABER LA POSICION DEL CARACTER "@" EN MENSAJES PRIVADOS public String nombre_privado; //NOMBRE DEL USUARIO CON EL QUE ESTABLECEMOS UN PRIVADO } } CÓDIGO DEL SERVIDOR /* CODIGO DEL SERVIDOR NOMBRE DE LA CLASE Server.java */ //LISTA DE PAQUETES A IMPORTAR import java.util.*; import java.io.*; import java.net.*; //ESTA CLASE PRESENTA EL CODIGO DEL SERVIDOR QUE PERMITE CREAR HEBRAS O HIJOS PARA ATENDER LAS 24 //PETICIONES DE LOS CLIENTES public class Server extends Object { //CONSTRUCTOR DE LA CLASE "Server" public Server (int puerto) { int serv_puerto=puerto; int cont_conex; //PARA CONTROLAR EL NUMERO DE CONEXIONES O CLIENTES QUE ACEPTA EL SERVIDOR try{ System.out.println("Servidor escuchando en el puerto"+puerto); //VECTOR DE HEBRAS PARA ATENDER A LOS CLIENTES v=new Vector(); cont_conex=0; //CREAMOS UN SOCKET DONDE EL SERVIDOR PERMANECE ESCUCHANDO ServerSocket sock_servidor=new ServerSocket (serv_puerto); //ACEPTAMOS HASTA 5 USUARIOS EN EL SISTEMA while (cont_conex<5){ //ACEPTAMOS LA CONEXION DEL CLIENTE Socket cli_sock=sock_servidor.accept(); System.out.println("Conexion efectuada: id " +cont_conex); //CREAMOS UNA HEBRA O HIJO QUE ATIENDA LA PETICION objetoServidor OB=new objetoServidor(cont_conex,cli_sock,cli_sock.getInetAddress().toString(),this); //AÑADIMOS LA NUEVA HEBRA AL VECTOR DE HEBRAS v.addElement(OB); //LANZAMOS LA EJECUCION DE LA HEBRA OB.start(); cont_conex++; 25 } }catch (IOException e){ e.printStackTrace(); }catch (IndexOutOfBoundsException e){ e.printStackTrace(); } }//FIN CONSTRUCTOR Server //PROCESA UN MENSAJE ENVIADO POR UN CLIENTE, IDENTIFICANDOLO Y ENVIANDO LA RESPUESTA ADECUADA public static synchronized void manejaMensaje(String nombre_usu, int id_hilo,String cad) { int leido=0; //PARA NO TENER PROBLEMAS AL LEER CON substring try { if (cad.equals(CADENA_SALIR)) //MENSAJE DE BAJA { salir_usu(id_hilo); leido=1; } else //PAG 8−23 { if (cad.equals(CADENA_LISTA_USU)) //MENSAJE DE LISTADO DE USUARIOS { lista_usuario(id_hilo); leido=1; } 26 else { if (cad.equals(CADENA_AYUDA)) //MENSAJE DE AYUDA { mandaMensajeUno(".u lista usuarios;.s salir;.h ayuda;.p<usuario>@mensaje privado",id_hilo); leido=1; } else { if (cad.length() > 1) //MENSAJE DE PRIVADO { if ( cad.substring(0,2).equals(CADENA_PRIVADO) ) { privado_usu(cad, id_hilo, nombre_usu); leido=1; } } } } } if (leido==0) //MENSAJE NORMAL DE DATOS { mandaMensajeTodos(nombre_usu+">"+cad); } }catch (StringIndexOutOfBoundsException e) { 27 mandaMensajeUno("Fallo procesando mensaje, consulta ayuda <.h> ",id_hilo); } }//FIN manejaMensaje //PERMITE ENVIAR UN MENSAJE A TODOS LOS USUARIOS CONECTADOS public static synchronized void mandaMensajeTodos(String cad) { System.out.println("−−mandaMensajeTodos"); //ACCEDEMOS A CADA UNO DE LOS CLIENTES A TRAVES DEL VECTOR DE HEBRAS for (Enumeration e=v.elements(); e.hasMoreElements(); ) { objetoServidor OB=(objetoServidor) e.nextElement(); OB.enviar_cad(cad); } }//FIN mandaMensajeTodos //PERMITE ENVIAR UN MENSAJE A TODOS LOS USUARIOS CONECTADOS EXCEPTO AL PASADO EN "id_hilo" public static synchronized void mandaMensajeTodosExcepto(String cad, int id_hilo) { for (Enumeration e=v.elements(); e.hasMoreElements(); ) { objetoServidor OB=(objetoServidor) e.nextElement(); if (OB.id_hilo!=id_hilo) OB.enviar_cad(cad); } }//FIN mandaMensajeTodosExcepto //PERMITE ENVIAR UN MENSAJE A UN USUARIO DETERMINADO public static synchronized void mandaMensajeUno(String cad, int id_hilo) 28 { System.out.println("MandaMensajeUno: "+cad); for (Enumeration e=v.elements(); e.hasMoreElements(); ) { objetoServidor OB=(objetoServidor) e.nextElement(); if (OB.id_hilo==id_hilo) //PAG 8−24 { System.out.println("Hacemos el envio"); OB.enviar_cad(cad); } } System.out.println("Tenemos en mandaMensajeUno "+v.size()+" elementos"); }//FIN mandaMensajeUno //PERMITE MANDAR UN MENSAJE PRIVADO A UN USUARIO public static synchronized void privado_usu(String cad, int id_hilo, String nombre_usu) { String nombre_privado; //NOMBRE DEL USUARIO AL QUE ENVIAMOS EL PRIVADO String retornar; //MENSAJE A ENVIAR AL USUARIO QUE ENVIA Y EL QUE RECIBE EL PRIVADO int pos; //PARA IDENTIFICAR LA PARTE DEL MENSAJE PRIVADO ENVIADO QUE NO ES //USADA PARA IDENTIFICAR EL MENSAJE DE TIPO PRIVADO pos=cad.indexOf("@"); nombre_privado=cad.substring(2,pos); retornar=nombre_usu.concat(">").concat(nombre_privado).concat(">").concat(cad.substring(pos+1,cad.length())); //MANDO MENSAJE AL USUARIO QUE SOLICITA PRIVADO mandaMensajeUno(".p",id_hilo); //IDENTIFICACION DE MENSAJE 29 mandaMensajeUno(retornar, id_hilo); //ENVIAMOS EL MENSAJE //OBTENGO EL id_hilo CORRESPONDIENTE AL USUARIO CON EL QUE QUIERO ESTABLECER EL PRIVADO System.out.println("Mensaje privado para enviar a: "+nombre_privado); for (Enumeration e=v.elements(); e.hasMoreElements(); ) { objetoServidor OB=(objetoServidor) e.nextElement(); if (nombre_privado.equals(OB.nombre_usu)) //PAG 8−24 { mandaMensajeUno(".p",OB.id_hilo); //IDENTIFICACION DE MENSAJE PRIVADO System.out.println("Hacemos el envio"); mandaMensajeUno(retornar, OB.id_hilo); //ENVIAMOS EL MENSAJE } } }//FIN privado_usu //PERMITE ENVIAR LA LISTA DE USUARIOS A UN USUARIO public static synchronized void lista_usuario(int id_hilo) { String mensaje; mensaje=CADENA_LISTA_USU; mandaMensajeUno(mensaje,id_hilo); System.out.println("Enviando lista al cliente"); mensaje=""+v.size(); mandaMensajeUno(mensaje,id_hilo); //MANDAMOS EL NUMERO DE USUARIOS System.out.println("Numero de usuarios: "+v.elements());//no muestra bien el valor for (Enumeration e=v.elements(); e.hasMoreElements(); )//MANDAMOS NOMBRES DE USUARIOS 1 A 1 30 { objetoServidor OB=(objetoServidor)e.nextElement(); mandaMensajeUno(OB.nombre_usu,id_hilo); System.out.println("Usuario: "+OB.nombre_usu); } }//FIN lista_usuario //PERMITE ENVIAR EL IDENTIFICADOR DE BAJA A UN USUARIO Y CIERRA LA CONEXION CON DICHO USUARIO public static synchronized void salir_usu(int id_hilo_salir) { for(Enumeration e=v.elements(); e.hasMoreElements(); ) { objetoServidor OB=(objetoServidor)e.nextElement(); if (OB.id_hilo==id_hilo_salir) { borrar_usu(id_hilo_salir,OB.nombre_usu); OB.enviar_cad(CADENA_SALIR); OB.cerrarSocket(); } } }//FIN salir_usu //ELIMINAMOS LA HEBRA DEL VECTOR DE HEBRAS E INFORMAMOS AL RESTO DE USUARIOS DE LA BAJA public static synchronized void borrar_usu(int id_hilo, String nombre_usu) { for(Enumeration e=v.elements(); e.hasMoreElements(); ) //PAG 8−25 31 { objetoServidor OB=(objetoServidor) e.nextElement(); if (OB.id_hilo==id_hilo) { v.removeElement(OB); if (nombre_usu!=null) mandaMensajeTodosExcepto("Desconectado: "+nombre_usu,id_hilo); } } }//FIN borrar_usu //CLASE PRINCIPAL public static void main (String args[]) { Server serv; serv= new Server(12325); }//FIN main //VARIABLES MIEMBRO DE LA CLASE SERVER static Vector v; public final static String CADENA_SALIR=new String(".s"); public final static String CADENA_LISTA_USU=new String(".u"); public final static String CADENA_AYUDA=new String(".h"); public final static String CADENA_PRIVADO=new String(".p"); //DECLARACION DE CLASE CUYOS OBJETOS (HEBRAS) SE ENCARGAN DE LA COMUNICACION CON LOS CLIENTES //PARA PODER CREAR HEBRAS HEREDAMOS DE LA CLASE Thread class objetoServidor extends Thread { 32 //CONSTRUCTOR DE LA CLASE objetoServidor(int indice, Socket sock, String dir_conex, Server servidor) { this.sock=sock; //ASOCIAMOS EL SOCKET AL OBJETO CREADO this.id_hilo=indice; //PARA IDENTIFICAR LA HEBRA DE CADA CLIENTE EN EL VECTOR DE HEBRAS this.servidor=servidor; //PARA TOMAR EL CODIGO DEL PADRE dir_usu=new String(dir_conex); //OBTENEMOS LA DIRECCION DEL USUARIO System.out.println("Creamos objeto servidor: "+indice); try { //PAG 8−26 //CREAMOS DOS CANALES O SOCKETS, UNO DE LECTURA Y OTRO DE ESCRITURA PARA ENVIAR Y //RECIBIR LOS MENSAJES DEL USUARIO recibido= new BufferedReader(new InputStreamReader(sock.getInputStream())); salida= new BufferedWriter(new OutputStreamWriter(sock.getOutputStream())); }catch (Exception e) { System.out.println("−−−−−−−−−−−−−−−ERROR EN LA CREACION"); e.printStackTrace(); } }//FIN objetoServidor //METODO PARA LANZAR LA HEBRA public void run() { try 33 { //ENVIAMOS UN MENSAJE AL CLIENTE INFORMANDO DEL ESTABLECIMIENTO DE LA CONEXION String env="Conexion con el servidor efectuada"; enviar_cad(env); //RECIBIMOS NOMBRE O NICK DEL USUARIO nombre_usu=recibido.readLine(); System.out.println("−−−−−−−−−−−−−−−−−−−−Conectado: "+nombre_usu); servidor.mandaMensajeTodos("Conectado: "+nombre_usu); //ENTRAMOS EN UN BUCLE PARA ESPERAR LA LLEGADA DE MENSAJES PARA SU POSTERIOR TRATAMIENTO while(true) { buffer=recibido.readLine(); buffer=tratar_buffer(buffer); servidor.manejaMensaje(nombre_usu,id_hilo,buffer); } }catch (Exception e) { System.out.println("−−−−−−−−−−−−−−Error"); servidor.borrar_usu(id_hilo,nombre_usu); cerrarSocket(); } //PAG 8−27 }//FIN run //CONTROLAR MENSAJES DE MAS DE 80 CARACTERES public String tratar_buffer(String buffer) 34 { if (buffer.length()>79) buffer=buffer.substring(0,79); buffer=buffer.trim();//QUITAMOS EL ESPACIO FINAL DE LA CADENA return(buffer); }//FIN tratar_buffer //PERMITE ENVIAR UN MENSAJE A UN USUARIO public void enviar_cad(String cad_usu) { try { cad_usu.trim(); //QUITAMOS EL ESPACIO FINAL DE LA CADENA cad_usu=cad_usu+"\n"; salida.write(cad_usu,0,cad_usu.length()); //ESCRIBIMOS EN EL CANAL DE SALIDA salida.flush(); System.out.println("Envio Servidor: "+cad_usu); }catch (Exception e) { servidor.borrar_usu(id_hilo,nombre_usu); cerrarSocket(); } }//FIN enviar_cad //PERMITE CERRAR LA COMUNICACION CON EL USUARIO public void cerrarSocket() { try 35 { if (sock !=null) { sock.close(); //CERRAMOS EL SOCKET salida.close(); //CERRAMOS EL CANAL DE SALIDA O ENVIO DE DATOS recibido.close(); //CERRAMOS EL CANAL DE RECEPCION DE DATOS } }catch (Exception e) { e.printStackTrace(); } }//FIN cerrarSocket //DECLARACION DE VARIABLES MIEMBRO DE LA CLASE private Socket sock=null; private BufferedReader recibido; private BufferedWriter salida; private String buffer; private Server servidor; public String dir_usu; public String nombre_usu; public int id_hilo; } } FUNCIONAMIENTO DE LA APLICACIÓN Clase servidor padre El primer paso para el funcionamiento correcto de la aplicación consiste en lanzar el servidor. 36 En el servidor tenemos un puerto conocido por todos los clientes, un vector para controlar las hebras del proceso cada una de las cuales se encargará de un cliente y un socket para establecer los canales de comunicación. En este caso se permiten hasta 5 usuarios en el sistema. (while (cont_conex<5)) El servidor permanece escuchando en el puerto conocido y para cada cliente que solicite una conexión creamos una nueva hebra que añadimos al vector de estas, incrementamos el número y lanzamos una nueva hebra. C lase servidor hijo (hebras) Una vez establecida la conexión con el cliente creamos dos canales de entrada y salida de datos respectivamente: recibido= new BufferedReader(new InputStreamReader(sock.getInputStream())); salida= new BufferedWriter(new OutputStreamWriter(sock.getOutputStream())); A continuación podemos distinguir 3 pasos: • Enviamos un mensaje al cliente informando del establecimiento de la conexión. • Enviamos un mensaje a cada uno de los usuarios del sistema informando de la incorporación del nuevo usuario al mismo. • Entramos en un bucle infinito donde esperamos las peticiones de los clientes y mediante la llamada a una función identificamos el tipo de petición y la atendemos. servidor.manejaMensaje(nombre_usu,id_hilo,buffer); Tipo de peticiones y respuesta a las mismas Se pueden dar 5 tipos de mensajes: • Mensaje de baja cad.equals(CADENA_SALIR) • Eliminamos la hebra correspondiente a dicho usuario del vector. v.removeElement(OB); • Informamos al resto de usuarios de la baja. mandaMensajeTodosExcepto("Desconectado: "+nombre_usu,id_hilo); • Enviamos el identificador de baja al usuario que la solicita para que termine su ejecución. OB.enviar_cad(CADENA_SALIR); • Mensaje de listado de usuarios 37 cad.equals(CADENA_LISTA_USU) • Enviamos mensaje de identificación. mensaje=CADENA_LISTA_USU; • Enviamos el número de usuarios. mensaje=""+v.size(); • Enviamos un mensaje por cada usuario identificándolo. mandaMensajeUno(OB.nombre_usu,id_hilo); • Mensaje de ayuda cad.equals(CADENA_AYUDA) • Enviamos mensaje con información de comandos del sistema. mandaMensajeUno(".u lista usuarios;.s salir;.h ayuda;.p<usuario>@mensaje privado",id_hilo); • Mensaje de privado cad.substring(0,2).equals(CADENA_PRIVADO) • Envío identificación de mensaje al usuario que solicita el privado. mandaMensajeUno(".p",id_hilo) • Envío mensaje al usuario que solicita privado. mandaMensajeUno(retornar, id_hilo) • Envío identificación de mensaje al usuario con el que se solicita el privado. mandaMensajeUno(".p",OB.id_hilo) • Envío mensaje al usuario con el que se solicita el privado. mandaMensajeUno(retornar, OB.id_hilo) • Mensaje normal de datos • Envío el mensaje recibido a todos los usuarios mandaMensajeTodos(nombre_usu+">"+cad); Clase cliente padre Esta parte del código del cliente se encarga de la creación del interfaz que le permitirá al usuario interactuar con el mismo. Para ello crea un formulario donde el usuario introducirá sus mensajes y podrá recibir los 38 mensajes del resto, esto se consigue mediante la llamada al método initComponents(); Así mismo dentro de este se definen otros métodos o eventos que se producirán tras la pulsación de alguno de los botones del formulario o cuando se cierre la ventana donde este se encuentra. exitForm(evt); jButton1MouseClicked (evt); jButton2MouseClicked (evt); SendMouseClicked(evt); La implementación de los mismos se encuentra fuera del método initComponents() Al final se presenta una relación de los componentes del formulario y como se estructuran dentro de este. El siguiente paso consiste en crear una hebra que se encargue de la gestión de los mensajes con el servidor y a continuación lanzarla. C lase cliente hijo (hebra) El primer paso consiste en la definición de dos vectores para guardar todos los mensajes y la lista de usuarios activos en el sistema. Después se crea un socket, se intenta establecer conexión con el puerto conocido del servidor y definimos dos canales de entrada y salida de datos respectivamente: recibido= new BufferedReader(new InputStreamReader(sock.getInputStream())); salida= new BufferedWriter(new OutputStreamWriter(sock.getOutputStream())); Por último entramos en un bucle infinito donde esperamos los mensajes enviados por el servidor y mediante la llamada a una función identificamos el tipo de mensaje y actuamos en consecuencia. compruebaMensaje(mens,recibido); Tipos de mensajes y actuación ante los mismos Se pueden dar 7 tipos de mensajes: • Mensaje de baja mens.equals(CADENA_SALIR) • Salimos del bucle, cerramos el socket y terminamos la ejecución. seguir=false; cerrar_socket();System.exit(0); • Mensaje de listado de usuarios 39 mens.equals(CADENA_LISTA_USU) • Recibimos el número de usuarios. Integer num=new Integer(recibido.readLine()); • Actualizamos lista de usuarios con los usuarios que recibimos. listaUsuarios.addElement(us); • Introducimos la nueva lista en el formulario. mes.setListData(listaUsuarios); • Mensaje de ayuda mens.equals(CADENA_AYUDA) • Mostramos por pantalla el tipo de mensaje recibido. System.out.println("Mensaje de ayuda"); • Posteriormente recibiremos como un mensaje normal de datos la información de los comandos disponibles. .u lista usuarios;.s salir;.h ayuda;.p<usuario>@mensaje privado • Mensaje de privado mens.substring(0,2).equals(CADENA_PRIVADO) • Mostramos por pantalla el tipo de mensaje recibido. System.out.println("Mensaje de privado"); • Posteriormente recibiremos como un mensaje normal de datos el mensaje privado tanto si somos el usuario que lo solicita como el que lo recibe. usuario_solicita>usuario_privado>mensaje • Mensaje de conexión mens.substring(0,10).equals("Conectado:") • Añadimos el nuevo usuario a la lista. listaUsuarios.addElement(mens.substring(11)); • Actualizamos la lista de usuarios del formulario. mes.setListData(listaUsuarios); 40 • Mensaje de desconexión mens.substring(0,13).equals("Desconectado:") Eliminamos el nuevo usuario a la lista. • listaUsuarios.removeElement(mens.substring(14)); • Actualizamos la lista de usuarios del formulario. mes.setListData(listaUsuarios); • Mensaje normal de datos • Añadimos el nuevo mensaje a la lista de mensajes. listaMensajes.addElement(mens); • Actualizamos la lista de mensajes del formulario. mes.setListData(listaMensajes); Métodos llamados tras la producción de algún evento en el formulario • Evento que se produce tras la pulsación de botón jButton1 SALIR • Enviamos al servidor la cadena de texto .s hebra.enviar(texto); • Evento que se produce tras la pulsación de botón jButton2 PRIVADO • Si no hay ningún usuario seleccionado en la lista de usuarios enviamos al servidor. text.setText(".p<usuario>@"); • Si hay algún usuario seleccionado en la lista de usuarios enviamos al sevidor. text.setText(".p".concat(users.getSelectedValue().toString()).concat("@")); • Evento que se produce tras la pulsación de botón send ENVIAR • Si el usuario no se ha dado de alta pero ha introducido un nick, enviamos al servidor. hebra.enviar(nomb); • Si el usuario no se ha dado de alta y no ha introducido un nick, mostramos mensaje de error. JOptionPane mensajeError=new JOptionPane ("No ha introducido el nombre de usuario", JOptionPane.WARNING_MESSAGE); • Si el usuario se ha dado de alta enviamos el mensaje al servidor. 41 hebra.enviar(texto); • Evento que se produce al cerrar la ventana del formulario • Enviamos al servidor la cadena de texto .s hebra.enviar(texto); Relación de componentes del formulario y estructuración de los mismos Formulario principal (this) jPanel1 JPanel13 jpanel15 jpanel2 jpanel16 jPanel10 jPanel18 jPanel1 jPanel11 JPanel17 jPanel12 jPanel2 jPanel6 jPanel7 jPanel8 jPanel16 jPanel4 JPanel3 jPanel9 jPanel5 jPanel14 jPanel10 jPanel18 jPanel20 jPanel19 jPanel20 JButton2 jPanel21 jButton1 jPanel6 42 jPanel7 jPanel8 text jPanel5 jPanel9 El resto de (jPanel) van vacíos. jPanel (3, 4, 11, 12, 13, 14, 15, 17, 19, 21) Tipo de componentes para cada variable: jButton JButton1, JButton2 y send jLabel JLabel1, JLabel2 y JLabel3 jScrollPane jScrollPane1 y JScrollPane2 jTextField nombreUsuario y text jList messages y users Descripción de componentes: • jPanel Representa un panel o marco donde introducir otros componentes. • jLabel Representa una etiqueta donde escribir texto. • jButton Representa un botón normalmente usado para que al ser pulsado se produzca un evento. • jTextField Representa una caja de edición donde podemos introducir texto en tiempo de ejecución. • jList Pequeña ventana o marco donde iremos introduciendo texto mediante el método adecuado durante la ejecución del formulario. • jScrollPane Permite contener componentes en su interior, en este caso lo hemos utilizado para contener JList. MANUAL DEL USUARIO Requerimientos: He probado la aplicación en un Pentium 166 MMX, y la velocidad es aceptable. Para poder ejecutar correctamente la aplicación necesitamos un compilador de Java, concretamente yo he utilizado la versión jdk1.3.0_02 bajo Windows 98. Aunque con una anterior también nos valdría. 43 Podemos obtener una versión del compilador en la dirección de internet: Introducir aquí la dirección www.sun.java.com Ejecución: Una vez instalado el compilador copia los archivos Client.java y Server.java en el directorio /bin del compilador. Posteriormente compilamos los ficheros, para ello y desde MS−DOS accedemos al directorio donde los acabamos de copiar y ejecutamos los siguientes comandos: javac Server.java javac Client.java Tras la correcta compilación estamos listos para pasar a ejecutar la aplicación. Para ello lanzamos un servidor y hasta un máximo de 5 clientes: • Servidor java Server • Cliente java Client Nota: La aplicación sólo funciona de forma local, para poder ejecutarse en red necesitaríamos saber la dirección IP del servidor, y modificar el código del cliente para que llamase a esa dirección en vez de utilizar la llamada a localhost. Por cada cliente lanzado tendremos una ventana con el siguiente aspecto: El primer paso consiste en darse de alta, para ello introducimos un nombre de usuario junto a la etiqueta usuario y enviamos el mensaje. Todos los mensajes escritos junto al botón enviar y tras pulsar dicho botón serán enviados a todos los usuarios que estén dados de alta en ese momento. Enviando el mensaje .h obtendrá una pequeña ayuda con todos los comandos disponibles. También podemos enviar un mensaje privado a uno de los usuarios de la lista. Para ello usamos la sintaxis especificada al solicitar la ayuda o elegimos el usuario en la lista y posteriormente pulsamos el botón de privado. 5 27 Práctica 2: `Arquitectura Cliente−Servidor en Java'. Implementación de un chat Pág send send ENVIAR jScrollPane1 jScrollPane1 90, 300 jList <users> 44 text jScrollPane2 jScrollPane2 jList <messages> Jlabel1 Jlabel1 Mensajes jPanel21 20, 20 jButton1 SALIR jButton2 PRIVADO jPanel19 30, 5 jPanel20 jLabel2 nombreUsuario jLabel2 Usuario nombreUsuario 100, 28 jPanel3 15, 220 jPanel4 20, 25 jPanel5 100, 325 jPanel9 jPanel14 15, 220 jPanel6 jPanel7 300, 300 jPanel8 jPanel10 jPanel11 400, 10 jPanel12 400, 10 45 jPanel17 100, 10 jPanel18 jPanel1 200, 100 jPanel2 jPanel13 20, 300 jPanel15 400, 20 jPanel16 46