25 de marzo 2016
Buenas, hoy os traigo un código Java que utiliza el protocolo TPC para comunicarse con un Bloc de notas. Este desarrollo nos lo pidió que se lo hiciesemos hace un par de días un lector del blog “Daniel Ruiz” estudiante de la Universidad de Sevilla, en concreto de la ESCUELA TÉCNICA SUPERIOR DE INGENIERÍA INFORMÁTICA.
Tenía el código del protocolo UDP pero no estaba bien estructurado, y obviamente no podía funcionar correctamente. Después de solicitar nuestra ayuda y acudir como el Equipo A, os dejo los resultados en forma de post.
Tuvimos que hacer algunos cambios y unas cuantas explicaciones que a continuación os dejo. Este el código del desarrollo que finalmente hicimos para él. Por si ha alguien más le vale.
import java.io.BufferedReader; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.*; public class TCPCliente { public static void main(String [] args) throws Exception{ String usuario = null; String opcion; String opcion2; String mensaje = null; String clave = null; String A_PDU; boolean autenticado = false; boolean salir = false; BufferedReader entradaDesdeUsuario = new BufferedReader(new InputStreamReader(System.in)); InetAddress direccionIP = InetAddress.getByName("gauss.dte.us.es"); Socket socketTCP = new Socket(direccionIP, 56794); socketTCP.setSoTimeout(3000); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(socketTCP.getInputStream())); PrintWriter printWriter = new PrintWriter(socketTCP.getOutputStream(), true); System.out.println("******************************* Bienvenido *******************"); System.out.println("* *"); System.out.println("* El cliente Bloc de notas remoto v4 usa el puerto: 33096 "); System.out.println("* *"); System.out.println("* "); System.out.println(" "); System.out.println("**************************************************************"); System.out.println(" Debe de validar su cuenta de usuario:"); System.out.println(""); while (!salir) { if (!autenticado) { System.out.println("Elija una opción (Menú M1):"); System.out.println(""); System.out.println(" 1.Autenticar"); System.out.println(" 2.Salir"); opcion = entradaDesdeUsuario.readLine(); if (opcion.equals("1")) { System.out.println("Introduzca el nombre de usuario:"); usuario = entradaDesdeUsuario.readLine(); System.out.println(""); System.out.println("Introduzca su clave:"); clave = entradaDesdeUsuario.readLine(); A_PDU = "aut " + usuario + " " + clave; // Personalmente me gusta más esta clase porque me desentiendo de tener que indicar el fin del envío (CRLF) printWriter.println(A_PDU); // Pero se puede usar esta otra forma, que simplemente usa un outputstream e indicar que los últimos bytes serán el indicador del fin del envío de este paquete TCP (con CRLF) // socketTCP.getOutputStream().write((A_PDU + "\r\n").getBytes()); System.out.println("Espere un momento por favor."); try{ String respuesta = bufferedReader.readLine(); if (respuesta.contains("01")) { autenticado = true; System.out.println(respuesta); } else { System.out.println("Se ha producido un error: " + respuesta); } }catch(SocketTimeoutException e){ System.out.println("El servidor no responde. Vuelva a intentarlo."); } } else if (opcion.equals("2")) { System.out.println("Usted ha seleccionado Salir por lo que la conexión se ha cerrado."); salir = true; } else { System.out.println("No ha introducido ninguna de las opciones posibles, vuelva a intentarlo."); } } else { System.out.println("Elija una opción (Menú M2):"); System.out.println(""); System.out.println(" 1.Solicitar bloc de notas"); System.out.println(" 2.Modificar bloc de notas"); System.out.println(" 3.Salir"); opcion2 = entradaDesdeUsuario.readLine(); if (opcion2.equals("3")) { System.out.println("Usted ha seleccionado Salir por lo que la conexión se ha cerrado."); salir=true; } else if (opcion2.equals("2")) { System.out.println("Introduzca el texto:"); mensaje = entradaDesdeUsuario.readLine(); A_PDU = "put " + usuario + " " + clave + " " + mensaje; printWriter.println(A_PDU); System.out.println("Espere un momento por favor."); try{ String respuesta = bufferedReader.readLine(); if (respuesta.contains("02")) { autenticado = true; System.out.println(respuesta); } else { System.out.println("Se ha producido un error: " + respuesta); } }catch(SocketTimeoutException e){ System.out.println("El servidor no responde. Vuelvalo a intentar."); } } else if (opcion2.equals("1")) { A_PDU = "get " + usuario + " " + clave; printWriter.println(A_PDU); System.out.println("Espere un momento por favor."); try{ String respuesta = bufferedReader.readLine(); if (respuesta.contains("03")) { autenticado = true; System.out.println(respuesta); } else { System.out.println("Se ha producido un error: " + respuesta); } }catch(SocketTimeoutException e){ System.out.println("El servidor no responde. Vuelvalo a intentar."); } } else { System.out.println("No ha introducido ninguna de las opciones posibles, vuelva a intentarlo."); } } } socketTCP.close(); } } |
Comentarios adicionales
1) He mantenido la misma estructura que se usaba en UDP para que se pueda centrar en los cambios.
2) La estructura de UDP es perfectamente válida para TCP. Sin embargo, para UDP yo usaría una variable que mantiene lo último enviado y estaría leyendo constantemente en un bucle. De esta forma, la pérdida de un paquete no afectaría el funcionamiento del resto de la clase.
3) He usado la clase BufferedReader en vez de ir leyendo simplemente del inputstream. Esta clase nos permite ir leyendo línea a línea del servidor y no tener que preocuparnos por el número de bytes de la respuesta metíendolos en un array de longitud determinada, ya que udp tiene el tamaño del paquete limitado pero TCP no. No obstante, se puede usar el InputStream y el array, igual que se hacía al leer UDP.
4) He usado la clase PrintWriter en vez de la clase OutputStream. PrintWriter envuelve dicha clase y nos permite despreocuparnos de tener que indicar el final de cada envío con un CRLF. He puesto un ejemplo de como se haría también con un OutputStream directamente en el primer envío.
5) Al no tener usuario, sólo he podido comprobar a tratar de autenticar y que me tire para atrás. La lógica del resto (que es la misma de UDP) parece estar correcta pero no he podido probarla. Es posible que en las respuestas haya que leer el BufferedReader hasta que esté vacío ya que, por el documento del protocolo, se envía información separada por un CRLF. En dicho caso, al leer, bastaría con hacer un bucle while y usar el readLine hasta que devuelva null.
Otros usuarios llegaron aquí buscando:
PROTOCOLO DE BLOC DE NOTAS REMOTO versión 4
PROGRAMA REDES DE COMPUTADORES
ESPECIFICACIÓN DE PROTOCOLOS