No es un bug, es una característica no documentada

jueves, 21 de enero de 2016

Procesos y servicios. Programación de Sockets (II)

Puedes leer la entrada anterior pulsando en (I)

Conexión de múltiples clientes. Hilos

Un único servidor con la clase ServerSocket e invocar al método accept() para esperar las peticiones de conexión de los clientes.

Cuando un cliente se conecta, el método accept() devuelve un objeto Socket, éste se usará para crear un hilo cuya misión es atender a este cliente.

Después se vuelve a invocar a accept() para esperar a un nuevo cliente; habitualmente la espera de conexiones se hace dentro de un bucle infinito.

Archivo Servidor.java

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class Servidor {
     
      public static void main(String args[]) throws IOException {
           
            ServerSocket servidor;
            servidor = new ServerSocket(6000);   
            System.out.println("Servidor iniciado...");
            while (true) {
                  Socket cliente = new Socket();
                  cliente=servidor.accept();//esperando cliente
                  HiloServidor hilo = new HiloServidor(cliente);
                  hilo.start(); //Se atiende al cliente
                 
            }// Fin de while
      }// Fin de main
}// Fin de Servidor

Clases para Sockets UDP

Los Sockets UDP son más simples y eficientes que los TCP pero no está garantizada la entrega de paquetes.

No es necesario establecer una “conexión” entre cliente y servidor, como en el caso de TCP.

Los datagramas deben contener explícitamente la dirección IP y el puerto de destino.


El paquete del datagrama está formado por los siguientes campos:


Clase DatagramPacket

Crea instancias de los paquetes Datagrama.

Constructores


Algunos métodos importantes

Clase DatagramSocket

Da soporte a sockets para el envío y recepción de datagramas UDP.

Constructores


Algunos métodos importantes son

Gestión de sockets UDP

En los sockets UDP no se establece conexión.

Podemos considerar servidor al que espera un mensaje y responde; y cliente al que inicia la comunicación.

Tanto uno como otro si desean ponerse en contacto necesitan saber en qué ordenador y en qué puerto está escuchando el otro.
  1. El servidor crea un socket asociado a un puerto local para escuchar peticiones de clientes. Permanece a la espera de recibir peticiones.
  2. El cliente creará un socket para comunicarse con el servidor. Para enviar datagramas necesita conocer su IP y el puerto por el que escucha. Utilizará el método send() del socket para enviar la petición en forma de datagrama.
  3. El servidor recibe las peticiones mediante el método receive() del socket. En el datagrama va incluido además del mensaje, el puerto y la IP del cliente emisor de la petición; lo que le permite al servidor conocer la dirección del emisor del datagrama. Utilizando el método send() del socket puede enviar la respuesta al cliente emisor.
  4. El cliente recibe la respuesta del servidor mediante el método receive() del socket.
  5. El servidor permanece a la espera de recibir más peticiones. 

Ejemplo

Archivo ServidorUDP.java

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class ServidorUDP {
     
      public static void main(String[] argv) throws Exception {
           
            byte[] bufer = new byte[1024];//bufer para recibir el datagrama
           
            //ASOCIO EL SOCKET AL PUERTO 12345
            DatagramSocket socket = new DatagramSocket(12345);
           
            //ESPERANDO DATAGRAMA
            System.out.println("Esperando Datagrama ................");
            DatagramPacket recibo = new DatagramPacket(bufer, bufer.length);
            socket.receive(recibo);//recibo datagrama
            int bytesRec = recibo.getLength();//obtengo numero de bytes   
            String paquete= new String(recibo.getData());//obtengo String
           
            //VISUALIZO INFORMACIÓN
            System.out.println("Número de Bytes recibidos: " + bytesRec);
            System.out.println("Contenido del Paquete    : " + paquete.trim());
            System.out.println("Puerto origen del mensaje: " + recibo.getPort());
            System.out.println("IP de origen             : " + recibo.getAddress().getHostAddress());
            System.out.println("Puerto destino del mensaje:" + socket.getLocalPort());
           
            socket.close(); //cierro el socket
           
      }//Fin de main
     
}// Fin de SerivdorUDP

El programa servidor (ServidorUDP) recibe un datagrama enviado por un programa cliente. El programa servidor  permanece a la espera hasta que le llega un paquete del cliente; en este momento visualiza: el número de bytes recibidos, el contenido del paquete, el puerto y la IP del programa cliente y el puerto local por el que recibe las peticiones.

Archivo ClienteUDP.java

import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class ClienteUDP {
     
      public static void main(String[] argv) throws Exception {
           
            InetAddress destino = InetAddress.getLocalHost();
            int port = 12345; //puerto al que envío el datagrama
            byte[] mensaje = new byte[1024];
           
            String Saludo="Enviando Saludos !!";
            mensaje = Saludo.getBytes(); //codifico String a bytes
           
            //CONSTRUYO EL DATAGRAMA A ENVIAR
            DatagramPacket envio = new DatagramPacket (mensaje, mensaje.length, destino, port);
            DatagramSocket socket = new DatagramSocket(34567);//Puerto local
            System.out.println("Enviando Datagrama de longitud: "+ mensaje.length);
            System.out.println("Host destino : "+ destino.getHostName());
            System.out.println("IP Destino : " + destino.getHostAddress());
            System.out.println("Puerto local del socket: " + socket.getLocalPort());
            System.out.println("Puerto al que envio: " + envio.getPort());
           
            //ENVIO DATAGRAMA
            socket.send(envio);
            socket.close(); //cierro el socket
     
      }//Fin de main
     
}//Fin de ClienteUDP

El programa cliente envía un mensaje al servidor (máquina destino, en este caso es la máquina local, localhost) al puero 12345 por el que espera peticiones. Visualiza el nombre del host de destino y la dirección IP. También visualiza el puerto local del socket y el puerto al que envía el mensaje.

Ejemplo 2

Archivo ClienteUDP2.java

import java.io.*;
import java.net.*;

public class ClienteUDP2 {
     
      public static void main(String args[]) throws Exception {
           
            // FLUJO PARA ENTRADA ESTANDAR
            BufferedReader in = new BufferedReader (new InputStreamReader(System.in));
           
            DatagramSocket clientSocket = new DatagramSocket();//socket cliente   
            byte[] enviados = new byte[1024];
            byte[] recibidos = new byte[1024];
           
            // DATOS DEL SERVIDOR al que enviar mensaje
            InetAddress IPServidor = InetAddress.getLocalHost();// localhost
            int puerto = 9876; // puerto por el que escucha
           
            // INTRODUCIR DATOS POR TECLADO
            System.out.print("Introduce mensaje: ");
            String cadena = in.readLine();
            enviados = cadena.getBytes();
           
            // ENVIANDO DATAGRAMA AL SERVIDOR
            System.out.println("Enviando " + enviados.length + " bytes al servidor.");
            DatagramPacket envio = new DatagramPacket (enviados, enviados.length, IPServidor, puerto);
            clientSocket.send(envio);
           
            // RECIBIENDO DATAGRAMÄ DEL SERVIDOR
            DatagramPacket recibo = new DatagramPacket (recibidos, recibidos.length);
            System.out.println("Esperando datagrama....");
            clientSocket.receive(recibo);
            String mayuscula = new String(recibo.getData());
           
            // OBTENIDENDO INFORMACIÓN DEL DATAGRAMA
            InetAddress IPOrigen = recibo.getAddress();
            int puertoOrigen = recibo.getPort();
            System.out.println("\tProcedente de: " + IPOrigen + ":" + puertoOrigen);
            System.out.println("\tDatos: " + mayuscula.trim());
           
            //cerrar socket
            clientSocket.close();
           
      }//Fin de main
     
}//Fin de ClienteUDP2

El programa cliente envía un texto tecleado en su entrada estándar al servidor (en un pueto pactado), el servidor lee el datagrama y devuelve al cliente el texto en mayúscula. El programa cliente recibe un datagrama del servidor y muestra información del mismo en pantalla (IP, puerto del servidor y el texto en mayúscula).

Archivo ServidorUDP2.java

import java.io.*;
import java.net.*;

public class ServidorUDP2 {
     
      public static void main(String args[]) throws Exception {
           
            //Puerto por el que escucha el servidor: 9876
            DatagramSocket serverSocket = new DatagramSocket(9876);
            byte[] recibidos = new byte[1024];
            byte[] enviados = new byte[1024];
            String cadena;
           
            while(true) {
                  System.out.println ("Esperando datagrama.....");
                 
                  //RECIBO DATAGRAMA
                  recibidos = new byte[1024];
                  DatagramPacket paqRecibido = new DatagramPacket (recibidos, recibidos.length);
                  serverSocket.receive(paqRecibido);
                  cadena = new String(paqRecibido.getData());
                 
                  //DIRECCION ORIGEN
                  InetAddress IPOrigen = paqRecibido.getAddress();
                  int puerto = paqRecibido.getPort();
                  System.out.println ("\tOrigen: " + IPOrigen + ":" + puerto);
                  System.out.println ("\tMensaje recibido: " + cadena.trim());
                 
                  //CONVERTIR CADENA A MAYÚSCULA
                  String mayuscula = cadena.trim().toUpperCase();
                  enviados = mayuscula.getBytes();
                 
                  //ENVIO DATAGRAMA AL CLIENTE
                  DatagramPacket paqEnviado = new DatagramPacket (enviados, enviados.length, IPOrigen, puerto);
                  serverSocket.send(paqEnviado);
                 
                  //Para terminar
                  if(cadena.trim().equals("*")) break;
                 
            }//Fin de while
           
            serverSocket.close();
            System.out.println ("Socket cerrado...");
     
      }//Fin de main
     
}//Fin de ServidorUDP2

El programa servidor finaliza cuando recibe como cadena un asterisco.

MulticastSocket

La clase MulticastSocket es útil para enviar paquetes a múltiples destinos simultáneamente.

Para poder recibir estos paquetes es necesario establecer un grupo multicast, que es un grupo de direcciones IP que comparten el mismo número de puerto.

Cuando se envía un mensaje a un grupo de multicast, todos los que pertenezcan a ese grupo recibirán el mensaje.

La pertenencia al grupo es transparente al emisor, es decir, el emisor no conoce el número de miembros del grupo ni sus direcciones IP.

Grupo multicast

Un grupo multicast sse especifica mediante una dirección IP de clase D y un número de puerto UDP estándar.

Las direcciones desde la 224.0.0.0 a la 239.255.255.255 están destinadas para ser direcciones de multicast.

La dirección 224.0.0.0 está reservada y no debe ser utilizada.

Constructores


Algunos métodos importantes son


Esquema general para un servidor multicast:
  • Se crea el socket multicast. No hace falta especificar puerto

MuslticastSocket ms = new MulticastSocket();
  • Se define el pueto multicast

int Puerto = 12345;
  • Se crea el grupo multicast

InetAddress grupo = InetAddress.getByName(“225.0.0.1”);
  • Se crea el datagrama

DatagramPacket paquete = new DatagramPacket(msg.getBytes(), msg.length(), grupo, Puerto);
  • Se envía el paquete al grupo

ms.send(paquete);
  • Se cierra el socket

ms.close();

Esquema general para un cliente multicast:

  • Se crea un socket multicast en el puerto establecido

MulticastSocket ms = new MulticastSocket(12345);
  • Se configura la IP del grupo al que nos conectaremos

InetAddress grupo = InetAddress.getByName(“225.0.0.1”);
  • Se une al grupo

ms.joinGroup(grupo);
  • Recibe el paquete del servidor multicast

byte[] buf = new byte[1000];
DatagramPacket recibido = new DatagramPacket(buf, buf.length);
ms.receive(recibido);
  • Salimos del grupo multicast:

ms.leaveGroup(grupo);
  • Se cierra el socket

ms.close();

Ejemplo

En este ejemplo tenemos un servidor multicast que lee datos por teclado y los envía a todos los clientes que pertenezcan al grupo multicast, el proceso terminará cuando se introduzca un asterisco.

El programa cliente visualiza el paquete que recibe el servidor, su proceso finaliza cuando recibe un asterisco.

Archivo ServidorMC.java

import java.io.*;
import java.net.*;

public class ServidorMC {
     
      public static void main(String args[]) throws Exception {
           
            // FLUJO PARA ENTRADA ESTANDAR
            BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
           
            //Se crea el socket multicast.
            MulticastSocket ms = new MulticastSocket();
            int Puerto = 12345;//Puerto multicast
            InetAddress grupo = InetAddress.getByName("225.0.0.1");//Grupo
            String cadena="";
             
            while(!cadena.trim().equals("*")) {
                 
                  System.out.print("Datos a enviar al grupo: ");
                  cadena = in.readLine();
                 
                  // ENVIANDO AL GRUPO
                  DatagramPacket paquete = new DatagramPacket (cadena.getBytes(), cadena.length(), grupo, Puerto);
                  ms.send (paquete);
                 
            }//Fin de while
           
            //cierro socket
            ms.close ();
            System.out.println ("Socket cerrado...");
           
      }//Fin de main
     
}//Fin de ServidorMC

Archivo ClienteMC.java

import java.io.*;
import java.net.*;

public class ClienteMC {
     
      public static void main(String args[]) throws Exception {
           
            //Se crea el socket multicast
            int Puerto = 12345;//Puerto multicast
           
            MulticastSocket ms = new MulticastSocket(Puerto);
            InetAddress grupo = InetAddress.getByName("225.0.0.1");//Grupo
           
            //Nos unimos al grupo
            ms.joinGroup (grupo);
            String msg="";
             
             
            while(!msg.trim().equals("*")) {
                 
                  //Recibe el paquete del servidor multicast
                  byte[] buf = new byte[1000]; // Genero dentro el buffer para que se sobreescriba al enviar un nuevo mensaje
                  DatagramPacket paquete = new DatagramPacket(buf, buf.length);
                  ms.receive(paquete);
                  msg = new String(paquete.getData());
                  System.out.println ("Recibo: " + msg.trim());
                 
            }//Fin de while
           
            ms.leaveGroup(grupo); //abandonamos grupo
           
            //cierra socket
            ms.close();
            System.out.println("Socket cerrado...");
           
     
      }//Fin de main
                       
}//Fin de ClienteMC

1 comentario: