Una applicazione distribuita in rete permette a delle applicazioni situate su diversi computer, ma entrambi connessi ad internet, di comunicare.
Ci sono due approcci per creare applicazioni distribuite in rete: il primo è quello di utilizzare gli standard del RFC, in tal modo anche se gli sviluppatori delle applicazioni create sono diversi, avendo eseguito i passi standard per la programmazione delle socket, i programmi sono comunque in grado di interagire. L'altro approccio è sviluppare l'app senza tenere conto delle regole del RFC, in questo caso il programmatore deve sviluppare il client e il server in modo che essi possano comunicare tra di loro. Altri sviluppatori indipendenti non posso fare interagire le loro app con quella, poiché essa non implementa un protocollo di pubblico dominio.
Per lo sviluppo di questa semplice app distribuita utilizzeremo python.
Faremo gli esempi con entrambi i protocolli del livello di trasporto, in questa fase utilizzeremo UDP.
Come abbiamo già detto, le applicazioni sono su sistemi diversi che comunicano attraverso le socket ed è come se fossero delle case, la cui porta rappresenta la socket. Dal lato interno della porta viene gestito il lato applicativo, dal lato esterno troviamo il protocollo di trasporto, che non possiamo programmare. L’app che invia il pacchetto UDP deve attaccare (attach) l’indirizzo cui è diretto il pacchetto al pacchetto stesso. In questo modo internet potrà instradare il pacchetto verso la destinazione. Sulla macchina che riceve il pacchetto possono essere in esecuzione più processi, bisogna indirizzare il pacchetto verso la socket del processo ricevente, per questo si specifica un numero di porta. Il lavoro che svolgerà la nostra app è il seguente:
udpclient.py
from socket import * # importate libreria per le socket
serverName = 'localhost'
serverPort = 12000
clientSocket = socket(AF_INET, SOCK_DGRAM)
# creazione del socket lato client, memorizzandone il riferimento in una variabile;
# AF_INET indica il tipo di dati trattati (indica la famiglia di indirizzi IPv4);
# SOCK_DGRAM indica che la socket è di tipo UDP;
message = input('Frase da convertire in maiuscolo:') # prende input da tastiera
clientSocket.sendto(message.encode(), (serverName, serverPort))
# invia il messaggio nella socket creata sopra;
# il messaggio viene prima convertito in byte (message.encode()) per poterla
# inviare nella socket;
# la funzione sendto attacca serverName e serverPort al messaggio converito in byte;
# viene attaccato anche l'IP del mittente, ma questo viene fatto in automatico.
modifiedMessage, serverAddress = clientSocket.recvfrom(2048)
# la funzione recvfrom(2048) chiamata sulla socket, fa in modo di ricevere i messaggi
# inviati da qualche server su quella socket (ascolta per dei pacchetti in arrivo);
# i valori di ritorno di questa funzione (dati contenuti nella socket e mittente)
# vengono piazzati nelle variabili modifiedMessage e serverAddress;
# 2048 è la grandezza del buffer.
print(modifiedMessage.decode())
# stampa il messaggio ricevuto (modificato dal 'server')
clientSocket.close()
from socket import * # importate libreria per le socket
serverPort = 12000
serverSocket = socket(AF_INET, SOCK_DGRAM)
serverSocket.bind(('', serverPort))
# associa alla socket il numero di porta serverPort;
# nel server il numero di porta viene esplicitamente collegato alla socket;
print('Ascolto nella socket per pacchetti in arrivato da altri client')
while 1:
message, clientAddress = serverSocket.recvfrom(2048)
# con recvfrom riceve i messaggi nel buffer di dimensione 2048
# e li piazza nelle variabili message e clientAddress
print('Ricevuto un pacchetto')
print(message)
printf('Lo converto in maiuscolo')
modifiedMessage = message.decode().upper()
# il messaggio viene decodificato, gli viene chiamata la funzione upper sopra
# per renderlo maiuscolo;
# il tutto viene salvato in modifiedMessage
serverSocket.sendto(modifiedMessage.encode(), clientAddress)
# il messaggio modificato viene convertito in byte e viene passato a sendto
# l'IP del client
TCP è un protocollo orientato alla connessione rispetto a UDP. Ciò vuol dire che prima di iniziare a scambiarsi i dati, client e server devono "presentarsi", attraverso un'operazione che viene detta three-way handshake (handshake = "stretta di mano"). La connessione TCP, dopo l'handshake, risulterà attaccata ai due estremi con i rispettivi host della comunicazione: client e server. Una connessione TCP è identificata dall'indirizzo del client (formato da IP e numero di porta) e l'indirizzo del server (IP e numero di porta).
A questo punto quando un lato vuole inviare dei dati all'altro deve solo mettere i dati nella connessione TCP attraverso la sua socket. In UDP il server doveva attaccare l'indirizzo del client a cui voleva inviare i dati.
from socket import * # importate libreria per le socket
serverName = 'localhost'
serverPort = 12000
clientSocket = socket(AF_INET, SOCK_STREAM)
# SOCK_STREAM indica che la socket è di tipo TCP
clientSocket.connect((serverName, serverPort))
# il client si connette con il server specificato (handshake)
message = input('Frase da convertire in maiuscolo:') # prende input da tastiera
clientSocket.send(message.encode())
# non è necessario specificare a chi, poiché il client è già connesso con il server
modifiedMessage = clientSocket.recv(1024)
# è noto da chi si riceve il messaggio (poiché la connessione è stata stabilita)
# quindi non server prelevare dal pacchetto l'indirizzo del server
print(modifiedMessage.decode())
# stampa il messaggio ricevuto (modificato dal 'server')
clientSocket.close()
from socket import * # importate libreria per le socket
serverPort = 12000
serverSocket = socket(AF_INET, SOCK_STREAM)
serverSocket.bind(('', serverPort))
serverSocket.listen(1) # la socket si mette in ascolto
print('Ascolto nella socket per pacchetti in arrivato da altri client')
while 1:
connectionSocket, addr = serverSocket.accept()
# la funzione accept() blocca il corpo del while su quella riga, fino a che
# non giunge un pacchetto al server (la funzione è bloccante);
# quando in client contatta il server viene creata una connessione
# connectionSocket dedicata all'host che l'ha contattato (handshake);
# l'handshake avviene su serverSocket
print('Ricevuto un pacchetto')
print(message)
printf('Lo converto in maiuscolo')
modifiedMessage = message.decode().upper()
serverSocket.send(modifiedMessage.encode())
# non serve specificare a chi viene inviato il pacchetto