15.05.2013 Views

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 ...

SHOW MORE
SHOW LESS

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.

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!