Práctica 3: Introducción a los sockets en Java - Redes de ...
Práctica 3: Introducción a los sockets en Java - Redes de ...
Práctica 3: Introducción a los sockets en Java - Redes de ...
You also want an ePaper? Increase the reach of your titles
YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.
<strong>Práctica</strong> 3: <strong>Introducción</strong> a <strong>los</strong> <strong>sockets</strong><br />
<strong>en</strong> <strong>Java</strong><br />
En esta práctica se va a t<strong>en</strong>er una primera toma <strong>de</strong> contacto con la<br />
interfaz <strong>de</strong> <strong>los</strong> <strong>sockets</strong> <strong>en</strong> <strong>Java</strong>. Para ello plantearemos una serie <strong>de</strong><br />
ejercicios muy s<strong>en</strong>cil<strong>los</strong> que ilustran algunos conceptos básicos <strong>de</strong>l<br />
funcionami<strong>en</strong>to <strong>de</strong> <strong>los</strong> <strong>sockets</strong> TCP <strong>en</strong> <strong>Java</strong>.<br />
1. Entorno <strong>de</strong> trabajo<br />
<strong>Java</strong> está disponible para difer<strong>en</strong>tes sistemas operativos y con diversos<br />
<strong>en</strong>tornos <strong>de</strong> programación. Nosotros vamos a trabajar <strong>en</strong> Linux. Para editar<br />
el programa os sugerimos el uso <strong>de</strong>l editor kwrite.<br />
Por otra parte, recuerda que si el nombre <strong>de</strong>l fichero fu<strong>en</strong>te <strong>de</strong> nuestro<br />
programa es Ejemplo.java lo compilaremos mediante la ord<strong>en</strong> “javac<br />
Ejemplo.java”, y lo ejecutaremos tecleando “java Ejemplo”.<br />
A<strong>de</strong>más, el l<strong>en</strong>guaje <strong>Java</strong> distingue <strong>en</strong>tre mayúsculas y minúsculas, y el<br />
nombre <strong>de</strong>l fichero <strong>de</strong>be coincidir con el <strong>de</strong> la clase principal.<br />
Toda la información sobre las clases <strong>de</strong> <strong>Java</strong> pue<strong>de</strong> <strong>en</strong>contrarse <strong>en</strong> la<br />
página Web <strong>de</strong> Sun: http://java.sun.com/j2se/1.4/docs/api.<br />
Por otra parte, <strong>en</strong> http://www.ulpgc.es/otros/tutoriales/java<br />
se pue<strong>de</strong> consultar un curso <strong>de</strong> <strong>Java</strong>.
P3-2 <strong>Práctica</strong>s <strong>de</strong> <strong>Re<strong>de</strong>s</strong> <strong>de</strong> Computadores<br />
2. Cómo establecer conexión con un servidor<br />
La clase java.net.Socket es la clase <strong>Java</strong> fundam<strong>en</strong>tal para<br />
realizar operaciones TCP <strong>de</strong>s<strong>de</strong> el extremo cli<strong>en</strong>te. A partir <strong>de</strong> aquí nos<br />
referiremos a ella como clase Socket. Esta clase posee varios constructores<br />
que permit<strong>en</strong> especificar el host <strong>de</strong>stino y el número <strong>de</strong> puerto al que<br />
queremos conectarnos. El host pue<strong>de</strong> especificarse como un objeto<br />
InetAddress (que correspon<strong>de</strong> a una dirección IP) o como un String. El<br />
número <strong>de</strong> puerto siempre se indica como un valor int que pue<strong>de</strong> variar<br />
<strong>de</strong>s<strong>de</strong> el 1 al 65.535.<br />
Empezaremos probando el costructor más s<strong>en</strong>cillo: ”public Socket<br />
(String host, int puerto) throws IOException,<br />
UnknownHostException”<br />
Este constructor crea un socket TCP e int<strong>en</strong>ta conectarlo al host y<br />
puerto remotos especificados como parámetros. Si el servidor DNS no está<br />
<strong>en</strong> funcionami<strong>en</strong>to o no pue<strong>de</strong> resolver el nombre, el constructor lanzará una<br />
excepción UnknownHostException. Si el nombre se resuelve pero el<br />
socket no pue<strong>de</strong> conectar por alguna otra razón, se lanzará una excepción<br />
IOException. Esto pue<strong>de</strong> <strong>de</strong>berse, <strong>en</strong>tre otras razones, a que el puerto<br />
<strong>de</strong>stino <strong>en</strong> el servidor no esté abierto, existan problemas <strong>de</strong> <strong>en</strong>caminami<strong>en</strong>to<br />
<strong>en</strong> la red para alcanzar el <strong>de</strong>stino o simplem<strong>en</strong>te el servidor especificado<br />
esté apagado.<br />
Ejercicio 1:<br />
Escribe un programa <strong>en</strong> java, “Cli<strong>en</strong>teTCP1.java”, que sea capaz <strong>de</strong> aceptar<br />
dos parámetros <strong>de</strong> <strong>en</strong>trada <strong>en</strong> línea <strong>de</strong> órd<strong>en</strong>es para establecer una conexión: el<br />
nombre <strong>de</strong> un servidor y un número <strong>de</strong> puerto al que conectar <strong>en</strong> ese servidor. El<br />
programa <strong>de</strong>be visualizar un m<strong>en</strong>saje por pantalla indicando si la conexión se ha<br />
establecido o no. En caso <strong>de</strong> éxito <strong>de</strong>be mostrar también el número <strong>de</strong> puerto y<br />
el nombre <strong>de</strong>l servidor con el que ha conectado, y <strong>en</strong> caso <strong>de</strong> fallo indicar el<br />
motivo: “Nombre <strong>de</strong> servidor <strong>de</strong>sconocido” o “No es posible realizar conexión”,<br />
<strong>de</strong>p<strong>en</strong>di<strong>en</strong>do el tipo <strong>de</strong> excepción ocurrida (ver notas <strong>en</strong> página sigui<strong>en</strong>te).<br />
Prueba tu programa con <strong>los</strong> sigui<strong>en</strong>tes servidores y puertos, comprobando lo que<br />
suce<strong>de</strong>:<br />
1. mail.re<strong>de</strong>s.upv.es 25<br />
2. hero<strong>de</strong>s.re<strong>de</strong>s.upv.es 25<br />
3. zoltar.re<strong>de</strong>s.upv.es 25
<strong>Introducción</strong> a <strong>los</strong> <strong>sockets</strong> <strong>en</strong> <strong>Java</strong> P3-3<br />
Ejercicio 1 (NOTAS):<br />
Para facilitar la <strong>de</strong>puración, el programa <strong>de</strong>be comprobar si hay parámetros <strong>de</strong><br />
<strong>en</strong>trada y, si no se han suministrado, int<strong>en</strong>tar conectarse al puerto 25 <strong>de</strong>l servidor<br />
“zoltar.re<strong>de</strong>s.upv.es”.<br />
Por otra parte, recuerda que <strong>los</strong> parámetros <strong>de</strong> <strong>en</strong>trada <strong>en</strong> <strong>Java</strong> son <strong>de</strong> tipo<br />
“String” y dado que el puerto <strong>en</strong> el constructor Socket es un “int”, es<br />
necesario realizar una conversión <strong>de</strong> tipo. De este modo, si args[1] es el<br />
parámetro correspondi<strong>en</strong>te al puerto, la conversión pue<strong>de</strong> hacerse, por ejemplo,<br />
como: int puerto = Integer.parseInt(args[1]).<br />
Otra opción para crear el socket es pasarle como parámetro la<br />
dirección IP mediante un objeto InetAddress, <strong>en</strong> lugar <strong>de</strong>l nombre <strong>de</strong><br />
dominio <strong>de</strong>l servidor. En este caso se podría g<strong>en</strong>erar una excepción<br />
IOException si no se pue<strong>de</strong> conectar pero no una UnknownHostException.<br />
Ahora la consulta al DNS se lleva a cabo al crear el objeto<br />
InetAddress y es, <strong>en</strong> este mom<strong>en</strong>to, cuando pued<strong>en</strong> surgir <strong>los</strong> problemas<br />
relacionados con la traducción nombre-dirección IP. La clase Inet-<br />
Address posee varios métodos estáticos que permit<strong>en</strong> crear objetos Inet-<br />
Address inicializados <strong>de</strong> forma a<strong>de</strong>cuada. El más popular es:<br />
public static InetAddress InetAddress.getByName(String HostName)<br />
que se utiliza <strong>de</strong> la forma:<br />
InetAddress dirIP = InetAddress.getByName(“www.upv.es”)<br />
Si se van a abrir varias conexiones al mismo host (como haremos <strong>en</strong> el<br />
último apartado <strong>de</strong> la práctica) es más efici<strong>en</strong>te emplear este constructor,<br />
para resolver únicam<strong>en</strong>te una vez la dirección IP a la que se <strong>de</strong>sea conectar e<br />
indicarla directam<strong>en</strong>te <strong>en</strong> el constructor Socket.<br />
Ejercicio 2:<br />
Copia el “Cli<strong>en</strong>teTCP1.java” a “Cli<strong>en</strong>teTCP2.java”. Modifícalo para<br />
que traduzca el nombre <strong>de</strong>l host antes <strong>de</strong> crear el socket. Comprueba que funciona<br />
<strong>de</strong> forma equival<strong>en</strong>te.<br />
3. Cómo obt<strong>en</strong>er información sobre la conexión establecida<br />
La clase Socket dispone <strong>de</strong> varios métodos que permit<strong>en</strong> obt<strong>en</strong>er<br />
información sobre la conexión establecida <strong>en</strong>tre el cli<strong>en</strong>te y el servidor.
P3-4 <strong>Práctica</strong>s <strong>de</strong> <strong>Re<strong>de</strong>s</strong> <strong>de</strong> Computadores<br />
Entre el<strong>los</strong> po<strong>de</strong>mos citar getLocalAddress() y getInetAddress(),<br />
que <strong>de</strong>vuelv<strong>en</strong> las direcciones IP local y remota, respectivam<strong>en</strong>te, y<br />
getLocalPort() y getPort(), que <strong>de</strong>vuelv<strong>en</strong> <strong>los</strong> puertos local y<br />
remoto, respectivam<strong>en</strong>te. A continuación se <strong>de</strong>tallan brevem<strong>en</strong>te <strong>los</strong> métodos<br />
m<strong>en</strong>cionados. Pue<strong>de</strong>s consultar más información sobre el<strong>los</strong> <strong>en</strong> la web<br />
<strong>de</strong> Sun, que se cita al inicio <strong>de</strong> la práctica<br />
• public int getPort(): <strong>de</strong>vuelve el puerto remoto al que el<br />
socket está conectado.<br />
• public InetAddress getInetAddress(): <strong>de</strong>vuelve la dirección<br />
IP remota a la que el socket está conectado.<br />
• public int getLocalPort(): <strong>de</strong>vuelve el puerto local al que el<br />
socket está ligado.<br />
• public InetAddress getLocalAddress(): Devuelve la dirección<br />
IP local a la que el socket está ligado.<br />
Ejercicio 3:<br />
Modifica el cli<strong>en</strong>te “Cli<strong>en</strong>teTCP2.java” <strong>de</strong>l ejercicio anterior para que<br />
muestre información <strong>en</strong> la pantalla <strong>de</strong>l cli<strong>en</strong>te sobre la conexión que establece<br />
(direcciones IP y números <strong>de</strong> puerto locales y remotos).<br />
Ejecútalo cuatro veces seguidas, conectándote con el servidor <strong>de</strong>l laboratorio<br />
“zoltar.re<strong>de</strong>s.upv.es” <strong>en</strong> el puerto 25 y comprueba qué valores se modifican.<br />
Interpreta el resultado obt<strong>en</strong>ido.<br />
4. Cómo leer <strong>los</strong> datos que se recib<strong>en</strong> a través <strong>de</strong> la conexión<br />
Para leer <strong>los</strong> datos que se van recibi<strong>en</strong>do a través <strong>de</strong>l socket<br />
utilizaremos el método getInputStream <strong>de</strong> la clase Socket. Este método<br />
<strong>de</strong>vuelve un objeto <strong>de</strong>l tipo InputStream (flujo <strong>de</strong> octetos <strong>de</strong> <strong>en</strong>trada), lo<br />
que se ajusta bi<strong>en</strong> a la fi<strong>los</strong>ofía TCP <strong>de</strong> transmisión ori<strong>en</strong>tada a flujo continuo<br />
<strong>de</strong> datos, pero no resulta cómodo para leer <strong>los</strong> m<strong>en</strong>sajes <strong>de</strong>l servidor. Lo<br />
que nos convi<strong>en</strong>e, para facilitar nuestro trabajo, es un método que proporcione<br />
un flujo <strong>de</strong> caracteres y, a ser posible, <strong>en</strong> líneas <strong>de</strong> texto completas.<br />
La clase InputStreamRea<strong>de</strong>r es un “pu<strong>en</strong>te” <strong>de</strong>s<strong>de</strong> <strong>los</strong> flujos <strong>de</strong><br />
bytes a <strong>los</strong> <strong>de</strong> caracteres: lee octetos y <strong>los</strong> codifica <strong>en</strong> caracteres empleando<br />
un código <strong>de</strong> caracteres <strong>de</strong>terminado. Adicionalm<strong>en</strong>te, para mejorar la<br />
efici<strong>en</strong>cia <strong>en</strong> la conversión pued<strong>en</strong> leerse varios octetos <strong>en</strong> cada operación
<strong>Introducción</strong> a <strong>los</strong> <strong>sockets</strong> <strong>en</strong> <strong>Java</strong> P3-5<br />
<strong>de</strong> lectura anidando esta clase d<strong>en</strong>tro <strong>de</strong> una BufferedRea<strong>de</strong>r. Por<br />
ejemplo, <strong>de</strong> la forma sigui<strong>en</strong>te:<br />
BufferedRea<strong>de</strong>r lee = new BufferedRea<strong>de</strong>r(new<br />
InputStreamRea<strong>de</strong>r(s.getInputStream()));<br />
si<strong>en</strong>do “s” el objeto <strong>de</strong> la clase Socket <strong>de</strong>finido previam<strong>en</strong>te.<br />
Cuando un programa lee <strong>de</strong> un BufferedRea<strong>de</strong>r, el texto se extrae<br />
<strong>de</strong> un buffer <strong>en</strong> lugar <strong>de</strong> acce<strong>de</strong>r al flujo <strong>de</strong> <strong>en</strong>trada directam<strong>en</strong>te. Cuando el<br />
buffer se vacía, vuelve a ll<strong>en</strong>arse con tanto texto como sea posible, incluso<br />
aunque no todo sea aún necesario. Esto hará que las futuras lecturas se<br />
llev<strong>en</strong> a cabo más rápidam<strong>en</strong>te.<br />
En nuestro caso, empleando el método readLine <strong>de</strong> la clase<br />
BufferedRea<strong>de</strong>r podremos leer las respuestas <strong>de</strong>l servidor como líneas<br />
completas <strong>de</strong> texto. Este método lee una línea <strong>de</strong> texto y la <strong>de</strong>vuelve como<br />
un String.<br />
public String readLine() throws IOException<br />
Ejercicio 4:<br />
R<strong>en</strong>ombra el cli<strong>en</strong>te “Cli<strong>en</strong>teTCP2.java” <strong>de</strong>l ejercicio anterior como<br />
“Cli<strong>en</strong>teSMTP.java”. Modíficalo para que se conecte siempre al puerto 25 y<br />
muestre la primera línea <strong>de</strong> texto que recibe <strong>de</strong>l servidor.<br />
5. Cómo <strong>en</strong>viar datos a través <strong>de</strong> la conexión<br />
Para escribir a través <strong>de</strong> un socket también es más efici<strong>en</strong>te utilizar<br />
una clase que proporcione cierta capacidad <strong>de</strong> almac<strong>en</strong>ami<strong>en</strong>to (buffering).<br />
Por este motivo, para escribir a través <strong>de</strong>l socket, utilizaremos un objeto <strong>de</strong><br />
la clase java.io.PrintWriter conectado al flujo <strong>de</strong> salida <strong>de</strong>l socket.<br />
Este objeto proporciona la capacidad <strong>de</strong> almac<strong>en</strong>ami<strong>en</strong>to <strong>de</strong>seada. A<strong>de</strong>más,<br />
esta clase permite manejar a<strong>de</strong>cuadam<strong>en</strong>te conjuntos <strong>de</strong> caracteres y texto<br />
internacional.<br />
Uno <strong>de</strong> sus constructores (el que utilizaremos habitualm<strong>en</strong>te) es:<br />
public PrintWriter(OutputStream out, boolean autoFlush)<br />
que a<strong>de</strong>más posee una v<strong>en</strong>taja importante como comprobaremos a<br />
continuación. Para ello el segundo parámetro <strong>de</strong>l constructor <strong>de</strong>be invocarse<br />
con el valor true.
P3-6 <strong>Práctica</strong>s <strong>de</strong> <strong>Re<strong>de</strong>s</strong> <strong>de</strong> Computadores<br />
6. Vaciado <strong>de</strong>l buffer TCP (flush)<br />
Aunque las v<strong>en</strong>tajas <strong>de</strong> emplear para la escritura una clase con<br />
almac<strong>en</strong>ami<strong>en</strong>to son claras, también pue<strong>de</strong> plantear algunos inconv<strong>en</strong>i<strong>en</strong>tes<br />
si uno no es cuidadoso, como vamos a ver <strong>en</strong> el ejercicio sigui<strong>en</strong>te.<br />
Ejercicio 5:<br />
Copia el programa “Cli<strong>en</strong>teSMTP.java” y r<strong>en</strong>ómbralo como<br />
“Cli<strong>en</strong>teSMTPej5.java”. Aña<strong>de</strong>, al final <strong>de</strong> tu programa, el código<br />
sigui<strong>en</strong>te:<br />
PrintWriter esc = new PrintWriter<br />
(s.getOutputStream());<br />
salida.print("EHLO re<strong>de</strong>sXX.re<strong>de</strong>s.upv.es\r\n");<br />
System.out.println(“lectura 2: “ + lee.readLine());<br />
s.c<strong>los</strong>e();<br />
NOTA: Se ha supuesto que el objeto BufferedRea<strong>de</strong>r <strong>de</strong>finido <strong>en</strong> el ejercicio<br />
anterior se llama “lee” y el objeto Socket se llama “s”.<br />
Ejecútalo para que se conecte al puerto 25 <strong>de</strong> “zoltar.re<strong>de</strong>s.upv.es”. ¿Qué es lo<br />
que ocurre?¿Aparece la segunda respuesta <strong>de</strong>l servidor <strong>en</strong> tu pantalla? (Lo<br />
normal será que no aparezca nada).<br />
Este cli<strong>en</strong>te SMTP <strong>de</strong>bería <strong>en</strong>viar un m<strong>en</strong>saje correcto al servidor<br />
SMTP <strong>de</strong> zoltar y recibir su respuesta, pero no recibe nada. ¿Por qué?<br />
Porque él tampoco le <strong>en</strong>vía nada. Para mejorar la efici<strong>en</strong>cia, el stream <strong>de</strong><br />
salida int<strong>en</strong>ta ll<strong>en</strong>ar su buffer tanto como sea posible antes <strong>de</strong> <strong>en</strong>viar <strong>los</strong><br />
datos, pero como el cli<strong>en</strong>te no ti<strong>en</strong>e más datos que <strong>en</strong>viar (<strong>de</strong> mom<strong>en</strong>to) su<br />
petición no llega a <strong>en</strong>viarse nunca.<br />
La solución a este problema la da el método flush() <strong>de</strong> la clase<br />
PrintWriter. Este método fuerza a que se <strong>en</strong>ví<strong>en</strong> <strong>los</strong> datos aunque el<br />
buffer no esté aún ll<strong>en</strong>o. En caso <strong>de</strong> duda acerca <strong>de</strong> si resulta o no necesario<br />
utilizarlo, es mejor invocarlo, ya que realizar un flush innecesario<br />
consume pocos recursos, pero no utilizarlo cuando se necesita pue<strong>de</strong><br />
provocar bloqueos <strong>en</strong> el programa.<br />
Ejercicio 6:<br />
Modifica el cli<strong>en</strong>te SMTP para que utilice el método flush y comprueba que<br />
ahora funciona correctam<strong>en</strong>te.
<strong>Introducción</strong> a <strong>los</strong> <strong>sockets</strong> <strong>en</strong> <strong>Java</strong> P3-7<br />
Po<strong>de</strong>mos realizar el vaciado <strong>de</strong>l buffer <strong>de</strong> forma automática al escribir<br />
<strong>en</strong> éste (sin t<strong>en</strong>er que emplear el método flush explícitam<strong>en</strong>te). Para ello<br />
necesitamos dos cosas:<br />
• El constructor <strong>de</strong> la clase PrintWriter <strong>de</strong>be emplearse tal y como<br />
se ha mostrado antes, con un segundo parámetro adicional a true.<br />
• La escritura <strong>de</strong>be realizarse mediante el método println(), <strong>en</strong><br />
lugar <strong>de</strong>l método print. A<strong>de</strong>más el método println aña<strong>de</strong> el final<br />
<strong>de</strong> línea con lo que no necesitamos escribirlo como parte <strong>de</strong> la ord<strong>en</strong><br />
que <strong>en</strong>vía el cli<strong>en</strong>te (a difer<strong>en</strong>cia <strong>de</strong> lo que hemos hecho <strong>en</strong> el<br />
programa anterior).<br />
Vamos a modificar nuestro cli<strong>en</strong>te para comprobar este funcionami<strong>en</strong>to.<br />
Ejercicio 7:<br />
Modifica el cli<strong>en</strong>te SMTP para que utilice el método println <strong>en</strong> lugar <strong>de</strong>l<br />
print (elimina lo que sobra, como la secu<strong>en</strong>cia \r\n y el flush). Comprueba<br />
que funciona volvi<strong>en</strong>do a establecer conexión con el servidor zoltar <strong>en</strong> el puerto<br />
25.<br />
NOTA: Hay que t<strong>en</strong>er <strong>en</strong> cu<strong>en</strong>ta que cuando usamos el método println, lo<br />
que se <strong>en</strong>vía como final <strong>de</strong> línea es \n. El estándar especifica que <strong>de</strong>be <strong>en</strong>viarse<br />
\r\n. No obstante, como podéis haber comprobado al usar println, el<br />
programa funciona correctam<strong>en</strong>te. Esto es <strong>de</strong>bido a la g<strong>en</strong>erosidad <strong>de</strong>l servidor,<br />
que contesta a la petición a pesar <strong>de</strong> que no se ajusta completam<strong>en</strong>te al estándar.<br />
Po<strong>de</strong>mos ajustarnos fácilm<strong>en</strong>te al estándar <strong>de</strong>fini<strong>en</strong>do <strong>los</strong> finales <strong>de</strong> línea como<br />
\r\n con la s<strong>en</strong>t<strong>en</strong>cia System.setProperty (“line.separador”, ”\r\n”);<br />
7. Cierre <strong>de</strong> la conexión<br />
Las conexiones se cierran mediante el método c<strong>los</strong>e() <strong>de</strong> la clase<br />
Socket. Una vez que un socket se ha cerrado, ya no está disponible para<br />
volverlo a utilizar (por ejemplo, no pue<strong>de</strong> ser reconectado). En caso<br />
necesario, hay que volver a crear un nuevo socket. Si este socket t<strong>en</strong>ía un<br />
flujo asociado, el flujo se cierra también.
P3-8 <strong>Práctica</strong>s <strong>de</strong> <strong>Re<strong>de</strong>s</strong> <strong>de</strong> Computadores<br />
8. Explorador <strong>de</strong> puertos<br />
¿Está seguro nuestro ord<strong>en</strong>ador? ¿Es fácil <strong>en</strong>trar <strong>en</strong> él <strong>de</strong> forma no<br />
autorizada <strong>de</strong>s<strong>de</strong> otro ord<strong>en</strong>ador? Respon<strong>de</strong>r a esta pregunta es complicado.<br />
En g<strong>en</strong>eral, sabemos que para <strong>en</strong>trar <strong>en</strong> nuestro ord<strong>en</strong>ador <strong>de</strong>s<strong>de</strong> otro equipo<br />
es necesario que <strong>en</strong> alguno <strong>de</strong> <strong>los</strong> puertos <strong>de</strong> nuestro ord<strong>en</strong>ador haya un<br />
servidor escuchando. En el caso más extremo, si todos <strong>los</strong> puertos <strong>de</strong><br />
nuestro ord<strong>en</strong>ador están cerrados, t<strong>en</strong>emos la seguridad <strong>de</strong> que no vamos a<br />
aceptar datos que prov<strong>en</strong>gan <strong>de</strong> la red, por lo que evitamos la <strong>en</strong>trada no<br />
<strong>de</strong>seada <strong>de</strong> intrusos.<br />
Cerrar todos <strong>los</strong> puertos <strong>de</strong>l ord<strong>en</strong>ador pue<strong>de</strong> no ser una bu<strong>en</strong>a i<strong>de</strong>a,<br />
pues seguram<strong>en</strong>te algunos <strong>de</strong> <strong>los</strong> servicios que usamos habitualm<strong>en</strong>te (y <strong>de</strong><br />
<strong>los</strong> cuales incluso no somos consci<strong>en</strong>tes) <strong>de</strong>jarán <strong>de</strong> funcionar, con la<br />
correspondi<strong>en</strong>te molestia.<br />
Una política más acertada es mant<strong>en</strong>er abiertos únicam<strong>en</strong>te <strong>los</strong> puertos<br />
que necesitamos y cerrar el resto. No obstante, a m<strong>en</strong>udo el sistema<br />
operativo se configura con <strong>de</strong>terminadas opciones por <strong>de</strong>fecto, <strong>de</strong>jando<br />
abiertos algunos puertos que seguram<strong>en</strong>te no <strong>de</strong>seamos que estén abiertos.<br />
En otros casos, es posible que un intruso haya abierto un puerto <strong>en</strong> nuestro<br />
ord<strong>en</strong>ador y haya <strong>de</strong>jado <strong>en</strong> él un servidor, <strong>de</strong>l cual no t<strong>en</strong>emos<br />
conocimi<strong>en</strong>to.<br />
La mejor manera <strong>de</strong> saber qué puertos están abiertos <strong>en</strong> nuestro<br />
ord<strong>en</strong>ador es usar un explorador <strong>de</strong> puertos. Realizar esta tarea <strong>en</strong> TCP es<br />
muy s<strong>en</strong>cillo. Basta con recorrer <strong>los</strong> puertos <strong>de</strong> nuestro ord<strong>en</strong>ador<br />
int<strong>en</strong>tando conectarnos a el<strong>los</strong>. Si un puerto nos permite conectarnos,<br />
significa que hay un servidor escuchando <strong>en</strong> él. Si rechaza la conexión,<br />
<strong>en</strong>tonces el puerto está cerrado. En el caso <strong>de</strong> UDP, hacer la exploración no<br />
es tan s<strong>en</strong>cilla, pues al ser un servicio sin conexión, si no recibimos<br />
contestación, nunca vamos a t<strong>en</strong>er la seguridad <strong>de</strong> qué es lo que ha pasado<br />
con nuestro m<strong>en</strong>saje.<br />
Para crear el explorador <strong>de</strong> puertos nos vamos a valer <strong>de</strong> la clase<br />
Socket, que ya hemos empleado <strong>en</strong> <strong>los</strong> ejercicios anteriores. Como hemos<br />
visto, su constructor pue<strong>de</strong> lanzar dos excepciones difer<strong>en</strong>tes:<br />
• UnknownHostException, que se g<strong>en</strong>era cuando el nombre <strong>de</strong>l ord<strong>en</strong>ador<br />
con el que queremos crear la conexión no pue<strong>de</strong> ser resuelto a una<br />
dirección IP.<br />
• IOException, que se g<strong>en</strong>era cuando no se pue<strong>de</strong> establecer la conexión<br />
por cualquier otro motivo, como por ejemplo que no haya un
<strong>Introducción</strong> a <strong>los</strong> <strong>sockets</strong> <strong>en</strong> <strong>Java</strong> P3-9<br />
servidor escuchando <strong>en</strong> el puerto especificado <strong>en</strong> <strong>los</strong> parámetros <strong>de</strong><br />
Socket.<br />
Utilizaremos esta segunda excepción para crear el explorador <strong>de</strong><br />
puertos, <strong>de</strong> manera que si mi<strong>en</strong>tras barremos <strong>los</strong> puertos <strong>de</strong> nuestro equipo<br />
se g<strong>en</strong>era esta excepción, sabremos que el puerto está cerrado. Si no se<br />
g<strong>en</strong>era, <strong>en</strong>tonces es que hay un servidor <strong>en</strong> ese puerto. Como lo habitual es<br />
que la mayoría <strong>de</strong> <strong>los</strong> puertos se <strong>en</strong>cu<strong>en</strong>tr<strong>en</strong> cerrados, nuestro explorador<br />
<strong>de</strong>be mostrar por pantalla un m<strong>en</strong>saje únicam<strong>en</strong>te si el puerto está abierto.<br />
En caso contrario, suel<strong>en</strong> salir tantos m<strong>en</strong>sajes que resulta difícil averiguar<br />
todos <strong>los</strong> puertos abiertos.<br />
Ejercicio 8:<br />
Teclea y completa el sigui<strong>en</strong>te explorador <strong>de</strong> puertos. Ejecútalo. ¿Qué puertos<br />
hay abiertos?¿A qué servicios correspond<strong>en</strong> esos puertos? (<strong>en</strong> /etc/services<br />
hay un listado <strong>de</strong>tallado <strong>de</strong> <strong>los</strong> servicios ord<strong>en</strong>ados por número <strong>de</strong> puerto).<br />
import java.net.*;<br />
import java.io.*;<br />
public class LowPortScanner {<br />
public static void main(String[] args) {<br />
String host = "localhost";<br />
for (int puerto = 1; puerto < 1024; puerto ++) {<br />
try {<br />
// COMPLETAR CÓDIGO<br />
}<br />
catch (UnknownHostException e) {<br />
// COMPLETAR CÓDIGO<br />
}<br />
catch (IOException e) {<br />
// COMPLETAR CÓDIGO<br />
}<br />
} // <strong>en</strong>d for<br />
} // <strong>en</strong>d main<br />
} // <strong>en</strong>d PortScanner<br />
Ejercicio 9:<br />
Elimina <strong>de</strong> tu ord<strong>en</strong>ador todos <strong>los</strong> ficheros que has creado durante el <strong>de</strong>sarrollo<br />
<strong>de</strong> esta práctica.