# Messaggi datagram e invio multicast

Abbiamo finora visto come la comunicazione in java tra un client e server avviene attraverso le socket: le socket creano un canale tra il client e server attraverso cui le due applicazioni comunicano. Il protocollo di comunicazione a livello trasporto è il **TCP** (*Trasmission Control Protocol*).

Le classi come visto per stabilire una connessione di tipo **TCP/IP** tra client e server sono `java.net.Socket` e `java.net.ServerSocket`.

Java mette a disposizione anche la possibilità di creare una connessione su protocollo **UDP** (*User Datagram Protocol*) tramite le classi `java.net.Datagram`, per il messaggio datagram, e `java.net.DatagramSocket`, la socket per l'invio e la ricezione del messaggio datagram.&#x20;

### DIFFERENZE TRA UDP e TCP

Da <https://en.wikipedia.org/wiki/User_Datagram_Protocol>

[Transmission Control Protocol](https://en.wikipedia.org/wiki/Transmission_Control_Protocol) is a connection-oriented protocol, which means that it requires handshaking to set up end-to-end communications. Once a connection is set up, user data may be sent bi-directionally over the connection.

* **Reliable** – Strictly only at [transport layer](https://en.wikipedia.org/wiki/Transport_layer), TCP manages message acknowledgment, retransmission and timeout. Multiple attempts to deliver the message are made. If it gets lost along the way, the server will re-request the lost part. In TCP, there's either no missing data, or, in case of multiple timeouts, the connection is dropped. (This reliability however does not cover [application layer](https://en.wikipedia.org/wiki/Application_layer), at which a separate acknowledgement flow control is still necessary)
* **Ordered** – If two messages are sent over a connection in sequence, the first message will reach the receiving application first. When data segments arrive in the wrong order, TCP buffers delay the out-of-order data until all data can be properly re-ordered and delivered to the application.
* **Heavyweight** – TCP requires three packets to set up a socket connection, before any user data can be sent. TCP handles reliability and [congestion control](https://en.wikipedia.org/wiki/Congestion_control).
* **Streaming** – Data is read as a [byte](https://en.wikipedia.org/wiki/Byte) stream, no distinguishing indications are transmitted to signal message (segment) boundaries.

User Datagram Protocol is a simpler message-based [connectionless protocol](https://en.wikipedia.org/wiki/Connectionless_protocol). Connectionless protocols do not set up a dedicated end-to-end connection. Communication is achieved by transmitting information in one direction from source to destination without verifying the readiness or state of the receiver.

* **Unreliable** – When a UDP message is sent, it cannot be known if it will reach its destination; it could get lost along the way. There is no concept of acknowledgment, retransmission, or timeout.
* **Not ordered** – If two messages are sent to the same recipient, the order in which they arrive cannot be predicted.
* **Lightweight** – There is no ordering of messages, no tracking connections, etc. It is a small transport layer designed on top of IP.
* **Datagrams** – Packets are sent individually and are checked for integrity only if they arrive. Packets have definite boundaries which are honored upon receipt, meaning a read operation at the receiver socket will yield an entire message as it was originally sent.
* **No congestion control** – UDP itself does not avoid congestion. Congestion control measures must be implemented at the application level.
* **Broadcasts** – being connectionless, UDP can broadcast - sent packets can be addressed to be receivable by all devices on the subnet.
* **Multicast** – a multicast mode of operation is supported whereby a single datagram packet can be automatically routed without duplication to very large numbers of subscribers.

Vedi anche: <https://www.comparitech.com/blog/vpn-privacy/udp-vs-tcp-ip/>

#### Utilizzo

TCP is best suited to be used for applications that require high reliability where timing is less of a concern.

* World Wide Web (HTTP, HTTPS)
* Secure Shell (SSH)
* File Transfer Protocol (FTP)
* Email (SMTP, IMAP/POP)

UDP is best suited for applications that require speed and efficiency.

* VPN tunneling
* Streaming videos
* Online games
* Live broadcasts
* Domain Name System (DNS)
* Voice over Internet Protocol (VoIP)
* Trivial File Transfer Protocol (TFTP)

### ESEMPIO COMUNICAZIONE CLIENT/SERVER su UDP

Esempio applicazione client, `QuoteClient`, che invia una richiesta al server stabilendo una connessione di tipo `UDP` e il server risponde inviando dei messaggio di risposta:

```java
import java.io.*;
import java.net.*;
import java.util.*;

public class QuoteClient {
	public static void main(String[] args) throws IOException {

		if (args.length != 1) {
			System.out.println("Usage: java QuoteClient <hostname>");
			return;
		}

		// get a datagram socket
		DatagramSocket socket = new DatagramSocket();

		// send request
		byte[] buf = new byte[256];
		InetAddress address = InetAddress.getByName(args[0]);
		DatagramPacket packet = new DatagramPacket(buf, buf.length, address, 4445);
		socket.send(packet);

		// get response
		packet = new DatagramPacket(buf, buf.length);
		socket.receive(packet);

		// display response
		String received = new String(packet.getData(), 0, packet.getLength());
		System.out.println("Quote of the Moment: " + received);

		socket.close();
	}
}

```

Le classi `QuoteServer` e `QuoteServerThread` implementano il server: il server al messaggio di richiesta del client invia un datagram di risposta contenente una frase: il server per sapere indirizzo e porta del client a cui rispondere, lo legge dal pacchetto che il client gli ha inviato:

```java
import java.io.*;
 
public class QuoteServer {
    public static void main(String[] args) throws IOException {
        new QuoteServerThread().start();
    }
}

```

```java
import java.io.*;
import java.net.*;
import java.util.*;

public class QuoteServerThread extends Thread {

	protected DatagramSocket socket = null;
	protected BufferedReader in = null;
	protected boolean moreQuotes = true;

	public QuoteServerThread() throws IOException {
		this("QuoteServerThread");
	}

	public QuoteServerThread(String name) throws IOException {
		super(name);
		socket = new DatagramSocket(4445);

		try {
			in = new BufferedReader(new FileReader("one-liners.txt"));
		} catch (FileNotFoundException e) {
			System.err.println("Could not open quote file. Serving time instead.");
		}
	}

	public void run() {

		while (moreQuotes) {
			try {
				byte[] buf = new byte[256];

				// receive request
				DatagramPacket packet = new DatagramPacket(buf, buf.length);
				socket.receive(packet);

				// figure out response
				String dString = null;
				if (in == null)
					dString = new Date().toString();
				else
					dString = getNextQuote();

				buf = dString.getBytes();

				// send the response to the client at "address" and "port"
				InetAddress address = packet.getAddress();
				int port = packet.getPort();
				packet = new DatagramPacket(buf, buf.length, address, port);
				socket.send(packet);
			} catch (IOException e) {
				e.printStackTrace();
				moreQuotes = false;
			}
		}
		socket.close();
	}

	protected String getNextQuote() {
		String returnValue = null;
		try {
			if ((returnValue = in.readLine()) == null) {
				in.close();
				moreQuotes = false;
				returnValue = "No more quotes. Goodbye.";
			}
		} catch (IOException e) {
			returnValue = "IOException occurred in server.";
		}
		return returnValue;
	}
}

```

### Multicast

Finora sia nel caso TCP/IP che UDP/IP abbiamo visto un tipo di comunicazione di tipo **unicast**, cioè punto-punto, tra client e server: il client invia una richiesta al client e il server risponde alla richiesta.

Non abbiamo ancora visto servizi di tipo **multicast**, in cui un server, invia uno stesso messaggio contemporaneamente a più client.

Il protocollo UDP permette anche l'invio di messaggi di tipo **multicast**, cioè messaggi inviati contemporaneamente a più host che si mettono in ascolto su tipi particolari di indirizzi IP detti **indirizzi di multicast**.

![Differenze tra unicast, broadcast e multicast](https://3264574854-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LVU7WiFKajQhwsnAlGh%2F-Lywu1KyH7tnI2CGljKT%2F-Lywvd2WG52ZSlYct04E%2FDifference-between-unicast-broadcast-and-multicast-diagram.png?alt=media\&token=67028025-268e-407d-83ba-659aeb4185b8)

**NOTA:** I dati sono trasportati sulla rete in tre semplici modi: Unicast, Broadcast e Multicast.

Per sintetizzare le differenze tra i tre modi:

* **Unicast**: da una sorgente a una destinazione - One-to-One;
* **Broadcast**: da una sorgente a tutte le possibili destinazioni - One-to-All;
* **Multicast**: da una sorgente a molte destinazioni che dichiarano l'interesse a ricevere il traffico -  One-to-Many;

Indirizzi di multicast sono indirizzi speciali; classe di indirizzamento `D`:

| Class | First Octet Range | Network (N), Host (H)                         | Subnet Mask   | No. of Networks | Hosts per Network |
| ----- | ----------------- | --------------------------------------------- | ------------- | --------------- | ----------------- |
| A     | 1-126             | N.H.H.H.                                      | 255.0.0.0     | 126             | 16.777.214        |
| B     | 128-191           | N.N.H.H.                                      | 255.255.0.0   | 16.382          | 65.534            |
| C     | 192-223           | N.N.N.H.                                      | 255.255.255.0 | 2.097.150       | 254               |
| `D`   | 224-239           | **Used for Multicasting**.                    |               |                 |                   |
| E     | 240-254           | Experimental; reserved for research purposes. |               |                 |                   |

All'interno degli indirizzi multicast:

| ***Start Address*** | ***End Address*** | ***Uses***                                            |
| ------------------- | ----------------- | ----------------------------------------------------- |
| 224.0.0.0           | 224.0.0.255       | Reserved for special "well known" multicast addresses |
| 224.0.1.0           | 238.255.255.255   | Globally scoped (Internet-wide) multicast address     |
| 239.0.0.0           | 239.255.255.255   | Administratively scoped (local) multicast addresses   |

Reference: [ http://www.tcpipguide.com/free/t\_IPMulticastAddressing.htm](http://www.tcpipguide.com/free/t_IPMulticastAddressing.htm)

Esempio, sotto, di servizio in multicast: i client ricevono dati dal server che invia a indirizzo multicast (nell'esempio indirizzo IP `230.0.0.1` e `porta` . I client per ricevere i datagram inviati dal server, devono utilizzare una `java.net.MulticastSocket` ed eseguire una **`joinGroup(InetAddress mcastaddr)`** e quando non si desidera più ricevere più pacchetti bisogna eseguire il metodo **`leaveGroup(InetAddress mcastaddr)`**.

Codice dell'applicazione server che invia messaggi:

{% code title="" %}

```java
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

public class UDPMulticastServer {

	public static void sendUDPMessage(String message, String ipAddress, int port) throws IOException {
		DatagramSocket socket = new DatagramSocket();
		InetAddress group = InetAddress.getByName(ipAddress);
		byte[] msg = message.getBytes();
		DatagramPacket packet = new DatagramPacket(msg, msg.length, group, port);
		socket.send(packet);
		socket.close();
	}

	public static void main(String[] args) throws IOException {
		sendUDPMessage("This is a multicast messge", "230.0.0.0", 4321);
		sendUDPMessage("This is the second multicast messge", "230.0.0.0", 4321);
		sendUDPMessage("This is the third multicast messge", "230.0.0.0", 4321);
		sendUDPMessage("OK", "230.0.0.0", 4321);
	}
}
```

{% endcode %}

Il codice dell'applicazione client che riceve i messaggi (vedi qui utilizzo di `java.net.MulticastSocket`):

{% code title="" %}

```java
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;

public class UDPMulticastClient implements Runnable {

	public static void main(String[] args) {
		Thread t = new Thread(new UDPMulticastClient());
		t.start();
	}

	public void receiveUDPMessage(String ip, int port) throws IOException {
		byte[] buffer = new byte[1024];
		MulticastSocket socket = new MulticastSocket(port);
		InetAddress group = InetAddress.getByName(ip);
		socket.joinGroup(group);
		while (true) {
			System.out.println("Waiting for multicast message...");
			DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
			socket.receive(packet);
			String msg = new String(packet.getData(), packet.getOffset(), packet.getLength());
			System.out.println("[Multicast UDP message received] >> " + msg);
			if ("OK".equals(msg)) {
				System.out.println("No more message. Exiting : " + msg);
				break;
			}
		}
		socket.leaveGroup(group);
		socket.close();
	}

	@Override
	public void run() {
		try {
			receiveUDPMessage("230.0.0.0", 4321);
		} catch (IOException ex) {
			ex.printStackTrace();
		}
	}
}

```

{% endcode %}

Complichiamo ora l'esempio precedente: il client è in attesa dei messaggi inviati in multicast come prima, ma, una volta ricevuto il messaggio, invia al server un messaggio di ACKNOLEDGE; il server quindi oltre a inviare i messaggi ai client in multicast, aspetta le risposte di ACKNOLEDGE. Per ogni messaggio inviato dal server devono arrivare tanti messaggi di ACKNOLEDGE quanti sono client collegati.

Vediamo il codice del client, `UDPMulticastClient2`, che riceve il messaggio multicast e invia l'ACKNOLEDGE per risposta al server:

```java
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.MulticastSocket;

public class UDPMulticastClient2 implements Runnable {

	public static void main(String[] args) {
		Thread t = new Thread(new UDPMulticastClient2());
		t.start();
	}

	public void receiveUDPMessage(String ip, int port) throws IOException {
		byte[] buffer = new byte[1024];
		MulticastSocket socket = new MulticastSocket(port);
		InetAddress group = InetAddress.getByName(ip);
		socket.joinGroup(group);
		
		while (true) {
			System.out.println("Waiting for multicast message...");
			DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
			socket.receive(packet);
			String msg = new String(packet.getData(), packet.getOffset(), packet.getLength());
			System.out.println("[Multicast UDP message received] >> " + msg);
			
			InetAddress addressSender = packet.getAddress();
			int portSender = packet.getPort();
			System.out.println("FROM: " + addressSender + 
					", ON PORT: " + portSender);
			
			// send acknoledge
			// get address and port to send acknoledge			
			
			byte[] msgAckn = "client AKN".getBytes();
			DatagramPacket packetAcknoledge = new DatagramPacket(msgAckn, msgAckn.length, addressSender, portSender);
			socket.send(packetAcknoledge);
			
			if ("OK".equals(msg)) {
				System.out.println("No more message. Exiting : " + msg);
				break;
			}
		}
		socket.leaveGroup(group);
		socket.close();
	}

	@Override
	public void run() {
		try {
			receiveUDPMessage("230.0.0.0", 4321);
		} catch (IOException ex) {
			ex.printStackTrace();
		}
	}
}

```

Il codice del server, `UDPMulticastServer2`, che invia i messaggi e riceve gli ACKNOLEDGE:

```java
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;

/**
 * Client give acknoledge they have received message.
 * 
 * @author Massimo
 *
 */
public class UDPMulticastServer2 {
	
	/**
	 * Thread di lettura degli ACKNOLEDGE inviati dai client
	 * @author Massimo
	 *
	 */
	private static class ReaderThread extends Thread {
		
		private DatagramSocket socket = null;
		private volatile boolean isToStop = false;
		
		public ReaderThread(DatagramSocket socket) {
			this.socket = socket;
		}
		
		public void doStopThread() {
			isToStop = true;
		}
		
		public void run() {
			
			while(!isToStop) {
				byte[] buf = new byte[256];
				DatagramPacket packet = new DatagramPacket(buf, buf.length);
				try {
					socket.receive(packet);
				} catch (IOException e) {
					if(!isToStop)
						e.printStackTrace();
					else {
						// exception OK because socket closed
						break;
					}
				}
				
				String received = new String(packet.getData(), 0, packet.getLength());
				
				InetAddress inetAddress = packet.getAddress();
				int port = packet.getPort();
				System.out.println(" |---->>> RECV: " + received + 
						"| - FROM: " + inetAddress + ", on PORT: " + port);
			}
			
			System.out.println("USCITA DAL THREAD LETTURA");
		}
	}

	public static void sendUDPMessage(DatagramSocket socket, String message, String ipAddress, int port) throws IOException {
		InetAddress group = InetAddress.getByName(ipAddress);
		byte[] msg = message.getBytes();
		DatagramPacket packet = new DatagramPacket(msg, msg.length, group, port);
		socket.send(packet);
	}

	private static void doSleep(int seconds) {
		try {
			Thread.sleep(seconds * 1000);
		} catch (InterruptedException e) {
			
		}
	}
	public static void main(String[] args) throws IOException {
		DatagramSocket socket = new DatagramSocket();
		
		ReaderThread readerThread = new ReaderThread(socket);
		readerThread.start();
		
		System.out.println("Invio primo messagio");
		
		sendUDPMessage(socket, "This is a multicast messge", "230.0.0.0", 4321);
		doSleep(3);
		
		System.out.println("Invio secondo messagio");
		
		sendUDPMessage(socket, "This is the second multicast messge", "230.0.0.0", 4321);
		doSleep(3);
		
		System.out.println("Invio terzo messagio");
		
		sendUDPMessage(socket, "This is the third multicast messge", "230.0.0.0", 4321);
		doSleep(3);
		
		System.out.println("Invio OK STOP");
		sendUDPMessage(socket, "OK", "230.0.0.0", 4321);
		
		doSleep(5);
				
		readerThread.doStopThread();
				
		System.out.println("In chiusura socket");
		socket.close();
	}
}

```

L'implementazione del client e server non è molto sofisticata (non ci sono molti controlli su gli ACKNOLEDGE ad esempio) per mantenere il codice semplice da capire e concentrarci sulla comprensione di come avviene lo scambio dei messaggi.

{% embed url="<https://docs.oracle.com/javase/tutorial/networking/datagrams/index.html>" %}

### UNA SEMPLICE APPLICAZIONE DI TIPO CHAT IN JAVA

Da: <https://www.geeksforgeeks.org/a-group-chat-application-in-java/>

Utilizzo di `java.net.MulticastSocket` per lavorare con indirizzi di multicast.

L'applicazione ha un thread che è in ascolto sulla `MulticastSocket` dei pacchetti inviati dagli altri utenti della chat. Con il metodo `receive(DatagramPacket p)` di `MulticastSocket` (ereditato dalla superclasse `DatagramSocket`) riceve i pacchetti. Prima però deve essere invocato il metodo `joinGroup(InetAddress mcastaddr)` di `MulticastSocket`.

Il codice del thread che è in ascolto dei messaggi inviati dalle altre chat:

```java
// metodo run del thread ReaderThread
@Override
public void run() {
	while (!GroupChat.finished) {
		byte[] buffer = new byte[ReadThread.MAX_LEN];
		DatagramPacket datagram = new DatagramPacket(buffer, buffer.length, group, port);
		String message;
		try {
			socket.receive(datagram);
			message = new String(buffer, 0, datagram.getLength(), "UTF-8");
			if (!message.startsWith(GroupChat.name))
				System.out.println(message);
		} catch (IOException e) {
			System.out.println("Socket closed!");
		}
	}
}
```

mentre nel main viene creata la `MulticastSocket`, eseguita la `joinGroup` ed il ciclo di invio dei messaggi digitati sulla tastiera tramite `void send(DatagramPacket p)`:

```java
InetAddress group = InetAddress.getByName("230.0.0.1");
int port = 3000;
				
MulticastSocket socket = new MulticastSocket(port);

// Since we are deploying
socket.setTimeToLive(0);
// this on localhost only (For a subnet set it as 1)

socket.joinGroup(group);

while (true) {
	String message;
	message = sc.nextLine();
	if (message.equalsIgnoreCase(GroupChat.TERMINATE)) {
					finished = true;
					socket.leaveGroup(group);
					socket.close();
					break;
				}
				message = name + ": " + message;
				byte[] buffer = message.getBytes();
				DatagramPacket datagram = new DatagramPacket(buffer, buffer.length, group, port);
				socket.send(datagram);
}

```

Qui il codice completo dell'applicazione `GroupChat`:

{% code title="" %}

```java
import java.net.*;
import java.io.*;
import java.util.*;

public class GroupChat {
	private static final String TERMINATE = "Exit";
	static String name;
	static volatile boolean finished = false;

	public static void main(String[] args) {
		if (args.length != 2)
			System.out.println("Two arguments required: <multicast-host> <port-number>");
		else {
			try {
				InetAddress group = InetAddress.getByName(args[0]);
				int port = Integer.parseInt(args[1]);
				Scanner sc = new Scanner(System.in);
				System.out.print("Enter your name: ");
				name = sc.nextLine();
				MulticastSocket socket = new MulticastSocket(port);

				// Since we are deploying
				socket.setTimeToLive(0);
				// this on localhost only (For a subnet set it as 1)

				socket.joinGroup(group);
				Thread t = new Thread(new ReadThread(socket, group, port));

				// Spawn a thread for reading messages
				t.start();

				// sent to the current group
				System.out.println("Start typing messages...\n");
				while (true) {
					String message;
					message = sc.nextLine();
					if (message.equalsIgnoreCase(GroupChat.TERMINATE)) {
						finished = true;
						socket.leaveGroup(group);
						socket.close();
						break;
					}
					message = name + ": " + message;
					byte[] buffer = message.getBytes();
					DatagramPacket datagram = new DatagramPacket(buffer, buffer.length, group, port);
					socket.send(datagram);
				}
			} catch (SocketException se) {
				System.out.println("Error creating socket");
				se.printStackTrace();
			} catch (IOException ie) {
				System.out.println("Error reading/writing from/to socket");
				ie.printStackTrace();
			}
		}
	}
}

class ReadThread implements Runnable {
	private MulticastSocket socket;
	private InetAddress group;
	private int port;
	private static final int MAX_LEN = 1000;

	ReadThread(MulticastSocket socket, InetAddress group, int port) {
		this.socket = socket;
		this.group = group;
		this.port = port;
	}

	@Override
	public void run() {
		while (!GroupChat.finished) {
			byte[] buffer = new byte[ReadThread.MAX_LEN];
			DatagramPacket datagram = new DatagramPacket(buffer, buffer.length, group, port);
			String message;
			try {
				socket.receive(datagram);
				message = new String(buffer, 0, datagram.getLength(), "UTF-8");
				if (!message.startsWith(GroupChat.name))
					System.out.println(message);
			} catch (IOException e) {
				System.out.println("Socket closed!");
			}
		}
	}
}

```

{% endcode %}

Esempio mandando in esecuzione il processo sull'indirizzo di multicast `230.0.0.1` alla porta `3000`:

```java
java GroupChat 230.0.0.1 3000
```

### CODICE ESEMPI

{% embed url="<https://github.com/checksound/EsempiNetworking>" %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://checksound.gitbook.io/corsojava/datagrams-e-multicasting.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
