Le primitive per la gestione della connessione

In questa lezione del corso di TPSIT 5, esploreremo le primitive fondamentali per la gestione della connessione in ambito networking. Analizzeremo come le applicazioni interagiscono con i protocolli di trasporto per instaurare e mantenere un canale di comunicazione affidabile.

Studieremo il ruolo dei socket e l'utilizzo delle chiamate di sistema necessarie per la gestione del ciclo di vita di una connessione in architetture client-server moderne.

  • shape
  • shape


Le primitive per la gestione della connessione

Nella lezione precedente abbiamo descritto che cosa accade quando una comunicazione viene aperta, usata e chiusa. Quella descrizione, pero, era fatta dal punto di vista di chi osserva: restava da chiarire che cosa scrive davvero un programmatore per ottenere quel comportamento. La risposta e che scrive una sequenza di chiamate a poche operazioni elementari, chiamate primitive, messe a disposizione dal sistema operativo.

Sono poche, si imparano una volta sola e si ritrovano ovunque: cambiano i nomi esatti da un linguaggio all'altro, ma i concetti e l'ordine restano gli stessi, perche derivano tutti dalla stessa interfaccia standard nata decenni fa e mai abbandonata.

Che cosa sono le primitive

Una primitiva e un'operazione elementare che il programma chiede al sistema operativo di compiere sul socket. Non e una funzione di un particolare linguaggio di programmazione: e un servizio del sistema, che ogni linguaggio espone con la propria sintassi e con nomi a volte diversi.

Questo spiega una cosa che agli studenti risulta spesso sorprendente: chi ha capito la sequenza in un linguaggio la ritrova quasi identica in un altro. Cio che va imparato, quindi, non e un elenco di nomi, ma quale operazione serve in quale momento e chi la compie, se il client o il server.

Le primitive del server

Il server compie quattro passi, in quest'ordine. Prima crea il socket, cioe chiede al sistema operativo un nuovo capo di comunicazione. Poi lo associa a un indirizzo e soprattutto alla porta del proprio servizio: e il passo che rende quel socket raggiungibile a un recapito noto. Quindi lo mette in ascolto, dichiarando di essere disponibile a ricevere tentativi di contatto. Infine accetta le richieste che arrivano.

L'operazione di accettazione merita attenzione, perche e quella in cui si concretizza il meccanismo visto nella lezione precedente: non restituisce un segnale di conferma, ma un nuovo socket, dedicato alla comunicazione con quel singolo client. Il socket originario resta in ascolto e verra usato ancora, per il client successivo. Di norma l'accettazione e inoltre un'operazione bloccante: il programma resta fermo su quella riga finche qualcuno non si presenta, ed e esattamente il comportamento passivo del server descritto nelle lezioni precedenti.

Il server didattico della scuola, all'avvio, compie la sequenza una volta sola: crea il socket, lo associa alla porta del servizio, si mette in ascolto. Poi entra in un ciclo che ripete sempre la stessa operazione, cioe accettare: a ogni studente che si collega corrisponde un nuovo socket dedicato, mentre quello in ascolto torna subito ad aspettare il prossimo.

Le primitive del client

Il client ha un compito piu breve: crea il proprio socket e poi chiede la connessione verso l'indirizzo e la porta del server. Due passi soli, contro i quattro del server.

Manca, in particolare, il passo di associazione alla porta: come sappiamo, la porta del client non deve essere nota a nessuno e il sistema operativo gliene assegna una libera al momento, senza che il programmatore debba occuparsene. La richiesta di connessione, come l'accettazione dall'altro lato, e di norma bloccante: il programma attende l'esito, che puo essere il successo oppure un errore, per esempio quando su quella porta non c'e nessuno in ascolto.

Regola fondamentale: le primitive sono poche e sempre le stesse. Cambia chi le chiama e in quale ordine: il server crea, associa, ascolta e accetta; il client crea e si connette. Da li in poi i due ruoli usano le stesse identiche operazioni di invio, ricezione e chiusura.

Invio, ricezione e chiusura

Stabilita la comunicazione, entrambi i capi dispongono delle stesse tre operazioni. L'invio consegna al socket una sequenza di byte da trasmettere. La ricezione legge i byte arrivati; anch'essa e tipicamente bloccante, cioe attende finche qualcosa arriva. La chiusura termina la comunicazione e libera le risorse, con l'importanza gia discussa nella lezione precedente.

Sulla ricezione va chiarito un punto delicato e spesso frainteso: un'operazione di ricezione restituisce i byte che sono arrivati fino a quel momento, che possono essere meno di quelli inviati con una singola operazione di invio dall'altro capo. Non esiste cioe una corrispondenza uno a uno fra un invio e una ricezione. Stabilire dove finisce un messaggio e compito del protocollo applicativo, non del socket: e uno dei motivi per cui i protocolli prevedono terminatori, lunghezze dichiarate o formati ben definiti.

Un client invia in una sola operazione un messaggio piuttosto lungo. Dall'altra parte la prima ricezione puo restituirne solo la prima parte: il programma che ha ricevuto deve continuare a leggere, e capire dalle regole del protocollo quando il messaggio e completo. Un programma scritto assumendo che una ricezione basti sempre funziona con i messaggi corti e sbaglia con quelli lunghi.

L'ordine conta

Le primitive non sono intercambiabili e non tollerano scorciatoie: ciascuna presuppone che le precedenti siano gia state eseguite. Accettare senza essersi messi in ascolto non ha senso, perche non c'e nessun socket pronto a raccogliere richieste. Inviare dati prima che la connessione sia stabilita non e possibile, perche manca il destinatario. Associare due servizi diversi alla stessa porta della stessa macchina genera un errore, perche quel recapito e gia occupato.

Per questo la sequenza va imparata come tale, e non come un insieme di operazioni sparse: e la sequenza a descrivere il ciclo di vita della comunicazione, e ogni deviazione corrisponde a un errore che il sistema operativo segnala.

Errori comuni e limiti

Il primo errore e quello appena visto sulla ricezione: dare per scontata la corrispondenza fra un invio e una ricezione. E un'idea intuitiva ma sbagliata, e produce programmi che sembrano corretti nelle prove semplici.

Il secondo riguarda il comportamento bloccante. Molte primitive fermano il programma finche non succede qualcosa: un server che accetta e serve i client uno alla volta, restando bloccato su ciascuno, fa aspettare tutti gli altri in coda. E il motivo per cui esistono tecniche per servire piu client insieme, che saranno oggetto delle attivita di laboratorio.

Il terzo e considerare i nomi delle primitive come la sostanza della cosa. Passando da un linguaggio all'altro i nomi cambiano, e a volte un'unica istruzione ne riunisce due; cio che non cambia e la successione logica dei passi, che resta il vero contenuto da padroneggiare.

Un quarto, comunissimo errore, riguarda la primitiva di accettazione. Sapere che l'accept restituisce un nuovo socket porta spesso a credere che il server apra una nuova porta per comunicare con quel client. Non e cosi: il nuovo socket rimane agganciato alla stessa porta del servizio (ad esempio la 443 per HTTPS). Il sistema operativo non fa confusione perche identifica ogni connessione tramite una "quadrupla" (IP e porta del client, IP e porta del server): poiche IP e porta del client cambiano sempre, i pacchetti in arrivo sulla stessa porta del server vengono indirizzati senza errori al socket corretto.

Sintesi applicativa

Riassumendo: il server crea, associa, ascolta e accetta, ottenendo per ogni client un socket dedicato; il client crea e si connette; entrambi poi inviano, ricevono e chiudono. Conoscere questa sequenza permette di leggere il codice di rete di qualunque programma e di capire, di fronte a un malfunzionamento, in quale punto della sequenza cercare. Con questa lezione si conclude la parte dedicata ai socket: nelle prossime affronteremo le architetture peer-to-peer, tornando a ragionare sull'organizzazione dei ruoli.

Domanda di autoverifica: un programma server, appena avviato, restituisce un errore proprio quando cerca di associare il proprio socket alla porta del servizio. Quale spiegazione conviene verificare per prima, ricordando che cosa rende unico un recapito su una macchina?

Paragrafi letti

 

    
0%
       Salva

Esercizi su "Le primitive per la gestione della connessione"

0%

Statistics