Passa al contenuto principale

III - Programma di controllo della rete

Ogni HOST implementa un modulo chiamato programma di controllo della rete (NCP) che controlla tutte le comunicazioni di rete che coinvolgono quell'HOST. La rete degli NCP forma un sistema di comunicazione distribuito che implementa percorsi di comunicazione tra singoli processi. Le questioni del protocollo NCP riguardano: (i) la definizione di questi percorsi di comunicazione, e (ii) un meccanismo per coordinare il sistema NCP distribuito nel mantenere tali percorsi di comunicazione. Di ciò si discute qui di seguito.

Socket​

La comunicazione tra due processi avviene tramite una connessione simplex tra due socket: un socket di invio collegato a un processo e un socket di ricezione collegato a un altro processo. I socket hanno le seguenti caratteristiche:

Identificatore di socket - Un identificatore di socket è usato in tutta la rete per identificare un socket in modo univoco. È composto da 48 bit, con i seguenti componenti:

  1. Numero utente (24 bit) - Un socket collegato a un processo viene identificato come appartenente a quel processo da un numero utente composto da 8 bit di codice di HOST "di origine" più 16 bit di codice utente assegnati dall'HOST di origine. Questo numero utente è lo stesso per tutti i socket collegati a uno qualunque dei suoi processi in qualunque HOST.

  2. Instance tag (8 bit) - Più di un processo appartenente a un utente può esistere simultaneamente all'interno di un unico HOST. L'instance tag identifica il particolare processo al quale appartiene un socket. Per convenzione, il primo processo di un utente su un HOST a usare la rete riceve l'instance tag = 0.

  3. Numero di HOST (8 bit) - È il codice dell'HOST sul quale esiste il processo collegato.

  4. Codice di socket (8 bit) - Questo codice prevede 128 socket di invio e 128 socket di ricezione in ogni processo. Il bit di ordine inferiore determina se si tratta di un socket "di invio" (= 1) o "di ricezione" (= 0).

Stati dei socket - Ogni socket ha uno stato associato. L'NCP può implementare stati più transitori di un socket, ma i tre seguenti hanno importanza concettuale.

  1. Inattivo - non esiste attualmente alcun processo che abbia detto all'NCP di voler ascoltare questo socket. Nessun altro processo può comunicare con successo con un socket inattivo.

  2. Aperto - un processo ha accettato di ascoltare gli eventi relativi a questo socket, ma esso non è ancora connesso.

  3. Connesso - Questo socket è attualmente connesso a un altro socket.

Coda degli eventi del socket - Per ogni socket aperto o connesso è mantenuta una coda di eventi da rivelare al processo proprietario. Essa consiste in un elenco, ordinato cronologicamente, di determinati eventi generati dall'azione di uno o più processi remoti che tentano di connettere o disconnettere questo socket. Una voce della coda degli eventi consiste nel tipo di evento più l'identificatore del socket remoto interessato. Sono definiti i seguenti tipi di evento:

  1. "request" - un socket remoto richiede la connessione. (non accodato se il socket locale è già connesso)

  2. "accept" - un socket remoto accetta la connessione richiesta.

  3. "reject" - un socket remoto rifiuta la connessione richiesta.

  4. "close" - un socket remoto disconnette una connessione esistente.

Un evento "request" viene rimosso dalla coda quando è accettato o rifiutato. Gli altri eventi vengono rimossi dalla coda man mano che sono rivelati al processo proprietario.

Alcuni eventi sono destinati a essere trasparenti per il processo proprietario del socket, e non generano voci nella coda degli eventi.

Sebbene una coda di eventi sia concettualmente illimitata, sembra necessario porre un limite pratico alla sua lunghezza. Quando la coda degli eventi di un socket è piena, ogni evento in arrivo che la accrescerebbe dovrebbe essere scartato e l'NCP mittente avvisato (tramite il comando ERR descritto più avanti).

Comunicazioni di controllo dell'NCP​

La rete degli NCP coordina le proprie attività tramite comandi di controllo scambiati tra i suoi singoli componenti. Questi comandi riguardano in genere la creazione e la manipolazione delle connessioni di socket controllate dall'NCP che riceve il comando. Un comando di controllo è indirizzato a un particolare NCP inviandolo al suo HOST come messaggio sul numero di collegamento 1 (designato come collegamento di controllo), che è riservato a tale scopo. La rete IMP non distingue questi messaggi dai normali messaggi di dati che realizzano la comunicazione attraverso una connessione di socket.

Sono definiti i seguenti comandi di controllo dell'NCP:

  1. Richiesta di connessione

    RFC <local socket> <foreign socket> [<link no.>]

    Un NCP indirizza questo comando a un NCP remoto per tentare di avviare una connessione tra un socket locale e un socket remoto. Se il socket remoto è aperto, l'NCP remoto inserisce un evento "request" nella coda degli eventi del socket, perché sia rivelato al processo che lo possiede. Se il processo remoto accetta, l'NCP remoto restituisce una conferma positiva sotto forma di un altro RFC. Rifiuta la connessione emettendo il comando CLS (vedi sotto). Un RFC viene rifiutato automaticamente, senza interpellare il processo proprietario, se il socket remoto non è aperto (inattivo o connesso). Più RFC diretti allo stesso socket sono inseriti nella sua coda degli eventi nell'ordine di ricezione. Tutti gli RFC accodati vengono automaticamente rifiutati dall'NCP non appena il processo proprietario decide di accettare una connessione. L'NCP che controlla il socket "di ricezione" della coppia potenzialmente connessa designa un numero di collegamento sul quale i messaggi devono fluire.

  2. Chiusura di una connessione

    CLS <local socket> <foreign socket>

    Un NCP emette questo comando di rete per disconnettere una connessione esistente o per dare conferma negativa a un RFC. Esiste un potenziale problema di competizione se un NCP chiude un socket di invio locale, poiché il comando CLS può raggiungere l'NCP remoto prima dell'ultimo messaggio su quella connessione di socket. Questa competizione è impedita rispettando due criteri: (i) un comando CLS per un socket di invio locale non viene trasmesso finché non è tornato l'RFNM dell'ultimo messaggio verso il socket remoto, e (ii) l'NCP remoto elabora tutti i messaggi in arrivo nell'ordine di ricezione.

  3. Blocco dell'output su una connessione

    BLK <foreign send socket>

    Un processo può leggere dati attraverso un socket di ricezione più lentamente di quanto i messaggi arrivino, e quindi i buffer dell'NCP possono tendere a intasarsi. L'NCP emette questo comando verso un NCP remoto per bloccare ogni ulteriore trasmissione sulla coppia di socket finché il processo ricevente non si riallinea.

  4. Ripresa dell'output su una connessione bloccata

    RSM <foreign send socket>

    Un NCP emette questo comando per sbloccare una connessione precedentemente bloccata.

  5. Interruzione del processo collegato a una connessione

    INT <foreign socket>

    La ricezione di questo messaggio induce l'NCP remoto a interrompere immediatamente il processo remoto collegato a <foreign socket>, se esso è connesso a un socket locale. I dati già in transito all'interno della rete degli NCP sulla connessione interrotta saranno trasmessi al socket di destinazione. Il significato di "interruzione" è che il processo interromperà immediatamente la sua esecuzione in corso ed eseguirà una qualche procedura standard. Tale procedura non è definita a questo livello del protocollo.

  6. Segnalazione di un comando erroneo a un NCP remoto

    ERR <code> <command length> <command in error>

    Questo comando serve a segnalare comandi o messaggi di rete spuri, oppure condizioni di sovraccarico che impediscono l'elaborazione del comando. <code> specifica il tipo di errore. Se <code> designa un comando di rete erroneo, <command in error> è quel comando (esclusa l'intestazione IMP) e <command length> è un intero che ne indica la lunghezza in bit. Se <code> designa un messaggio erroneo, <command in error> contiene solo il numero di collegamento sul quale il messaggio erroneo è stato trasmesso. (Ciò differisce leggermente dalla specifica dell'NWG/RFC 40.)

  7. Comando di prova della rete

    ECO <48 bit code> <echo switch>

    Un NCP può collaudare la qualità delle comunicazioni tra sé e un NCP remoto inviandogli un comando ECO con un <48 bit code> arbitrario (della stessa lunghezza di un identificatore di socket) e <echo switch> 'on'. Un NCP che riceve un tale comando ECO dovrebbe inviare immediatamente all'NCP di origine un ECO di conferma con lo stesso <48 bit code> e <echo switch> 'off'. Un NCP non dà conferma di un ECO con <echo switch> 'off'. Riteniamo che questo comando sarà di considerevole aiuto nel collaudo iniziale dell'intera rete.

  8. Comando di nessuna operazione

    NOP

    Un NCP scarta questo comando al ricevimento.

Interfaccia utente verso l'NCP​

L'NCP di ogni HOST dispone di un'interfaccia attraverso la quale un processo locale può usare la rete, sotto il controllo dell'NCP. La specifica esatta di questa interfaccia non è una questione di protocollo di rete, poiché ogni installazione avrà una propria interfaccia adattata alle sue particolari esigenze. I requisiti di protocollo per l'interfaccia utente verso un NCP sono che essa fornisca tutte le funzioni di rete previste e nessun privilegio illegale. Esempi di tali privilegi illegali sono la capacità di spacciarsi per un altro processo, di origliare comunicazioni non destinate ad esso, o di indurre l'NCP a emettere comandi o messaggi di rete spuri.

Delineiamo qui un'interfaccia basata sulla proposta di Carr, Crocker e Cerf, sufficiente a utilizzare pienamente la rete. Sebbene questo particolare insieme di chiamate sia destinato principalmente a scopo illustrativo, esso indica i tipi di funzioni necessarie.

Sono disponibili le seguenti chiamate all'NCP:

  1. LISTEN <my 8 bit socket code>

    L'utente apre questo socket, creando per esso una coda degli eventi vuota. Questa chiamata LISTEN può bloccarsi in attesa del primo evento "request", oppure può tornare immediatamente.

  2. INIT <my socket code> <foreign socket>

    L'utente tenta di connettere <my socket> a <foreign socket>. L'NCP locale invia un RFC all'NCP remoto chiedendo che la connessione sia creata. La conferma restituita è un RFC (richiesta accettata) oppure un CLS (richiesta rifiutata). A scelta del chiamante, la chiamata INIT si blocca sull'evento "accept" o "reject" atteso, oppure può tornare immediatamente senza attendere. In questo caso l'utente deve chiamare STATUS (vedi sotto) in un momento successivo per determinare l'azione dell'NCP remoto. Quando una chiamata INIT bloccata ritorna, l'evento "accept" o "reject" viene rimosso dalla coda degli eventi.

  3. STATUS <my socket code>

    Questa chiamata riporta l'evento non ancora segnalato più antico nella coda di <my socket>. La chiamata STATUS elimina l'evento dalla coda se quel tipo di evento è eliminabile mediante rivelazione.

  4. ACCEPT <my socket code>

    L'utente accetta la connessione con il socket remoto il cui evento "request" è il più antico nella coda degli eventi di <my socket>. Un RFC di conferma viene inviato al socket remoto accettato, e l'evento "request" viene eliminato dalla coda degli eventi. Se nella coda esiste un altro evento "request", l'NCP nega automaticamente la connessione inviando un comando CLS ed eliminando l'evento.

  5. REJECT <my socket code>

    L'utente rifiuta la connessione con il socket remoto il cui evento "request" è il più antico nella coda degli eventi di <my socket>. L'NCP invia un comando CLS ed elimina l'evento "request" dalla coda.

  6. CLOSE <my socket code>

    L'utente ordina all'NCP di disconnettere ogni connessione attiva verso questo socket e di disattivare il socket. L'NCP invia un comando CLS al socket remoto se è esistita una connessione. Anche lo stato del socket remoto diventa chiuso non appena l'evento "close" è rivelato al processo remoto.

  7. INTERRUPT <my socket code>

    L'utente ordina all'NCP di inviare un comando INT al socket remoto connesso a <my socket>.

  8. TRANSMIT <my socket code> <pointer> <nbits>

    L'utente desidera leggere (<my socket> è di ricezione) o scrivere (<my socket> è di invio) <nbits> di dati da o verso un'area indicata da <pointer>. Una chiamata di scrittura ritorna immediatamente dopo che l'NCP ha accodato i dati per inviare un messaggio sulla connessione. La chiamata di scrittura si blocca solo se la connessione è bloccata o se l'NCP locale è troppo carico per elaborare subito la richiesta. I dati da trasmettere su una connessione sono formattati in uno o più messaggi IMP di lunghezza massima 8095 bit e trasmessi all'HOST remoto sul numero di collegamento specificato nell'RFC inviato dall'NCP che controlla la connessione di ricezione. Un evento "close" nella coda degli eventi di <my socket> è rivelato dall'azione di TRANSMIT. Una chiamata di scrittura rivela immediatamente l'evento "close". Una chiamata di lettura lo rivela quando tutti i dati sono stati letti.

La storia di una connessione dal punto di vista dell'utente​

Un esempio illustrativo​

Supponiamo che il processo 'a' sull'HOST A desideri stabilire una connessione con il processo 'b' sull'HOST B. Prima che la comunicazione possa avvenire, devono essere soddisfatte due condizioni:

  1. il processo 'a' deve poter indicare al proprio NCP un socket nello spazio di socket di 'b' al quale vuole connettersi.

  2. il processo 'b' deve già essere in ascolto (LISTEN) su questo socket.

1. Stabilire la connessione​

  1. il processo 'b' esegue LISTEN sul socket 'Bb9'.

  2. il processo 'a' esegue INIT di 'Bb9' verso il proprio 'Aa12'. L'NCP presso A genera un RFC che specifica il numero di collegamento = 47, scelto dal proprio insieme di collegamenti disponibili. Questo è il collegamento sul quale riceverà i messaggi se la connessione è accettata (ACCEPT) dal processo 'b'.

  3. il processo 'b' è informato della richiesta INIT di A. Può rifiutare (REJECT) la connessione (l'NCP B restituisce un CLS) oppure accettarla (ACCEPT) (l'NCP B restituisce un RFC).

  4. Se il processo 'b' esegue ACCEPT, l'RFC di conferma stabilisce la connessione, e i messaggi possono ora fluire.

          HOST  A               |          HOST B
INITIATOR | ACCEPTOR
PROCESS 'a' | PROCESS 'b'
|
|
| a. LISTEN 'socket code 9'
|
|
b. INIT 'socket code 12' 'Bb9' |
RFC 'AA12' 'Bb9' 'link 47' ==========>
|
| c. ACCEPT 'socket code 9'
| RFC 'Bb9' 'Aa12'
|
| d. TRANSMIT 'send buffer' 'len'
| 'socket 9'
<============== IMP message 'link 47' 'send buffer'
|
e. TRANSMIT 'rec buffer' 'length'
'socket 12' ============>
|
| f. CLOSE 'socket code 9'
|
last RFNM ===>
<============== CLS 'Bb9' 'Aa12'
closes socket 'Aa12' |
|

Figura 2: Stabilire una connessione socket e comunicare su di essa

2. Inviare messaggi su una connessione​

  1. Il processo 'b' emette una chiamata TRANSMIT per inviare dati sulla connessione. L'NCP B li formatta in un messaggio IMP e lo invia all'NCP A con il numero di collegamento = 47 come specificato dall'RFC di A.

  2. L'NCP A riceve il messaggio grezzo dall'NCP B con il numero di collegamento = 47. L'NCP A usa questo numero di collegamento per decidere chi sia il destinatario previsto, e memorizza il messaggio in un buffer per il processo destinatario.

  3. Il processo 'a' può emettere una chiamata di lettura (TRANSMIT) per il codice di socket 12 in qualunque momento. La chiamata di lettura si blocca se non vi sono dati in attesa per il socket. La chiamata di lettura preleva il numero specificato di bit trasmessi sul codice di socket 12, eventualmente a cavallo di un confine tra messaggi IMP. I confini dei messaggi IMP sono invisibili alla chiamata di lettura.

  4. Se il processo 'b' invia dati sulla connessione più rapidamente di quanto il processo 'a' li prelevi, l'NCP A può emettere un comando BLK verso l'NCP B quando i buffer di A iniziano a riempirsi. In seguito, quando il processo 'a' si è riallineato, l'NCP A può dire a B di riprendere la trasmissione tramite un comando RSM.

3. Il processo 'b' chiude la connessione​

  1. Il processo 'b' decide di chiudere la connessione ed emette la chiamata CLOSE verso l'NCP B. Per evitare problemi di competizione, B attende l'RFNM del messaggio precedente su questa connessione, poi invia il comando CLS all'NCP A. Quando l'RFNM del messaggio di comando CLS ritorna, l'NCP B elimina il socket 'Bb9' dalle proprie tabelle, realizzando così la chiusura dalla sua parte e disattivando 'Bb9'.

  2. A causa dell'elaborazione sequenziale all'interno dell'NCP A, è garantito che l'ultimo messaggio verso il socket 'Aa12' sia stato indirizzato a un processo prima che arrivi il CLS dall'NCP B. Al ricevimento del CLS da B, l'NCP A contrassegna il socket 'Aa12' come "close pending" e inserisce un evento "close" nella coda degli eventi di 'Aa12'.

  3. Il processo 'a' può ancora emettere chiamate di lettura per il socket 'Aa12' finché vi sono dati in buffer in attesa. Quando 'a' emette una chiamata di lettura dopo che il buffer è stato svuotato, l'evento "close" viene rivelato per informare 'a' della chiusura, e il socket 'Aa12' viene eliminato dalle tabelle dell'NCP A.

4. Il processo 'a' chiude la connessione​

  1. Torniamo al passo 2 e supponiamo che il processo 'a' voglia chiudere la connessione dalla propria parte. Non vi è alcun problema di competizione, poiché supponiamo che, una volta emessa una chiamata CLOSE, 'a' non voglia più leggere messaggi su quel socket.

  2. Supponiamo che il processo 'a' emetta una chiamata CLOSE sul socket 'Aa12'. L'NCP A invia immediatamente un comando CLS all'NCP B e contrassegna il socket 'Aa12' come "close pending". Ogni dato in buffer per la lettura su 'Aa12' viene scartato. Per consentire ai messaggi rimanenti, già in transito dal processo 'b', di filtrare attraverso la rete IMP fino all'NCP A ed essere scartati senza commenti di errore, l'NCP A mantiene 'Aa12' nelle proprie tabelle per un periodo di tempo adeguato dopo aver ricevuto l'RFNM del comando CLS. Durante questo periodo l'NCP A scarta tutti i messaggi ricevuti sulla connessione in chiusura. Dopo aver concesso un tempo ragionevole perché questi messaggi morti arrivino, l'NCP A elimina 'Aa12' dalle proprie tabelle, chiudendo di fatto la connessione e disattivando 'Aa12'. Ulteriori messaggi verso il socket 'Aa12' fanno sì che l'NCP A invii un ERR "erroneous command" all'NCP di origine.

  3. Quando l'NCP B riceve il comando CLS, il socket 'Bb9' viene contrassegnato come "close pending" e l'evento CLS viene inserito nella coda degli eventi di 'Bb9'. La volta successiva che il processo 'b' desidera scrivere su quel socket, l'evento CLS viene rivelato per informarlo della chiusura, e il socket 'Bb9' viene rimosso dalle tabelle dell'NCP B.