Socket
Il protocollo TCP/IP
La comunicazione tra macchine diverse è strutturata secondo cinque strati:
- Application layer
- Transport layer
- Internet layer
- Network access layer
- Physical layer
Il protocollo IP (Internet Protocol) viene usato nel layer 3
per instradare i messaggi tra reti diverse. Esso è presente
anche nei router, cioè in quelle macchine che fungono da snodo
per la comunicazione tra le reti.
Il protocollo TCP (Transmission Control Protocol) viene usato
nel layer 2 per fornire una modalità di comunicazione che sia
affidabile e che garantisca in ricezione lo stesso ordine del
flusso di dati (
stream) imposto in
trasmissione. TCP deve essere presente nei due terminali
(host) della comunicazione.
Indirizzi
Ogni host ha un indirizzo internet univoco in senso globale,
usato da IP per instradare i messaggi. Esso è di 32 bit (in
IPv4, e di 128 bit in IPv6, diventato standard nel 1996) e
normalmente fornito in notazione "dotted-quad"
(e.g. 157.138.204.250) o come nome di host
(e.g. www.iuav.it). La traduzione da nomi a numeri puo'
avvenire mediante il file /etc/hosts o mediante il database
distribuito chiamato Domain Name Service (DNS).
Ogni applicazione deve avere un numero di port (di 16 bit)
univoco all'interno dell'host, usato da TCP per gestire la
comunicazione da e verso l'applicazione. I numeri fino a
IPPORT_RESERVED (tip. 1024) sono riservati per un accesso ai
server di sistema (HTTP, FTP, etc.).
Dati
Un blocco di dati inviato da una applicazione si arricchisce
di informazioni aggiuntive (header) mentre scende verso gli
strati piu' bassi dei protocolli di comunicazione.
Affidabilità della trasmissione
All'interno del TCP header, il sequence number tiene traccia
dell'ordine dei pacchetti inviati (non piu' grandi di 64KB),
in modo che in ricezione si possano individuare i pacchetti
persi e ricostruire l'ordine corretto. Mediante il checksum il
ricevente è in grado di rilevare errori di trasmissione. Il
ricevente notifica la corretta ricezione mediante messaggi di
risposta. Essendo questo protocollo basato sulla connessione
di due nodi, tale connessione (virtuale) deve essere stabilita
prima di iniziare la comunicazione.
Il protocollo UDP (User Datagram Protocol) consente la
comunicazione priva dell'overhead dovuto ai controlli di
integrità e consistenza dei messaggi. Essenzialmente, UDP
aggiunge ad IP soltanto la capacita' di indirizzare dei
port. Nell'UDP header c'è anche un campo checksum, ma non è
previsto l'acknowledgement di ricezione corretta. Il
protocollo UDP non ha bisogno di una connessione stabilita
prima della comunicazione. Il protocollo UDP, oltre ad avere
minore overhead rispetto a TCP, è anche essenziale al
supporto di certe attività. Si pensi ad esempio al programma
ping, il quale verifica la qualità di una connessione. Esso
può funzionare solo se ha la possibilita' di verificare la
perdita eventuale di pacchetti.
Il modello client-server e i socket Un processo
server attende le eventuali connessioni di processi
client. Quando la connessione viene stabilita, il server
esegue dei compiti in base a quanto ricevuto dal client e poi,
di solito, la connessione viene interrotta. La comunicazione
tra client e server deve essere affidabile.
La programmazione della comunicazione mediante TCP/IP viene di
solito effettuata mediante l'interfaccia socket BSD,
introdotta da UNIX 4.2BSD. E', di fatto, una forma di
Inter-Process Communication che si aggiunge alle altre forme
di comunicazione tra processi di UNIX (pipe, shared memory,
signals, message queues, semaphores) con la peculiarità di
consentire la comunicazione tra macchine diverse fornite di
indirizzo IP.
Definition 1:
Socket è uno dei due terminali di una
connessione bidirezionale tra due processi in esecuzione su
macchine collegate ad una rete.
Java fornisce una suite di classi che consentono di stabilire
delle connessioni mediante socket in modo indipendente dalla
particolare realizzazione dei socket da parte del sistema
operativo sottostante. In Java, le classi Socket e
ServerSocket vengono usate per stabilire la connessione
rispettivamente dal lato client e dal lato server.
La comunicazione client-server mediante socket avviene come segue:
| CLIENT |
- |
SERVER |
| conosce hostname e port number del server |
- |
ascolta il socket |
| richiede la connessione al server |
→ |
accetta la connessione |
| crea un socket e lo usa per comunicare con il server |
← |
ottiene un nuovo socket su un port differente |
| |
- |
continua ad ascoltare il socket originario |
Il modello message passing
Il protocollo di trasmissione UDP realizza di fatto un modello
di comunicazione di tipo message passing con ricezione
bloccante e invio non bloccante. Quindi i due partner
comunicano con una forma di rendez vous "lasco". E' demandato
al programmatore il compito di assicurare la corretta
ricezione dei messaggi (
datagram) se essa
è importante per l'applicazione.
Definition 2:
Datagram è un messaggio la cui
trasmissione in rete non assicura l'effettiva ricezione, il
tempo di arrivo, e l'integrita' del contenuto.
La comunicazione in UDP avviene mediante indirizzamento
indiretto di tipo molti a uno: la primitiva send indirizza il
proprio messaggio ad uno specifico identificatore del port
(che gioca il ruolo di una mailbox) di accesso al processo
destinazione (hostname e port). Il processo destinazione
conosce l'identità del processo sorgente mediante hostname e
port specificati in testa al messaggio.
Mediante UDP, è anche possibile che molti client si mettano in
ascolto di messaggi inviati (broadcast) da un processo
server. Questa è una comunicazione di tipo message passing uno
a molti.
In Java, le classi DatagramSocket e DatagramPacket consentono
di confezionare dei messaggi e di spedirli e riceverli
attraverso socket di tipo UDP. La classe MulticastSocket
consente ad un client di associarsi al gruppo di client che
possono ricevere il broadcast e di ricevere in modo passivo i
messaggi inviati dal server.
Comunicazione client-server in Processing Tra le
core libraries di Processing, la
Network
consente di creare client e server.
Esempio 1: Controller come client
In questo esempio la posizione del mouse sulla finestra
determina valori di ampiezza e frequenza che controllano
i parametri di un oscillatore server. Più correttamente,
si assume che ci sia un oscillatore con due server già
attivi: uno in ascolto sulla porta
5214
(frequenza) e uno in ascolto sulla porta
5215 (ampiezza). Il numero IP
127.0.0.1 indica localhost, o la medesima
macchina su cui sta girando il codice Processing.
import processing.net.*;
int portf = 5214;
int porta = 5215;
Client frequency, amplitude;
int val = 0;
void setup() {
size(200, 200);
frequency = new Client(this, "127.0.0.1", portf);
amplitude = new Client(this, "127.0.0.1", porta);
}
void draw() {
background(val);
frequency.write(str(mouseX));
frequency.write(';');
amplitude.write(str(height - mouseY));
amplitude.write(';');
}
Comunicazione client-server in Pure Data Pure
Data mette a disposizione gli oggetti netsend e
netreceive per realizzare la comunicazione di
tipo client-server.
L'oggetto netreceive apre un socket di ricezione
TCP (stream) o UDP (datagram) su un port specificato come
argomento. Quando si usa TCP, l'outlet di destra restituisce
il numero di client che hanno aperto su questo socket la
connessione. La comunicazione via UDP si sceglie aggiungendo
un secondo argomento 1 a netsend e
netreceive.
Dispositivi connessi alla rete E' frequente
oggigiorno l'utilizzazione di protocolli di rete per l'invio
di dati raccolti con sensori. Esempi di dispositivi che fanno
questo sono le macchine Kroonde e Toaster di
La Kitchen,
azienda francese che ha purtroppo chiuso le proprie attività
di recente. Queste macchine si appoggiano a UDP per inviare i
dati raccolti dai sensori.
I
patch per Pure Data, basati su
netreceive,
sono a disposizione.
Esempio 2: Oscillatore come server
Un oscillatore che risponde a richieste quali quelle
riportate in
Esempio 1 è rappresentato in
Figura 1. Si noti come in
Esempio 1 il valore numerico rappresentante la
posizione del mouse viene convertito in stringa nel momento
in cui esso viene inviato. Inoltre, viene inserito il
separatore
';' per consentire
l'estrazione dei numeri da parte di Pure Data.
Problem 1
Si modifichino i client e server di
Esempio 1 e
Esempio 2 in maniera da
realizzare una comunicazione bidirezionale. Per esempio,
fare in modo che le consolle di Processing e Pure Data
scrivano entrambe i valori scambiati di frequenza.
[
Click for Solution 1 ]
Solution 1
import processing.net.*;
int portf = 5214;
int porta = 5215;
int ports = 5204;
Client frequency, amplitude;
Server myServer;
int val = 0;
void setup() {
size(200, 200);
frequency = new Client(this, "127.0.0.1", portf);
amplitude = new Client(this, "127.0.0.1", porta);
myServer = new Server(this, ports);
}
void draw() {
background(val);
frequency.write(str(mouseX));
frequency.write(';');
amplitude.write(str(height - mouseY));
amplitude.write(';');
Client thisClient = myServer.available();
if (thisClient !=null) {
String whatClientSaid = thisClient.readString();
if (whatClientSaid != null) {
print(whatClientSaid);
}
}
}
[
Hide Solution 1 ]
MIDI
Il protocollo
MIDI (Musical
Instrument Digital Interface; IPA) è un protocollo di
comunicazione tra dispositivi elettronici, introdotto dalle
industrie degli strumenti musicali all'inizio degli anni '80
per garantire l'interoperabilità dei propri apparati. Il
fatto che esso non si occupa di trasmettere segnali ma
messaggi relativi ad eventi ha fatto sì che la sua diffusione
sia stata ampia anche in ambito non strettamente musicale.
Lo standard MIDI comprende sia il protocollo di comunicazione
sia la definizione dell'interfaccia fisica. Questa trasmette
alla velocità di 31,250 bit al secondo dei pacchetti
costituiti da un bit di start (0), otto bit (un byte) di dati,
un bit di stop (1). I messaggi scambiati sono relativi a
eventi discreti (
note-on,
note-off, etc.) o processi che inviano
flussi di valori (
pitch bend,
aftertouch,
control
change, etc.). Tutti questi messaggi appartengono a
uno (o tutti) di 16 canali. Un byte di
status, contenente canale e codice
relativo al tipo di messaggio, precede uno o due byte di
dati. Per le interazioni continue che ci si trova a dover
trattare nell'interaction design, sono particolarmente utili i
control
change.
Il MIDI ha avuto un notevole successo come standard
industriale e, per molte applicazioni, funziona egregiamente
ed ha un punto di forza nella sua semplicità. Tuttavia, ha gli
svantaggi di una comunicazione seriale e a bassa velocità,
nonché le limitazioni legate alla sua bassa espressività e
all'origine nell'ambito degli strumenti musicali a tastiera.
In Pure Data, è disponibile una gamma di oggetti per la
gestione dei messaggi MIDI. La pagina di documentazione di
riferimento è riportata in
Figura 3. Si vede,
ad esempio, che l'oggetto
ctlin ha tre outlet che
forniscono, rispettivamente, il valore, il numero, e il canale
di un control change.
Per Processing e per Java esiste
proMIDI, una
sofisticata libreria che include funzioni di
sequencer.
Il MIDI può essere usato anche per far dialogare tra loro
applicazioni che girano in uno stesso computer o su computer
diversi all'interno di una rete. Il sistema operativo del
Macintosh, ad esempio, ha una applicazione chiamata
Audio MIDI Setup che consente di impostare uno
Inter-Application Communication -
IAC Driver o un
MIDI
Network driver. Se abilitati, questi device
driver saranno poi selezionabili come input e output device
dalle preferences di Pure Data.
OSC
OpenSound Control -
OSC
è un protocollo di comunicazione che consente a diversi
dispositivi elettronici e applicazioni di scambiarsi dati in
tempo reale su un supporto di rete. E' stato sviluppato per
superare le limitazioni di MIDI e per sfruttare le possibilità
di TCP e UDP, consentendo al tempo stesso una semantica
raffinata dei messaggi.
Le caratteristiche principali di OSC sono:
- indirizzamento simbolico "tipo-URL" (e.g.,
/voices/synth1/osc1/modfreq) - indipendenza dal tipo di trasporto (tipicamente UDP socket)
- argomenti numerici e simbolici ai messaggi
- espressioni regolari per la specificazione
di più destinatari (e.g.,
/voices/synth1/osc[1-4]/modfreq) - temporizzazione fine
- accorpamento di messaggi in bundle che richiedono azione simultanea.
- possibilità di interrogare un OSC server sulle sue capacità
L'unità fondamentale di OSC è il messaggio, il quale consiste
di un pattern di indirizzo, una stringa di tipo
(
type tag), e un certo numero di
argomenti. Ad esempio, un messaggio può essere indirizzato a
/voice/3/freq, specificare come type tag un
singolo numero floating point, e il suo argomento può essere
261.62558. L'indirizzamento aperto di OSC consente a ogni
server di definire il proprio spazio di indirizzamento in
relazione alla propria organizzazione di servizi.
Un'ottima
introduzione
a OSC si può scaricare, insieme a dettagliata
documentazione e a link alle realizzazioni software presso il
sito
ufficiale di OSC.
Per Processing esiste la libreria
oscP5
che consente di produrre ed interpretare messaggi e bundle di
OSC.
Problem 2
Si installi oscP5 per Processing e si provino gli esempi
della
documentazione. In
particolare, si cerchi di comprendere la composizione e
decomposizione dei messaggi. Nell'esempio
oscP5plug è illustrato un meccanismo di
event-based
programming, secondo il quale è possibile
instradare messaggi con un determinato pattern di
indirizzo a un metodo specifico di una classe
(
event handler).