Protocolul UDP

Protocolul UDP este un protocol fara stare, minimal ce nu garanteaza livrarea pachetelor sau ordinea de livare. In cazul UDP pachetele de date se mai numesc si datagrame. Datele pot fi trimise prin UDP fara a fi nevoie sa se seteze o conexiune in prealabil cu destinatarul pachetului, dar, ca si in cazul TCP, se utilizeaza porturilie pentru a identifica in mod unic aplicatiile.

Avantajul lucrului cu datagrame este creşterea vitezei cu care pachetele ajung la destinaţie. Există cazuri în care viteza de transmisie a datelor este mai importantă decât garantarea 100% a ajungerii acestora la destinaţie. De exemplu în cazul transmiterii unui semnal audio în timp real, viteza de transmitere a acestuia este mai importantă decât garantarea ajungerii la destinaţie.

In java pentru implementarea protocolului UDP sunt utilizate clasele: DatagramPacket şi DatagramSocket. Spre deosebire de programarea TCP, în cazul UDP nu există conceptul de ServerSocket. Atât serverul cât şi clientul utilizează DatagramSocket pentru realizarea conexiunii. Pentru transmiterea şi recepţionarea datelor se utilizează clasa DatagramPacket.

Aplicatie server de timp

In cadrul acestei secţiuni este construit un server de timp care va trimite la cerere data curentă către clienţii care solicită acest lucru. De asemenea este construit şi clientul care accesează serviciile serverului de timp.

La nivelul serverului se creează un obiect DatagramSocket, care va primi ca parametru portul pe care serverul va începe ascultarea.

 DatagramSocket socket = new DatagramSocket(1977);

In continuare se construieşte un obiect DatagramPacket, care va fi utilizat de către server pentru a recepţiona cererea de la client. O dată construit obiectul DatagramPacket, serverul va începe ascultarea portului 1977, prin invocarea metodei receive().

 byte[] buf = new byte[256];
 DatagramPacket packet = new DatagramPacket(buf,buf.length);
 socket.receive(packet);

In momentul în care un client doreşte să apeleze la serviciile serverului, acesta va trimite un pachet către server. Serverul citeşte din cadrul pachetului portul şi adresa clientului, şi îi va trimite acestuia un pachet ce conţine data curentă.

 InetAddress address = packet.getAddress();
 int port = packet.getPort();
 buf = ((new Date()).toString()).getBytes();
 packet = new DatagramPacket(buf,buf.length,address,port);
 socket.send(packet);

Un client, pentru a se conecta la server, trebuie să creeze un obiect DatagramSocket, şi să trimită un pachet către server. Spre deosebire de server, clientul nu este obligat să specifice nici un port în momentul creierii obiectului DatagramSocket, întrucât se atribuie automat un port liber respectivului obiect.

Program server

import java.io.*;
import java.net.*;
import java.util.*;
 
public class TimeServer extends Thread{
 
  boolean running=true;
  public TimeServer() {start();}
 
  public void run(){
  try{
    DatagramSocket socket = new DatagramSocket(1977);
    while(running){
      //asteapta client
      byte[] buf = new byte[256];
      DatagramPacket packet = new DatagramPacket(buf,buf.length);
      socket.receive(packet);
      //citeste adresa si portul clientului
      InetAddress address = packet.getAddress();
      int port = packet.getPort();
      //trimite un reply catre client
      buf = ((new Date()).toString()).getBytes();
      packet = new DatagramPacket(buf,buf.length,address,port);
      socket.send(packet);
   }
    }catch(Exception ex){ex.printStackTrace();}
  }
  public static void main(String[] args) {
    TimeServer timeServer1 = new TimeServer();
  }
}

Program client

 import java.io.*;
 import java.net.*;
 import java.util.*;
 
public class Client {
 
  public static void main(String[] args) {
  try{
    DatagramSocket socket = new DatagramSocket();
    byte[] buf = new byte[256];
 
    DatagramPacket packet = new DatagramPacket(buf,buf.length,
      InetAddress.getByName("localhost"),1977);
    socket.send(packet);
    packet = new DatagramPacket(buf,buf.length);
    socket.receive(packet);
 
    System.out.println(new String(packet.getData()));
  }catch(Exception ex){ex.printStackTrace();}
  }
}

Aplicatie de tip broadcast

Pe langa clasa DatagramSocket, Java include si clasa MulticatsSocket. Aceasta clasa este folosita la nivelul clientului, pentru a asculta mesaje care sunt trimise de catre server catre clienti multipli. Pentru a trimite mesaje catre clienti multipli serverul va folosi o abdresa de IP de tip multicats - o astfel de adresa nu identifica in mod unic un calculator - orice mesaj trimis catre o astfel de adresa va fi receptionat de catre toate echipamentele din LAN.

In cadrul aplicatie server se observa adresa catre care sunt trimise pachetele:

 InetAddress group = InetAddress.getByName("230.0.0.1");

Aceasta adresa este o adresa de tip multicast.

Serverul de mai jos asteapta intr-o bucla infinita mesaje de la clienti. In momentul receptionarii unui mesaj, este transmis un mesaj de tip broadcast avand acelasi continut ca si cel al mesajului receptionat. Mesajul de tip broadcast va fi receptionat de catre toi clientii care au facut join la adresa multicat.

Aplicatia server

import java.net.*;
public class MulticastServer extends Thread {
 
    DatagramSocket socket;
    boolean alive=true;
 
    public MulticastServer() throws SocketException{
        socket = new DatagramSocket(1977);
    }
 
    public void run(){
 
        try{
            System.out.println("Multicast server started.");
	        while(alive){
	            byte[] buf = new byte[256];
 
	            // receive request
	            DatagramPacket rcvdPacket = new DatagramPacket(buf, buf.length);
 
	            //serverul asteapta primire de pachete
	            socket.receive(rcvdPacket);
 
	            //adresa multicast catre care for fi trimise mesajele
	            InetAddress group = InetAddress.getByName("230.0.0.1");
 
	            //construieste pachetul
	            DatagramPacket packet = new DatagramPacket(buf, buf.length, 
                        group, 4446);
 
	            packet.setData(rcvdPacket.getData());
 
	            //trimte datele catre adresa multicast
	            socket.send(packet);
 
	        }
 
        }catch(Exception e){
            System.err.println("Server error: "+e.getMessage());
            e.printStackTrace();
        }
    }
 
    public static void main(String[] args)throws Exception {
        MulticastServer ms = new MulticastServer();
        ms.start();
 
 
    }
}

Aplicatia client

Pentru a receptiona mesaje de tip multicat, aplicatia client va executa urmatoarea secventa de instructiuni:

MulticastSocket socket = new MulticastSocket(port);
InetAddress group = InetAddress.getByName("230.0.0.1");
socket.joinGroup(group);

O data executata comanda joinGroup clientul va receptiona orice mesaj de tip broadcast trimis pe adresa 230.0.0.1.

Aplicatia client este compusa din doua fire de executie. Fir de executie ce executa metoda run() asteapta in bucla mesaje pe adresa de multicast si afiseaza mesajele receptionate pe ecran. Firul main citeste intr-o bucla linii de text de la tastatura si le trimite catre server.

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.*;
 
public class MulticastClient extends Thread{
 
    boolean alive=true;
    int port;
 
 
    /**
     * @param port
     */
    public MulticastClient(int port) {
        this.port = port;
    }
 
    public void run(){
        try{
 
        MulticastSocket socket = new MulticastSocket(port);
        //pregateste aplicatia client pentru a putea receptiona mesaje multicast
        InetAddress group = InetAddress.getByName("230.0.0.1");
        socket.joinGroup(group);
 
        DatagramPacket packet;
        while(alive){
        	byte[] buf = new byte[256];
            packet = new DatagramPacket(buf, buf.length);
            //asteapta receptionarea de pachete
            socket.receive(packet);
 
            //extrage continultul mesajlui din pachet
            String received = new String(packet.getData());
 
            //afiseaza mesajul
            System.out.println("rcv: " + received);
        }
        socket.leaveGroup(group);
        socket.close();
        }catch(Exception e){
            System.err.println("Error on client : "+e.getMessage());
            e.printStackTrace();
        }
    }
 
    public void sendMessage(String msg, String serverIP) throws Exception{
        DatagramSocket socket = new DatagramSocket();
        byte[] buf = new byte[256];
        InetAddress address = InetAddress.getByName(serverIP);
        DatagramPacket packet = new DatagramPacket(buf, buf.length, 
                                                   address, port);
 
        packet.setData(msg.getBytes());
 
        socket.send(packet);
    }
 
 
 
    public static void main(String[] args) throws Exception{
 
 
 
        //start listening for brodcasted messages
        MulticastClient mc = new MulticastClient(4446);
        mc.start();
 
 
        //intr-o bucla while citeste de la tastatura linii de text
        String msg="";
        while(!msg.equals("exit")){
 
	        BufferedReader sb = new BufferedReader(new InputStreamReader(System.in));
	        System.out.print("snd:");
	        //citeste o linie de la tastatura
	        msg = sb.readLine();
	        try{
	         //trimte mesajul catre serverul multicast care este pornit pe acelasi calculator
	         mc.sendMessage(msg,"127.0.0.1");
	        }catch(Exception e){
	            System.err.println("Error sending message:"+e.getMessage());
	        }
	    }
        System.exit(0);
      }
}