Servizi TCP/IP
In TCP/IP, ogni servizio è associato a un ID, che si chiama porta.
- Per accedere a un servizio, dobbiamo specificare IP e porta su cui il servizio è in attesa di richieste.
- Per interagire con il fornitore di servizio dobbiamo rispettare uno specifico protocollo di comunicazione.
Ad ogni tipologia di servizio è associato un protocollo di comunicazione unico.
Livello di trasporto in TCP/IP
Al di sopra di IP, vi sono diversi protocolli di livello trasporto, ma i principali sono TCP e UDP:
-
TCP: permette di stabilire dei flussi di comunicazione bidirezionali affidabili tra due nodi. Offre un servizio con connessione e affidabile (viene preservato l'ordine, ritrasmissione, vengono compattati i messaggi, controllo di congestione, ecc.). Si avverte della corretta ricezione mediante un ACK ed implementa la consegna affidabile (es: GOBACK N, SELECTIVE REPEAT). Molto oneroso.
- Implementa il controllo di flusso: serve per permettere a un ricevitore lento di rallentare l'invio dei messaggi da parte del mittente, in modo da non saturare il server.
- Implementa il controllo di congestione: cerca di impedire un numero maggiore di pacchetti rispetto a quanto (una parte) dell'infrastruttura di rete riesca a gestire, rallentando la velocità di trasmissione.
- UDP: permette a un nodo di mandare messaggi a un altro nodo. Offre un servizio senza connessione e non affidabile. Non preserva l'ordinamento dei messaggi, non garantisce la consegna, non controlla il flusso e la congestione.
La scelta del protocollo di trasporto da usare dipende dal tipo di applicazione che devo realizzare. Esistono altri protocolli di trasporto come SCTP, DCCP, e QUIC. I principali però rimangono TCP e UDP.
TCP/IP: il livello rete
In TCP/IP il livello rete è implementato dallo Internet Protocol (IP). Esistono due versioni:
-
IPv4: attuale. Un indirizzo IPv4 è composto da 32 bit e suddiviso in una parte che identifica la Local Area Network (LAN) in cui esso si trova (network ID) e in una parte di host (host ID).
- Il numero massimo teorico possibile di indirizzi IPv4 è di circa 4 miliardi (non tutti sono utilizzabili per via delle reti private). Numero molto limitato, praticamente finito. Per ovviare a questo problema: migrazione verso IPv6 o NAT.
-
IPv6: la futura, in fase di adozione. Adotta indirizzi di 128 bit (3,8 × 1038 indirizzi). Ha una rappresentazione esadecimale.
- Presenta vantaggi rispetto a IPv4 come il ripensamento di protocolli come ARP per protocolli più robusti. Le applicazioni di rete moderne devono essere esplicitamente sviluppate per funzionare in tutte e tre le situazioni (IPv4, IPv6 ed entrambi).
Routing in una LAN
All'interno di una LAN i dispositivi IP possono comunicare direttamente tra loro usando le funzioni offerte dal protocollo di livello 2 → Uso di Address Resolution Protocol (ARP) per scoprire quale indirizzo di livello 2 (link) corrisponda a un dato indirizzo di livello 3 (IP).
Routing al di fuori di una LAN
Per comunicare con nodi al di fuori di una LAN si usa il meccanismo di IP routing. I router si occupano di questo mediante tabelle di routing per l'instradamento di pacchetti.
NAT
NAT è una soluzione che trasforma automaticamente indirizzi privati in indirizzi globali.
- Tramite il NAT i pacchetti IP in uscita vengono modificati sostituendo all'indirizzo privato mittente X un indirizzo pubblico Y.
- I pacchetti IP in ingresso all'indirizzo Y pubblico sarà sostituito l'indirizzo privato X.
- Permette di connettere a Internet un gran numero di indirizzi privati usando un ridotto numero di indirizzi pubblici.
Svantaggi:
- Problemi nel caso si voglia rendere accessibili all'esterno servizi che girano nella rete con indirizzi privati.
- Problemi con alcuni tipi di servizi come FTP.
TCP/IP e mobilità
Adozione di diversi meccanismi per fornire il supporto alla mobilità dei nodi:
- DHCP: protocollo di autoconfigurazione. Assegna dinamicamente un indirizzo IP a un nodo. DHCP configura anche gateway e DNS.
- Mobile IP.
Naming
Necessità di identificare (e ritrovare) le altre entità (e risorse) nel sistema.
I nomi sono organizzati in livelli:
- Nome logico, specifica a quale oggetto si riferisce. Sono scelti dagli utenti.
- Indirizzo fisico, specifica dove l'oggetto risiede. Sono assegnati dal sistema.
- Route, specifica come raggiungere l'oggetto. Sono gestiti dal protocollo di rete IP.
Il passaggio nomi → indirizzi → route avviene attraverso funzioni di corrispondenza (Mapping). Risoluzione del nome: mapping dal nome all'indirizzo consultando la tabella di mapping.
Binding tra nomi e risorse
Binding: associazione di un nome con un oggetto e il set di relativi attributi (es. l'indirizzo fisico). Il binding è il legame tra un nome e la specifica risorsa a esso associata che il client andrà a utilizzare.
I sistemi di nomi sono l'elemento fondamentale nella gestione del binding tra nomi e risorse in una rete di calcolatori. Sono possibili due diverse strategie:
- Binding statico: il binding viene eseguito una sola volta e rimane inalterato per tutto il tempo di vita dell'applicazione.
- Binding dinamico: il binding ha durata limitata e viene rieseguito periodicamente. Ottimizza l'efficienza ma ha un costo maggiore.
Internet necessita di un servizio di nomi. Una prima soluzione fu un database centralizzato, inefficiente e non scalabile per via di:
- Single point of failure;
- Prestazioni dovute alle congestioni in un unico nodo;
- Distanza, quindi alta latenza per i client molto distanti dal server;
- Manutenzione molto frequente e molto costosa.
Domain Name System (DNS)
Servizio di nomi decentralizzato e basato sul concetto di domini amministrativamente indipendenti.
Nomi logici gerarchici
Gerarchia di domini logici:
- 13 root server di cui IP noto: sono i nodi radice. Conoscono i server che gestiscono i sottodomini.
- Top Level Domains (.com, .it, ecc..).
Ogni livello ha info sul livello inferiore. Concetto di delega. I singoli nomi sono case insensitive e al max 63 char, il nome completo al max 255 char. Ogni dominio indicato in modo relativo o assoluto. Ogni dominio deve fare riferimento al dominio che lo contiene. Es. ing è interno a unife, che è interno a it, che è interno a root.
Risoluzione di un nome
Risoluzione di un nome (conversione tra nome logico e indirizzo fisico) avviene tramite un servizio di nomi che risponde (dinamicamente) alle richieste. A livello di API si passa il riferimento da mappare a un resolver che:
- O conosce già la corrispondenza (cache) es: già visitato.
- Oppure la trova attraverso una richiesta a un name server (server di nomi).
DNS Resolver
I client effettuano richieste a un componente DNS Resolver, installato nella propria rete locale o fornito dal proprio ISP, che a sua volta effettua query ricorsive, per tradurre nome ad indirizzo, e salva i risultati nella propria cache, per riutilizzarli più tardi.
Il DNS salva le informazioni in record. Ad ogni nome possono essere associati molti record, sia di tipo diverso (es. sia indirizzo IPv4 che IPv6), che dello stesso tipo (es. per ridondanza). I diversi tipi di record possono essere:
- A per risoluzione da nome host a indirizzo IPv4.
- AAAA per risoluzione da nome host a indirizzo IPv6.
- MX per risoluzione da nome dominio a nome dei server di posta elettronica.
- PRT per risoluzione inversa, ovverosia da indirizzo IP a nome (meno usata per motivi di sicurezza).
DNS – Protocollo di trasporto
DNS usa un semplice protocollo richiesta-risposta basato su UDP, poiché non è necessaria affidabilità, congestion control ed è importante minimizzare la latenza nella risoluzione dei nomi. Questo porta con sé anche problemi di sicurezza come il fatto che il traffico passa tutto in chiaro (ripensamento del protocollo con DNSSEC, DNS-over-HTTPS ma con problemi di velocità).
URL: Uniform Resource Locator
È un indirizzo composto da:
Spazio dei nomi
Nome è una costruzione linguistica che distingue (denota) un oggetto in una collezione di oggetti. Uno spazio dei nomi (name space) è la collezione di tutti i nomi (sintatticamente) validi per un particolare servizio.
Lo spazio dei nomi può essere:
- Piatto (flat): nessuna struttura (come i processi in UNIX).
- Partizionato: gerarchia e contesti (DNS). Es: www.ing.unife.it (ing viene risolto nel contesto unife, ecc.).
- Descrittivo: con riferimento a una struttura di oggetto oggetti hanno attributi e classi.
Servizio di nomi
Un servizio di nomi memorizza i binding tra i nomi e gli attributi degli oggetti. Il principale scopo di un servizio di nomi è di risolverli, cioè fornire ai clienti gli attributi associati a un nome. I client del name server sono:
- Sia i clienti che hanno esigenza di risolvere un nome per potere riferire una risorsa.
- Sia le entità risorse (server rispetto ai clienti di prima, ossia che devono essere riferiti) che devono registrare il servizio e diventano clienti del name server.
Nel server si preferisce ottimizzare la lettura, siccome è più frequente, rispetto alla scrittura.
Caching
Per velocizzare le richieste di risoluzione (successive), i sistemi di nomi fanno spesso uso di meccanismi di caching. Il DNS prevede un caching distribuito a più livelli.
Tempo di validità delle associazioni e caching
Le associazioni tra nome e attributo possono cambiare, senza preavviso. Soluzioni specifiche per evitare che i client usino informazioni invalide:
- Tempo di validità (time to live TTL) scaduto il quale vanno eliminati dalla cache.
- Tempo di propagazione DNS, tempo necessario per il DNS dominio per aggiornare le cache sulla rete.
Servizi di directory e discovery
Un servizio di nomi recupera gli attributi associati a un nome. Un servizio di directory/discovery recupera le informazioni disponibili a partire da qualunque attributo (anche i nomi possono essere visti come attributi). Un servizio di discovery dà accesso alle stesse informazioni di un directory service ma è specifico per un ambiente locale dove i servizi e gli host possono cambiare molto dinamicamente. Un servizio di discovery mantiene le associazioni <nome-attributi> come un servizio di directory o di nomi, però memorizza gli attributi delle risorse locali perché serve per facilitare l'accesso a tali risorse senza conoscenza prestabilita.
Il problema dell'eterogeneità
Nelle reti di calcolatori vi è un'estrema eterogeneità sia HW che SW. Necessità di definire una rappresentazione comune dei dati e di implementare meccanismi per gestirla (es: Little e Big Endian). Anche la ricompilazione dello stesso codice sorgente su piattaforme diverse (portabilità) può rappresentare un grande problema.
Rappresentazione dei dati
Per comunicare tra nodi eterogenei sono possibili due tipi di soluzioni:
- Ogni nodo converte i dati nel formato specifico del destinatario (prestazioni). Impossibile.
- Si concorda un formato comune di rappresentazione dei dati che i nodi useranno per comunicare tra loro (flessibilità).
External Data Representation (XDR)
- XDR fornisce un insieme di procedure di conversione per trasformare la rappresentazione nativa dei dati in una rappresentazione esterna (XDR) e viceversa;
- Fa uso di uno stream (contenuto in un buffer) che permette di creare un messaggio con i dati in forma XDR;
- I dati vengono inseriti/estratti nello/dallo stream XDR uno alla volta, tramite operazioni di serializzazione e/o deserializzazione;
- Permette anche di gestire tipi di dati complessi, ed il loro formato è definito dal linguaggio IDL il cui compilatore genera automaticamente procedure di codifica e decodifica dei dati complessi.
Altre soluzioni: CDR, Google Protocol Buffer, Avro etc.
Protocolli testuali
L'adozione di protocolli testuali si è spesso rivelata vincente poiché:
- Facilità di testing e debugging;
- Estendibilità;
- Resilienza alla complessità.
1) US-ASCII: usato per molti decenni: definisce un set di caratteri e una loro rappresentazione in formato binario a 8-bit (127 caratteri); i 7 bit meno significativi sono effettivamente utilizzati nello standard, il bit più significativo è settato sempre a 0 per sincronizzazione.
2) ISO 8859: US-ASCII rappresentava solo simboli in lingua inglese. Lo standard ISO 8859 estende US-ASCII utilizzando anche l'ottavo bit (255 caratteri). Ma pian piano si è avuta la necessità di implementare nuovi alfabeti (come il cinese, arabo, ecc.) e modi di scrittura right to left.
3) ISO 10646: definisce lo Universal Character Set (UCS), un set di caratteri che contiene tutti i caratteri universalmente noti:
- Ciascun carattere ha un codice a esso associato, e viene rappresentato con una notazione esadecimale come U+12345678;
- Supporto a caratteri composti.
4) UNICODE: si è allineato con l'ISO 10646 nel '91 e definisce degli standard (UTF-*/UCS-*) per l'encoding dei caratteri dello UCS.
5) UTF-32/UCS-4: è l'encoding più semplice: ogni carattere viene rappresentato con 4 byte (molto onerosa poiché include caratteri talmente rari che potrebbero essere esclusi); codifica non compatibile con US-ASCII; è raramente adottato, praticamente utilizzato solo all'interno delle librerie di gestione del testo.
6) UTF-16/UCS-2: è una codifica a lunghezza variabile: 16 bit per i caratteri del Basic Multilingual Plane e 32 bit per gli altri; rappresentazione piuttosto compatta; encoding standard di Java e Windows; non compatibile con US-ASCII.
7) UTF-8: UTF-8 è una codifica a lunghezza variabile, che supporta tutto il set di caratteri UCS:
- Associa a ciascun carattere una sequenza di byte di lunghezza variabile (da 1 a 4);
- Encoding standard di XML, di JSON, e della maggior parte dei sistemi Unix moderni.
Retrocompatibilità: offre un certo livello di compatibilità con il passato: i caratteri da U+0000 a U+007F sono identici ai caratteri della tabella US-ASCII (usa un solo byte); UTF-8 permette stringhe null-terminated; è del tutto compatibile con l'encoding US-ASCII.
Encoding
- Velocità di decodifica: il primo byte mi dice quanti altri byte devo leggere per ottenere un carattere completo;
- Auto-sincronizzazione: riesco a capire facilmente e velocemente dove inizia un carattere guardando in un intorno di 3 byte dalla posizione corrente.
Validazione:
- Poiché in UTF-8 i caratteri hanno dimensione variabile, si deve fare particolare attenzione a verificare che un buffer di memoria non contenga dei caratteri incompleti prima di utilizzarlo;
- Oltre a verificare che un buffer non contenga caratteri incompleti bisogna verificare che i dati rappresentati siano effettivamente validi.
8) wchar_t, char16_t e char32_t: Storicamente, C si basava su caratteri wide (tipo di dato wchar_t) per l'encoding di caratteri multibyte. Sfortunatamente, la dimensione di wchar_t non è standardizzata. C11 introduce i tipi char16_t e char32_t ma non le funzioni per gestirli, sostanzialmente non ci si può fare nulla.
Manipolazione stringhe UNICODE
Poiché UCS ammette caratteri composti, non è detto che la lunghezza di una stringa equivalga al numero effettivo di caratteri che essa stamperà a video. Dunque meglio usare librerie specifiche per la gestione del testo UTF-8.
UNICODE e applicazioni distribuite
È bene quindi realizzare applicazioni distribuite che comunichino con l'esterno utilizzando UTF-8. Nel caso in cui il formato di rappresentazione non sia UTF-8 (ad esempio su Windows), dobbiamo prevedere una transcodifica da UTF-8 alla codifica locale e viceversa.
Per la transcodifica tra UTF-8 e codifica locale:
- In Java funzioni di transcodifica fornite da InputStreamReader e OutputStreamWriter;
- In Unix/C uso di librerie come libunistring.
N.B.: la sanitizzazione è una pessima idea.
Dati strutturati
È possibile scambiare dati di tipo strutturato anche al di sopra di protocolli di tipo testuale, utilizzando standard come XML e JSON.
- XML: è un metalinguaggio in ottica machine oriented per facilitare la generazione automatica del codice. XML è usato sia per la rappresentazione di dati e messaggi scambiati che per la definizione del loro formato.
- XML SCHEMA: è un linguaggio derivato da XML che consente di definire tipi di documento XML contenenti tipi di dati con strutture complesse o non regolari.
- JSON: è un formato di rappresentazione dati particolarmente leggero e molto utilizzato sul Web (molto compatto rispetto XML). Pensato per app Web e JavaScript.
Socket
La programmazione client/server e le socket
Un client e un server su macchine diverse possono comunicare mediante socket. Una socket rappresenta un terminale (end point) di un canale di comunicazione bidirezionale.
Ci sono due diversi tipi di modalità socket:
- Con connessione e affidabile, in cui viene stabilita una connessione senza perdita di messaggi tra client e server. Uso di socket STREAM.
- Senza connessione e non affidabile, in cui non c'è connessione e i messaggi vengono recapitati uno indipendentemente dall'altro senza garanzia di consegna. Uso di socket DATAGRAM.
Identificazione dei processi comunicanti (sistema di nomi)
Per l'identificazione dei processi (il client e il server) nella rete, uso di nomi globali univoci e sempre non ambigui: Indirizzo IP della macchina + Porta come identif... (continua nel documento originale).
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.
Scarica il documento per vederlo tutto.