Tema 13 - Programación en Java para Red PDF
Tema 13 - Programación en Java para Red PDF
1. INTRODUCCIÓN....................................................................................................... 3
2. URLS......................................................................................................................... 3
2.1. CLASE URL......................................................................................................... 4
EJERCICIO 1.................................................................................................... 5
3. SOCKETS ................................................................................................................. 7
3.1. LA CLASE SOCKET ............................................................................................... 8
EJERCICIO 2.................................................................................................. 10
Ejercicio 3 ....................................................................................................... 12
Ejercicio 4 ....................................................................................................... 15
1. INTRODUCCIÓN
Java proporciona una serie de clases, englobadas en el paquete java.net, que permiten
realizar conexiones entre aplicaciones y transacciones de datos a través de la red.
Utilizando este paquete se pueden comunicar dos o más computadoras que estén
repartidas en distintos puntos del mundo incluso independientemente del tipo de equipo que
sea y de la plataforma utilizada.
En los próximos capítulos vamos a estudiar algunas de las clases más importantes del
paquete java.net, analizando las dos formas principales de establecer comunicaciones entre
aplicaciones en Java: mediante URLs y mediante sockets.
2. URLS
Una URL (Uniform Resource Location) representa la dirección de un recurso dentro de
una red. Toda URL se caracteriza por una cadena de caracteres identificativa que tiene el
siguiente formato:
identificador_protocolo://localización_recurso
En este caso, la cadena identificativa para la localización del recurso tiene el siguiente
formato:
nombre_dominio:protocolo/aplicacion/dir_recurso
Siendo:
http://www.syncrom.com:8080/proyectos/index.html
hace referencia a un recurso de tipo documento HTML que pertenece a la aplicación
“proyectos” que se encuentra publicada en el servidor www.syncrom.com que escucha las
peticiones http por el puerto 8080.
Con el que podemos construir un objeto URL a partir de la cadena de caracteres que la
identifica.
Entre los métodos más importantes que proporciona esta clase destacamos:
A partir del objeto InputStream devuelto por el método openStream() podemos construir
un objeto InputStreamReader que, a su vez, nos permita crear un objeto BufferedReader con el
que poder leer el texto contenido en un recurso remoto de la misma forma en que se realizaba
la lectura de datos de un archivo de texto almacenado en disco.
new InputStreamReader(url.openStream()));
Hay que tener en cuenta que la creación del objeto URL puede lanzar una excepción
de tipo MalformedURLException si se emplea un protocolo desconocido o la cadena tiene un
formato incorrecto.
EJERCICIO 1
En este primer ejercicio vamos a realizar una aplicación que muestre en pantalla el
contenido de una página de inicio del servidor google.
import java.net.*;
import java.io.*;
public class Principal {
public static void main(String[] args){
try{
URL url =new URL("http://www.google.es");
BufferedReader bf=new BufferedReader(new
InputStreamReader(url.openStream()));
String cad="";
while((cad=bf.readLine())!=null){
System.out.println(cad);
}
}
catch(MalformedURLException e){
System.out.println("URL incorrecta");
}
catch(IOException e){
System.out.println("Error en la entrada/salida de
datos");
}
}
}
Por ejemplo, el siguiente código nos mostraría las cookies enviadas por el
servidor al conectar con la página de inicio de google:
URL url =new URL("http://www.google.es");
URLConnection con=url.openConnection();
System.out.println(con.getHeaderField(“Set-Cookie”));
El siguiente programa de ejemplo nos mostraría en pantalla todos los datos asociados
al servidor Web de Syncrom:
import java.net.*;
public class Principal {
public static void main(String[] args) throws
UnknownHostException{
InetAddress dir=
InetAddress.getByName("www.syncrom.com");
System.out.println("nombre host :"+dir.getHostName());
System.out.println("nombre host completo "+
dir.getCanonicalHostName());
System.out.println("dir ip "+dir.getHostAddress());
for(int i=0;i<dir.getAddress().length;i++){
System.out.println(dir.getAddress()[i]);
}
}
}
3. SOCKETS
Un socket representa el punto de conexión utilizado por un proceso para comunicar con
otro proceso a través de una red TCP o UDP.
Según cuál de estos protocolos se utilice, distinguiremos entre dos tipos de sockets:
La clase Socket se utiliza habitualmente para crear sockets a nivel de aplicación cliente
utilizando el protocolo TCP. La comunicación a través de este tipo de socket requiere que se
indique la localización de la máquina con la que se establecerá la conexión y el puerto de la
misma por el que se producirá la comunicación.
Equipo: xxx
Puerto n
Aplicación
cliente
socket
Host:xxx
Puerto:n
Figura. 1.
Socket (String host, int puerto): Crea un socket con un nombre de host y en
un puerto específico. Además de IOException, este constructor puede lanzar
una excepción UnknownHostException en caso de que no exista el nombre de
host indicado.
import java.net.*;
import java.io.*;
public class Test {
public static void main(String[] args) throws
UnknownHostException, IOException {
String url="java.sun.com";
int puerto =80;
Socket sc=new Socket(url, puerto);
BufferedReader bf=new BufferedReader(
new InputStreamReader(sc.getInputStream()));
PrintWriter out=new PrintWriter(
sc.getOutputStream(),true);
out.println("GET /");
String s=null;
while((s=bf.readLine())!=null){
System.out.println(s);
}
}
}
Además de los anteriores, la clase Socket dispone de una serie de métodos que nos
permiten obtener información sobre el mismo:
Una vez finalizado el intercambio de datos entre una aplicación y el punto final
representado por el socket, se debe proceder al cierre del mismo a fin de liberar los recursos
utilizados, operación esta que se lleva a cabo a través del método close() de la clase Socket.
Hay ocasiones en las que a la hora de enviar información por un socket a través del
stream de salida los datos no llegan hasta el punto final mientras el socket esté abierto. En este
caso podemos recurrir al método flush() del objeto PrintWriter utilizado para la escritura, el cual
realiza el vaciado del stream provocando que los datos lleguen de forma inmediata a su
destino.
EJERCICIO 2
En este ejercicio se va a desarrollar una aplicación que enviará una cadena de texto al
puerto de eco (puerto nº 7) de un servidor a través de un socket; dado que se trata del puerto
de eco, el servidor devolverá inmediatamente la cadena recibida, la cual se mostrará en
pantalla.
Hay que tener en cuenta que este programa no funcionará si el equipo contra el
queremos conectarnos dispone de un cortafuegos que impida el acceso al puerto de eco.
import java.net.*;
import java.io.*;
public class Eco {
public static void main(String[] args) {
String url="java.sun.com";
int puerto=7;
try{
Socket sc=new Socket(url,puerto);
//objeto para lectura del teclado
BufferedReader bfteclado=new BufferedReader(
new InputStreamReader(System.in));
//objeto para lectura de los datos recibidos
//del servidor a través del socket
BufferedReader bfsocket=new BufferedReader(
new InputStreamReader(sc.getInputStream()));
//envía al servidor los datos introducidos
//por teclado
PrintWriter out=new PrintWriter(
sc.getOutputStream(),true);
System.out.println("escribe un texto");
out.println(bfteclado.readLine());
out.flush();
//muestra la respuesta del servidor
System.out.println("Eco devuelto por servidor: "+
bfsocket.readLine());
sc.close();
}
catch(UnknownHostException e){
System.out.println("El host indicado no existe");
}
catch(IOException e){
System.out.println("Error en la entrada/salida de
datos");
}
}
}
Como decíamos anteriormente, una vez creado el socket de servidor el objeto queda
preparado para recibir conexiones a través del puerto indicado. La llamada al método accept()
de ServerSocket bloquearía la aplicación hasta recibir una conexión, momento en el cual
accept() devolvería un objeto Socket asociado a dicha conexión.
Utilizando este objeto socket la aplicación del servidor puede enviar y recibir
información hacia/desde la aplicación cliente.
Ejercicio 3
Por un lado crearemos una aplicación de servidor que utilizará un objeto ServerSocket
para recibir las peticiones por parte de los procesos cliente. Cada vez que se reciba una
petición, este proceso recuperará la información enviada por el cliente y se le enviarán a través
de la conexión creada diez mensajes de texto en el que se le indique el número de orden que
hace su petición en el conjunto total. Para que el proceso servidor pueda atender múltiples
solicitudes clientes de forma simultánea, cada petición será atendida por un hilo diferente.
Por otro lado, el desarrollo del proceso cliente consistirá en una aplicación de consola
que se encargará de conectar con el servidor y pasarle una cadena de texto solicitada al
usuario, así como de mostrar en pantalla la respuesta generada por el servidor.
package servidor;
import java.net.*;
import java.io.*;
public class IniciarServidor {
public static void main(String[] args) {
int puerto=3000;
int orden=1;
try{
ServerSocket servidor=new ServerSocket(puerto);
while(true){
System.out.println("Esperando petición...");
//cuando recibe una petición desde un cliente
// crea un hilo para atenderlo, pasándole el
// socket que representa la conexión con
//el cliente
Socket con=servidor.accept();
(new ProcesaConexion(con,orden)).start();
orden++;
}
}
catch(UnknownHostException e){
System.out.println("El host indicado no existe");
}
catch(IOException e){
System.out.println("Error en la entrada/salida de
datos");
}
}
}
La clase del servidor encargada de procesar las peticiones clientes se muestra en el
siguiente listado:
package servidor;
import java.net.*;
import java.io.*;
public class ProcesaConexion extends Thread{
Socket sc;
int orden;
public ProcesaConexion(Socket sc, int orden){
this.sc=sc;
this.orden=orden;
}
public void run(){
try{
PrintWriter out=new
PrintWriter(sc.getOutputStream());
BufferedReader bf=new BufferedReader(new
InputStreamReader(sc.getInputStream()));
//recupera la cadena enviada por el cliente
//al establecer la conexión
String nombre=bf.readLine();
//envia 10 mensajes al cliente indicandole su orden
//en el conjunto de peticiones recibidas
for(int i=1;i<=10;i++){
out.println("mensaje "+i+
" para el cliente "+
nombre+": Su número de orden es "+orden);
out.flush();
Thread.sleep(200);
}
sc.close();
}
catch(IOException e){
System.out.println("Error en la entrada/salida de
datos");
}
catch(InterruptedException e){
e.printStackTrace();
}
}
}
Finalmente, el proceso cliente será implementado por la siguiente clase:
package cliente;
import java.net.*;
import java.io.*;
public class IniciarCliente {
public static void main(String[] args) {
int puerto=3000;
String mensaje=null;
try{
Socket conexion=new Socket("localhost",puerto);
PrintWriter out=new PrintWriter(
conexion.getOutputStream(),true);
//solicita un dato al cliente
BufferedReader lectorteclado=new BufferedReader(
new InputStreamReader(System.in));
System.out.println("Introduce tu nombre");
//envía el dato solicitado al servidor
out.println(lectorteclado.readLine());
out.flush();
//recupera la respuesta generada por el servidor
//al enviarle el dato y la muestra en pantalla
BufferedReader lectorsocket=new BufferedReader(
new InputStreamReader(conexion.getInputStream()));
while((mensaje=lectorsocket.readLine())!=null){
System.out.println("Mensaje recibido "+mensaje);
}
conexion.close();
}
catch(UnknownHostException e){
System.out.println("El host indicado no existe");
}
catch(IOException e){
System.out.println("Error en la entrada/salida de
datos");
}
}
}
Ambos procesos (cliente y servidor) pueden ejecutarse en la misma máquina. De
hecho, como se puede apreciar en los listados anteriores, cada uno tiene su propia clase main.
Para simular el funcionamiento concurrente con múltiples clientes, la clase IniciarCliente deberá
ejecutarse tantas veces como cliente se quieran generar.
Ejercicio 4
Figura. 2.
Identificador_tienda|cantidad|fecha|host_cliente
package cliente;
import javax.swing.*;
import java.awt.event.*;
public class Interfaz extends JFrame implements ActionListener{
JTextField jtxtid,jtxtcant;
public Interfaz(String title, int x, int y, int w, int h){
super(title);
this.getContentPane().setLayout(null);
this.setBounds(x, y, w, h);
//controles para introducción de identificador
JLabel jlbid=new JLabel("Identificador de tienda");
jlbid.setBounds(100, 100, 160, 30);
this.getContentPane().add(jlbid);
jtxtid=new JTextField();
jtxtid.setBounds(240, 100, 50, 25);
this.getContentPane().add(jtxtid);
//controles para introdución de cantidad
JLabel jlbcant=new JLabel("Cantidad vendida");
jlbcant.setBounds(100, 180, 160, 30);
this.getContentPane().add(jlbcant);
jtxtcant=new JTextField();
jtxtcant.setBounds(240, 180, 50, 25);
this.getContentPane().add(jtxtcant);
//botón enviar
JButton bt=new JButton("Enviar");
bt.setBounds(150,260, 120, 40);
this.add(bt);
//asociación con el manejador de evento
bt.addActionListener(new ProcesarEnvio(this));
//botón salir
JButton btsalir=new JButton("Salir");
btsalir.setBounds(280,260, 120, 40);
this.add(btsalir);
//asociación con el manejador de evento
btsalir.addActionListener(this);
this.setVisible(true);
}
package cliente;
import java.net.*;
import java.io.*;
import javax.swing.*;
import java.awt.event.*;
public class ProcesarEnvio implements ActionListener{
private Interfaz ventana;
public ProcesarEnvio(Interfaz ventana){
this.ventana=ventana;
}
public void actionPerformed(ActionEvent e){
//recupera los datos de los campos de texto
//y monta la primera parte de la cadena
String identificador=this.ventana.jtxtid.getText();
String cantidad=this.ventana.jtxtcant.getText();
String datoenvio=identificador+"|"+cantidad+"|";
String host="localhost";
int puerto=3000;
//conecta con el servidor y le envía la cadena
//con el identificador y la cantidad
try{
Socket conexion=new Socket(host,puerto);
PrintWriter out=new PrintWriter(
conexion.getOutputStream(),true);
out.println(datoenvio);
out.flush();
out.close();
}
catch(UnknownHostException ex){
System.out.println("El host indicado no existe");
}
catch(IOException ex){
System.out.println("Error en la entrada/salida de
datos");
}
}
}
package servidor;
import java.net.*;
import java.io.*;
public class IniciarServidor {
package servidor;
import java.net.*;
import java.io.*;
import java.util.*;
public class ProcesaConexion extends Thread{
Socket sc;
FileWriter f;
public ProcesaConexion(Socket sc, FileWriter f){
this.sc=sc;
this.f=f;
}
public void run(){
try{
PrintWriter out=new
PrintWriter(sc.getOutputStream());
BufferedReader bf=new BufferedReader(new
InputStreamReader(sc.getInputStream()));
//recupera los datos enviados por el cliente
//al establecer la conexión
String dato=bf.readLine();
//agrega el resto de datos necesarios
dato=montarCadena(dato);
//escribe la cadena completa en el fichero
//utilizando un bloque sincronizado para evitar
//interferencias entre los clientes
synchronized(f){
f.write(dato);
f.write(System.getProperty ("line.separator"));
f.flush();
f.notify();
}
}
catch(IOException e){
System.out.println("Error en la entrada/salida de
datos");
}
}
public String montarCadena(String dato){
Calendar c=Calendar.getInstance();
//añade la fecha actual a la cadena
String fecha="";
fecha+=c.get(Calendar.DAY_OF_MONTH)+
"-"+(c.get(Calendar.MONTH)+1)+
"-"+c.get(Calendar.YEAR);
dato+=fecha;
//añade el nombre del host a la cadena
dato+="|"+sc.getLocalAddress().getHostName();
return dato;
}
}
AUTOEVALUACIÓN
1. Un objeto URL:
A. (new URL(dir)).getIP();
B. (new InetAddres(dir)).getHostAddress();
C. (InetAddress.getByName(dir)).getHostAddress();
D. (new URLConnection(dir)).toString();
String dirserver=”nombre_servidor”;
try{
out.pintln(textocualquiera);
out.close();
}
}
catch(UnknownHostException e){
System.out.println("host desconocido");
catch(IOException e){
5. Para establecer una conexión con un recurso de servidor cuya url se encuentra
almacenada en la variable de tipo String “dir”, a través de un objeto
URLConnection, se debería proceder:
8. Si a partir de un socket que nos conecta con un host remoto y que está
referenciado por la variable “sc”, queremos conocer la dirección IP de dicho host
deberíamos escribir:
A. sc.getInetAddress().getHostAddress();
B. sc.getRemoteAddress();
C. sc.getHost().getAddress();
D. sc.getLocalAddress();
Ejercicios Propuestos
1. Realizar una programa que solicite por teclado el nombre de un host externo y nos
muestre por pantalla la dirección ip del mismo. Así mismo, se preguntará al
usuario si quiere ver la página de inicio de dicho servidor y, en caso afirmativo, se
solicitará ésta al servidor y se mostrará en la consola (tal cual sea enviada por el
servidor)
2. Realizar un programa basado en interfaz gráfica con dos campos de texto donde
el usuario introducirá los nombres de dos servidores (host) remotos. Al pulsar un
botón de título “comprobar”, el programa nos indicará si los servidores se
encuentran o no en el mismo rango de IPs (los tres primeros números de los
cuatro que forman la IP deberán ser iguales).