Bases UDP en Java

Cours 02

Le protocole UDP

Le protocole UDP suit le principe de la poste.

  • socket UDP = boîtes aux lettres
  • adresse IP + numéro de port = adresse postale
  • paquet UDP = colis

Une socket UDP permet d'envoyer / recevoir des paquets vers / depuis n'importe quelle autre socket UDP accessible.

Pas de notion de connexion !

Principe UDP

User Datagram Protocol (RFC 768)

Garanties UDP

  • Les paquets peuvent être perdus.
  • Les paquets peuvent arriver dans un ordre différent de celui d'envoi.
  • Si un paquet arrive, il est identique à celui envoyé (préservation des limites).
  • Si on envoie P1 puis P2 à une même adresse, on peut recevoir P1 puis P2, ou P2 puis P1, ou P1 seulement ou P2 seulement ou bien rien du tout.

    IP en Java

    L'accès au protocole IP en Java est très limité.

    • Adresse IP
    • Adresse de socket

    Adresse IP

  • IPv4 (4 octets): 192.168.0.1
  • IPv6 (16 octets): FE80:0000:0000:0000:0202:B3FF:FE1E:8329
  • Représentés en Java par InetAddress avec deux sous-classes Inet4Address et Inet6Address.

    On travaillera toujours avec InetAddress.

    InetAddress

    Pas de constructeur public.
    Création avec la méthode InetAddress.getByName(String host).

    InetAddress addr1 = InetAddress.getByName("192.168.0.1");
    InetAddress addr2 = InetAddress.getByName("FE80:0:0:0:0202:B3FF:FE1E:8329");
    InetAddress addr3 = InetAddress.getByName("www.google.fr");
                                        
    System.out.println(addr1 + "\n" + addr2 + "\n" + addr3);
    
    /192.168.0.1
    /fe80:0:0:0:202:b3ff:fe1e:8329
    www.google.fr/64.233.166.94
    

    Dans le dernier cas, il y a une résolution DNS.

    InetAddress = address (numérique) + [ name (littéral) ]

    InetSocketAddress

    Adresse de socket = Adresse IP + port

    Plusieurs constructeurs :

    InetAddress inetAddress = InetAddress.getByName("www.google.com");
    
    InetSocketAddress insa1 = new InetSocketAddress(inetAddress, 7);
                                        
    InetSocketAddress insa2 = new InetSocketAddress("www.u-pem.fr", 7);
    
    InetSocketAddress insa3 = new InetSocketAddress("192.168.0.1", 1025);
    
    InetSocketAddress insa4 = new InetSocketAddress(7777);
    
    System.out.println(insa1 + "\n" + insa2 + "\n" + insa3 + "\n" + insa4);
    
    www.google.com/173.194.67.147:7
    www.u-pem.fr/193.50.159.151:7
    /192.168.0.1:1025
    0.0.0.0/0.0.0.0:7777
    

    DatagramChannel

    Les sockets UDP (les boîtes aux lettres) sont représentées par la classe DatagramChannel.

    On les crée avec la méthode factory : DatagramChannel.open

    DatagramChannel dc = DatagramChannel.open();
    

    Une fois crée, elle n'est pas encore "attachée" : elle n'a pas d'adresse de socket.

    DatagramChannel

    L'attachement (faire le lien entre la boîte aux lettre et son adresse) se fait avec datagramChannel.bind.

    dc.bind(null);
    

    Attachement à un numéro de port libre aléatoire.

    Envoi d'un paquet

    Pour envoyer un paquet, il faut :

    • les données (ByteBuffer) et
    • la socket adresse du destinataire (InetSocketAddress)

    On utilise la méthode send du DatagramChannel :

    send(ByteBuffer buffer, SocketAddress destination);

    DatagramChannel dc; 
    ...
    var buffer = StandardCharsets.UTF_8.encode("Hello world €");
    var destination = new InetSocketAddress("gaspard.univ-mlv.fr", 7);
    dc.send(buffer, destination);
    

    Réception d'un paquet

    Pour recevoir un paquet (dans la boîte aux lettres), il faut prévoir l'espace nécessaire dans un ByteBuffer pour stocker les données.
    La méthode receive renvoie l'adresse de l'expéditeur du paquet :

    SocketAddress datagramChannel.receive(ByteBuffer buffer);

    La méthode est bloquante.

    DatagramChannel dc;
    ...
    var buffer = ByteBuffer.allocate(1024);
    var sender = (InetSocketAddress) dc.receive(buffer);
    buffer.flip();
    System.out.println("Received " + buffer.remaining() + " bytes from " + sender);
    

    Attention au flip() !

    Si la taille du paquet reçu dépasse la taille de stockage, la fin est perdue sans notification.

    Zoom côté client (0/11)

    Zoom côté client (1/11)

    Zoom côté client (2/11)

    Zoom côté client (3/11)

    Zoom côté client (4/11)

    Zoom côté client (5/11)

    Zoom côté client (6/11)

    Zoom côté client (7/11)

    Zoom côté client (8/11)

    Zoom côté client (9/11)

    Zoom côté client (10/11)

    Zoom côté client (11/11)

    Libération des ressources

    La création open et l'attachement bind réservent des ressources système (descripteur et buffer associés au port réservé).

    Il faut impérativement les libérer explicitement :

    • soit avec datagramChannel.close();
    • soit avec un try-with-resources :
    • try( DatagramChannel dc = DatagramChannel.open() ){
          ...
      }