Alma Mater Studiorum - Universita' di Bologna
Sede di Cesena
II Facolta' di Ingegneria
Reti di Calcolatori
XDR - eXternal Data Representation
Vedi:
• SunSoft, Network Interfaces Programmer's Guide, app. A e B, pagg.
291-352.
• RFC 1014, External Data Representation Standard, 1987.
• RFC 1057, RPC: Remote Procedure Call Protocol Specification –
Version 2, 1988.
Copyright © 2006-2012 by Claudio Salati.
Lez. 9
1
Che cosa e'?
•
XDR is a standard for the description and encoding of information.
• Description Abstract Syntax, equivalente di ASN.1 OSI
• Encoding
•
Transfer Syntax, equivalente di BER OSI
Quindi XDR definisce
Sia una notazione per la definizione di sintassi astratte (ASN)
•
cioe’ una notazione per la definizione delle strutture
informative scambiate tra i moduli di una applicazioni
distribuita
•
cioe’ una notazione per la definizione di protocolli applicativi
Sia un sintassi di trasferimento capace di codificare in una
sequenza di byte le strutture informative definite utilizzando la
notazione astratta XDR
•
la rappresentazione concreta di rete puo’ quindi essere
trasmessa al pari e da questo decodificata
2
Riferimenti
•
Definito da Sun, vedi
•
•
RFC 1014, External Data Representation Standard, 1987.
Utilizzato da Sun
•
per la definizione dell’ASE Sun RPC: vedi
RFC 1057, RPC: Remote Procedure Call Protocol
Specification – Version 2, 1988.
•
per la definizione dell’ASE Sun NFS (Network File System):
vedi
RFC 1094, NFS: Network File System Protocol Specification,
1989.
(poi aggiornato fino a RFC 3530, Network File System (NFS)
version 4 Protocol, 2003)
3
Che cosa e'?
•
In quanto ASN, XDR definisce un linguaggio per descrivere delle
strutture informative.
•
N.B.
•
•
Il linguaggio serve solo per descrivere strutture dati
(informazioni), non per definire operazioni (istruzioni):
non e' un linguaggio di programmazione.
•
L'alternativa di utilizzare mappe di byte, se si devono definire
protocolli complessi, diventa velocemente impraticabile.
•
Anche l’alternativa di definire protocolli tramite stringhe di
caratteri non e’ felice (vedi SMTP e HTTP).
XDR, in quanto sintassi di trasferimento, si basa sulle seguenti
ipotesi:
•
I byte (gli ottetti) sono portabili e costituiti di una sequenza di 8 bit
(hp.: ordine dei bit nel byte uniforme su tutte le macchine).
•
I byte sono scambiati in modo comprensibile tra nodi della rete.
4
XDR TRANSFER SYNTAX
•
.1
La sintassi di trasferimento definita da XDR non e' di tipo TLV.
• L'informazione di tipo non e' mai trasportata esplicitamente.
Cio' implica che il ricevente deve conoscere a priori il tipo
della struttura informativa che sta ricevendo
(come accade ad esempio anche per i protocolli IP, TCP e
UDP!).
• L'informazione di lunghezza e' trasportata esplicitamente solo
quando cio’ e‘ necessario.
•
Per rendere possibile tutto cio' si evitano campi opzionali o default e
strutture dati disordinate (presenti in ASN.1).
• Campi opzionali sono comunque possibili utilizzando il costruttore
union e il tipo void.
• E’ evidente che e’ sempre possibile fare finta che una struttura
dati ordinata (e.g. un array) rappresenti un valore di tipo nonordinato (e.g. un insieme) .
5
XDR TRANSFER SYNTAX
.2
•
La sintassi di trasferimento e' descritta attraverso mappe di byte.
•
Ogni encodifica ha lunghezza multipla di 4 byte.
•
Se una encodifica non e' fisiologicamente di lunghezza multipla di 4,
XDR prescrive che essa venga paddata a destra con dei byte nulli.
• Il padding con un valore ben definito consente il confronto
corretto di informazioni tramite confronto tra i byte corrispondenti
delle loro rappresentazioni.
+--------+--------+...+--------+--------+...+--------+
| byte 0 | byte 1 |...|byte n-1|
0
|...|
0
|
+--------+--------+...+--------+--------+...+--------+
|<-----------n bytes---------->|<------r bytes------>|
|<-----------n+r (where (n+r) mod 4 = 0)>----------->|
BLOCK
6
XDR TRANSFER SYNTAX
.3
•
L'approccio XDR si basa sulla definizione di una rappresentazione
canonica (di rete) per ogni tipo di dato da trasmettere.
•
Ad esempio:
• Viene definito un ordinamento canonico dei byte (big-endian).
• Viene definita una rappresentazione canonica per i numeri interi
(quella binaria, complemento a due).
• Viene definita una rappresentazione canonica dei numeri reali
(quella IEEE).
• ...
•
In questa maniera qualunque trasmettitore che utilizza XDR puo'
inviare una stessa informazione a qualunque ricevitore XDR
trasmettendo la stessa stringa di byte.
•
Esiste un caso in cui questo modo di procedere e' inefficiente:
• quando sia il trasmettitore che il ricevitore sono little-endian.
7
Rappresentazione concreta locale dell’informazione
•
Non e’ specificata da XDR
CORBA specifica invece la rappresentazione locale
dell’informazione in C++ per il suo IDL
•
Specificata dal singolo sistema di programmazione XDR, e.g.
rpcgen di Sun per C:
• Specifico sistema di programmazione XDR-C
• Specifico sistema di programmazione XDR-Java
• Specifico sistema di programmazione XDR-. . .
•
Definisce le strutture dati locali (specifiche dell’ambiente di
programmazione) tramite le quali i tipi di dato XDR sono (devono
essere) rappresentati concretamente in un modulo del programma
distribuito
8
Tipi di dato XDR
•
Tipi semplici:
• [unsigned] int e hyper int
• float, double e quadruple
• enum (enumerazioni, e in particolare bool)
• opaque
• string
• void
•
Tipi composti:
• array
• struct
• union
9
Tipi XDR semplici: int e unsigned int
•
Il tipo int implementa (sintassi astratta) la nozione di intero con
segno nel range -2.147.483.648 .. 2.147.483.647.
•
La rappresentazione canonica (sintassi di trasferimento) di un int e'
• In notazione complemento a due.
• Su 4 byte.
• Big-endian.
•
Una variabile di tipo int e' dichiarata come in C:
int identifier;
•
Il tipo unsigned int implementa (sintassi astratta) la nozione di
intero senza segno nel range 0 .. 4.294.967.295.
•
La rappresentazione canonica (sintassi di trasferimento) di un valore
di tipo unsigned int e'
• In notazione binaria.
• Su 4 byte.
• Big-endian.
•
Una variabile di tipo unsigned int e' dichiarata come in C:
unsigned int identifier;
10
Tipi XDR semplici: hyper int e unsigned hyper int
•
XDR definisce anche tipi interi (con e senza segno) con range di
valori piu’ esteso e che sono rappresentabili su 8 byte, chiamandoli
[unsigned] hyper int.
•
La rappresentazione canonica di un [unsigned] hyper int e'
l'ovvia estensione di quella del corrispondente tipo intero
• In notazione complemento a due o binaria a seconda che l'intero
sia con o senza segno.
• Su 8 byte.
• Big-endian.
•
Una variabile di tipo [unsigned] hyper int e' dichiarata come
in C:
hyper int identifier;
unsigned hyper int identifier;
N.B.: XDR non definisce interi con sizeof di 1 o 2 byte!
11
Tipi XDR semplici: tipi interi
(MSB)
(LSB)
+-------+-------+-------+-------+
|byte 0 |byte 1 |byte 2 |byte 3 |
+-------+-------+-------+-------+
<------------32 bits------------>
INTEGER
(MSB)
(LSB)
+-------+-------+-------+-------+
|byte 0 |byte 1 |byte 2 |byte 3 |
+-------+-------+-------+-------+
<------------32 bits------------>
UNSIGNED INTEGER
(MSB)
(LSB)
+-------+-------+-------+-------+-------+-------+-------+-------+
|byte 0 |byte 1 |byte 2 |byte 3 |byte 4 |byte 5 |byte 6 |byte 7 |
+-------+-------+-------+-------+-------+-------+-------+-------+
<----------------------------64 bits---------------------------->
HYPER INTEGER
UNSIGNED HYPER INTEGER
12
Tipi XDR semplici: float e double
•
Sono definiti due tipi che rappresentano sottoinsiemi di numeri reali,
basati sulle seguenti sintassi di trasferimento:
• float, basato sulla rappresentazione IEEE a 32 bit.
• double, basato sulla rappresentazione IEEE a 64 bit.
• quadruple, basato sulla rappresentazione IEEE double
extended precision.
•
Una variabile di tipo reale e' dichiarata come in C, e.g.:
float identifier;
double identifier;
•
Notare che le notazioni IEEE per i numeri floating-point consentono
anche di esprimere i seguenti "valori":
• infinito con segno (overflow)
• numero denormalizzato (underflow)
• NaN (not a number), che pero' e' definito come system dependent e
13
non puo' quindi essere utilizzato tra sistemi diversi.
Tipi XDR semplici: enumerati
•
E' possibile definire enumerazioni di valori simbolici.
•
Sintassi astratta:
•
Le enumerazioni rappresentano effettivamente sottoinsiemi di
numeri interi con segno (int), e ogni valore simbolico
dell'enumerazione e' associato ad un valore intero univoco.
•
Una variabile di tipo enumerato (enum) e' definita nel modo
seguente (come in C):
enum { symbolName = constant, . . . } identifier;
•
Esempio:
enum { RED = 2, BLUE = 3, GREEN = 5 } colours;
La variabile colours puo' assumere solo uno dei valori interi
listati nell'enumerazione.
•
Sintassi di trasferimento: la rappresentazione canonica (o encodifica)
di un valore enumerato e' identica a quella di un intero con segno.
14
Tipi XDR semplici: booleani
•
Un particolare tipo di enumerazione e' rappresentato dal tipo
booleano (bool).
•
Sintassi astratta:
•
Il tipo booleano ammette due soli valori: FALSE e TRUE.
•
Una variabile di tipo bool e' definita nel modo seguente:
bool identifier;
•
Questa definizione e' equivalente alla seguente
enum { FALSE = 0, TRUE = 1 } identifier;
•
Sintassi di trasferimento:
•
La rappresentazione canonica di un valore booleano e'
identica a quella di un enumerato (un booleano e’ uno
specifico enumerato) e quindi a quella di un intero con segno.
•
N.B.: la rappresentazione canonica di rete XDR di un bool e’
diversa dalla rappresentazione concreta locale C di un
15
booleano.
Tipi XDR semplici: dati opachi .1
•
A volte due programmi devono scambiarsi dei dati (byte) noninterpretati.
• Si e' gia' visto come questo sia tipico delle architetture di
comunicazione layerizzate (l’n+1PDU, cioe’ l’nSDU, e’ un dato non
interpretato per il layer n, e quindi compare come tale nell’nPDU) .
• In ASN.1 questo tipo di dati e' rappresentato attraverso il metatipo
ANY o attraverso il tipo EXTERNAL.
•
Sintassi astratta:
•
Questo tipo di dati in XDR e' chiamato opaco (opaque), ed e'
costituito da una sequenza ordinata di byte.
•
XDR consente di definire sia dati opachi di lunghezza fissa che
dati opachi di lunghezza variabile.
•
Una variabile che rappresenta un dato opaco di lunghezza fissa n
(byte) e' definita nel modo seguente:
opaque identifier [ n ];
// lunghezza fissa
16
Tipi XDR semplici: dati opachi .2
•
Sintassi astratta (continua):
•
Una variabile che rappresenta un dato opaco di lunghezza
indefinita e' definito nel modo seguente:
opaque identifier < n >; // di lunghezza
// indefinita ma
// limitata a n
o
opaque identifier < >;
// di lunghezza
// indefinita
// (limitata a 232-1)
•
Nel primo caso n rappresenta la dimensione massima (upper
bound della lunghezza) del dato opaco.
•
Anche nel secondo caso il dato opaco ha comunque una
dimensione massima, pari a 232-1.
•
Ogni istanza (valore) di questo tipo ha comunque lunghezza
17
definita.
Tipi XDR semplici: dati opachi .3
•
Sintassi di trasferimento:
•
L'encodifica di un dato opaco di lunghezza fissa n e' costituita
dalla sequenza degli n ottetti (byte).
• Se n non e' multiplo di 4 la sequenza di ottetti e' paddata con
un epilogo di allineamento di byte nulli di lunghezza 0..3.
•
L'encodifica di un dato opaco di lunghezza variabile e' costituita di
due parti:
•
dall'encodifica dell'intero senza segno (4 byte) che ne indica
la lunghezza (non paddata),
secondo le regole di encodifica XDR del tipo unsigned int,
•
seguita dalla sequenza di ottetti non interpretati,
eventualmente paddata con byte nulli.
18
Tipi XDR semplici: dati opachi .4
0
1
...
+--------+--------+...+--------+--------+...+--------+
| byte 0 | byte 1 |...|byte n-1|
0
|...|
0
|
+--------+--------+...+--------+--------+...+--------+
|<-----------n bytes---------->|<------r bytes------>|
|<-----------n+r (where (n+r) mod 4 = 0)------------>|
FIXED-LENGTH OPAQUE
0
1
2
3
4
5
...
+-----+-----+-----+-----+-----+-----+...+-----+-----+...+-----+
|
length n
|byte0|byte1|...| n-1 | 0 |...| 0 |
+-----+-----+-----+-----+-----+-----+...+-----+-----+...+-----+
|<-------4 bytes------->|<------n bytes------>|<---r bytes--->|
|<----n+r (where (n+r) mod 4 = 0)---->|
VARIABLE-LENGTH OPAQUE
19
Tipi XDR semplici: stringhe di caratteri ASCII
•
E' possibile definire stringhe di caratteri ASCII di lunghezza indefinita
(tipo string).
•
Queste stringhe non sono null-terminated come in C
(ma, naturalmente, se le stringhe utilizzate nel programma sono quelle
normali del C, non conterranno alcun carattere '\0'):
la loro lunghezza dovra’ quindi essere indicata esplicitamente.
•
Una variabile che rappresenta una stringa di caratteri ASCII e' definita
in uno dei modi seguenti (sintassi astratta):
string identifier < n >;
// di lunghezza indefinita
// ma limitata a n
string identifier < >;
// di lunghezza indefinita
// (limitata a 232-1)
20
Tipi XDR semplici: : stringhe di caratteri ASCII
•
Sintassi di trasferimento:
•
l'encodifica di una stringa di caratteri ASCII e' uguale a quella di
una sequenza di dati opachi di lunghezza indefinita.
0
1
2
3
4
5
...
+-----+-----+-----+-----+-----+-----+...+-----+-----+...+-----+
|
length n
|byte0|byte1|...| n-1 | 0 |...| 0 |
+-----+-----+-----+-----+-----+-----+...+-----+-----+...+-----+
|<-------4 bytes------->|<------n bytes------>|<---r bytes--->|
|<----n+r (where (n+r) mod 4 = 0)---->|
STRING
21
Tipi XDR composti: array
•
E' possibile definire vettori omogenei di qualunque tipo di elementi, sia
di lunghezza definita che di lunghezza indefinita.
Il tipo degli elementi del vettore puo' essere sia semplice che
composto.
•
Anche se tutti gli elementi del vettore sono dello stesso tipo, non
necessariamente devono avere la medesima dimensione (e.g., vettori
di stringhe ASCII di lunghezza indefinita)
•
Una variabile che rappresenta un vettore e' definita in uno dei modi
seguenti (sintassi astratta):
base-type identifier [n];
// lunghezza fissa
base-type identifier <n>;
// lunghezza indefinita
// ma limitata a n
base-type identifier < >;
// lunghezza indefinita
// (limitata a 232-1)
22
Tipi XDR composti: array
•
Sintassi di trasferimento:
•
L'encodifica di un vettore e' simile a quella di una analoga
sequenza di dati opachi.
•
In caso di lunghezza indefinita l'intero iniziale indica il numero di
elementi del vettore.
+---+---+---+---+---+---+---+---+...+---+---+---+---+
|
element 0
|
element 1
|...| element n-1 |
+---+---+---+---+---+---+---+---+...+---+---+---+---+
|<--------------------n elements------------------->|
FIXED-LENGTH ARRAY
0 1 2 3
+--+--+--+--+--+--+--+--+--+--+--+--+...+--+--+--+--+
|
n
| element 0 | element 1 |...|element n-1|
+--+--+--+--+--+--+--+--+--+--+--+--+...+--+--+--+--+
|<-4 bytes->|<--------------n elements------------->|
COUNTED ARRAY
23
Tipi XDR composti: strutture (record)
•
Una variabile che rappresenta una struttura (struct, sequenza
ordinata eterogenea di componenti, semplici o composti) e' dichiarata
nel modo seguente (sintassi astratta):
struct {
component-declaration-1;
component-declaration-2;
. . .
} identifier;
dove ogni component-declaration assume la forma di una
dichiarazione di variabile.
•
Sintassi di trasferimento:
• L'encodifica di una struttura e' formata dalla sequenza delle
encodifiche dei suoi componenti, nello stesso ordine in cui essi
compaiono nella dichiarazione.
• L'encodifica di ogni componente e' di lunghezza multipla di 4 byte,
ma diversi componenti possono ovviamente avere diversa
24
lunghezza.
Tipi XDR composti: esempi di struttura (record)
•
La variabile punto1 rappresenta un punto del piano attraverso le sue
coordinate cartesiane:
struct {
double x;
} punto1;
•
La variabile punto2 rappresenta un punto del piano attraverso le sue
coordinate polari:
struct {
double ro;
} punto2;
•
double y;
double teta;
La variabile complex1 rappresenta un numero complesso attraverso
il valore delle sue parti reale e immaginaria:
struct {
double re;
} complex1;
double im;
25
Tipi XDR composti: esercizi
•
Esercizio 1: Quale e’ la sintassi di trasferimento di valori di questi
tipi/variabili?
•
Esercizio 2: E’ possibile distinguere tra loro sintassi di
trasferimento di questi diversi tipi?
•
Esercizio 3: E’ possibile distinguere la sintassi di trasferimento di
questi tipi da quella di un array di double di lunghezza fissa di 2
elementi?
26
Tipi XDR composti: unioni discriminate
.1
•
Una unione discriminata (union) e' un tipo composto da
• un campo discriminante
• seguito da un secondo campo di un tipo scelto tra un insieme
predefinito di possibilita'
• in base al valore del campo discriminante.
•
Il tipo del campo discriminante deve essere int, unsigned int o un
enumerato (compreso bool).
•
Ogni opzione relativa al secondo campo, detta arm, e' preceduta dal
valore del discriminante che la seleziona.
•
Una variabile che rappresenta una unione discriminata e' dichiarata
nel modo seguente:
union switch (discriminant-decl)
case discriminant-value-1 :
case discriminant-value-2 :
. . .
default :
} identifier;
{
arm-declaration-1;
arm-declaration-2;
default-arm-decl;
27
Tipi XDR composti: unioni discriminate
.2
•
La keyword case e' seguita da un valore legale del discriminante.
•
La dichiarazione del discriminante e degli arm ha la forma di una
dichiarazione di variabile.
•
L'arm default e' opzionale: esso e’ significativo quando il valore del
discriminante e’ diverso da quello che identifica gli altri arm.
•
Se l'arm default non e' presente, il campo discriminante di un valore
legale deve avere uno dei valori listati come selettori degli arm.
•
Sintassi di trasferimento:
•
L'encodifica di una unione discriminata e' formata dalla sequenza
della encodifica del discriminante e di quella dell'arm selezionato
dal valore del discriminante.
•
L'encodifica di ogni arm e quella del discriminante sono di
lunghezza multipla di 4 byte, ma diversi arm possono ovviamente
avere diversa lunghezza.
28
Tipi XDR composti: esempio di unione discriminata
•
La variabile saldoContabile fornisce il valore del saldo del conto
corrente in diverse valute:
union switch (int valuta) {
case 0 : double
euro;
case 1 : double
dollar;
case 2 : double
yen;
default : struct { string valuta<>;
double ammontare;
} altraValuta;
} saldoContabile;
•
Esercizio: definire saldoContabile utilizzando un enumerato come
campo discriminante.
•
Esercizio: quale e’ la rappresentazione canonica (sintassi di
trasferimento) del valore della variabile saldoContabile?
•
Se saldoContabile vale 5 euro?
•
Se saldoContabile vale 5 dollari?
•
Se saldoContabile vale 5 lire sterline?
29
Tipi XDR composti: strutture e unioni
+-------------+-------------+...
| component A | component B |...
+-------------+-------------+...
0
1
2
3
+---+---+---+---+---+---+---+---+
| discriminant | implied arm |
+---+---+---+---+---+---+---+---+
|<---4 bytes--->|
STRUCTURE
DISCRIMINATED UNION
30
Tipi XDR semplici: void
•
XDR supporta una notazione per indicare assenza di un valore.
•
L'assenza di un valore e' indicata dal tipo void, che ha un range di
valori ammessi vuoto.
•
Il tipo void e' utile per specificare funzioni che non hanno alcun
parametro in ingresso o che non ritornano alcun risultato (nelle
RPC, come in C standard).
• void puo' essere anche usato in una unione discriminata per
indicare la non disponibilita' di un valore significativo come secondo
campo, o per estendere il range di un altro tipo con il valore "nondefinito".
•
La dichiarazione di un void ha la seguente forma:
void;
•
L'encodifica (sintassi di trasferimento) di un void e' nulla (0 byte).
31
Tipi XDR semplici: esempio d'uso di void
•
La variabile saldoContabile fornisce il valore in euro del saldo del
conto corrente se il dato e' disponibile:
union switch (bool disponibile) {
case TRUE
: double
euro;
case FALSE : void;
} saldoContabile;
•
Esercizio: quale e’ la rappresentazione canonica (sintassi di
trasferimento) della variabile se il saldo non e’ disponibile?
•
Esercizio: ci sono altri tipi XDR oltre a void la cui sintassi di
trasferimento e’ nulla (ha cioe’ lunghezza 0 byte)?
32
XDR value notation e costanti simboliche
•
ASN.1 definisce una notazione per specificare non solo tipi di dati
ma anche i relativi valori.
•
XDR non fornisce strumenti per esprimere valori a livello di sintassi
astratta!
•
In XDR e’ possibile esprimere dei valori solo in termini di sintassi di
trasferimento.
Notare che una volta noto a priori il tipo del valore ricevuto, la
lunghezza della sua codifica e’ anch’essa
•
•
o nota a priori
•
o indicata esplicitamente nella sintassi di trasferimento.
Meglio: a livello di sintassi astratta XDR offre possibilita’ molto
limitate per esprimere valori.
33
XDR value notation e costanti simboliche
•
A livello di sintassi astratta l’unica sintassi dei valori prevista
consente di esprimere solo numeri interi.
•
E’ anche possibile assegnare un nome simbolico ad un valore intero
attraverso la seguente dichiarazione
const constant-identifier = n;
// n e’ la sintassi dei valori XDR per gli interi,
// e.g. 5
Dove n deve essere un numero (literal) intero (eventualmente con
segno) espresso in notazione decimale.
•
Una costante simbolica puo’ essere utilizzata in un testo XDR
ovunque potrebbe essere utilizzato il corrispondente valore
numerico (e.g. dimensione di vettore o valore discriminante in un
arm)
const dozzina = 12;
uomo quella_sporca[dozzina];
34
Definizione di tipi utente: typedef
•
Si e’ parlato di come dichiarare variabili in XDR, ma in effetti la
dichiarazione di variabili in XDR non ha alcun significato!
XDR serve per definire una interfaccia (il protocollo tra due pari)
e, come negli header file C, in un file di interfaccia non ha senso
definire delle variabili, ma solo dei tipi!
(btw, attenzione alla distinzione in C tra dichiarazione e
definizione!)
E tanto piu’ in quanto due processi remoti non possono
condividere variabili!
(in realta’ nei sistemi distribuiti per l’automazione industriale …)
Caso mai interesserebbe avere a disposizione una adeguata
sintassi dei valori.
•
Lo statement typedef, del tutto analogo a quello del C, anche dal
punto di vista della sintassi, consente di associare un identificatore
ad una definizione di tipo.
35
Definizione di tipi utente: typedef
•
Considerando la definizione
typedef declaration;
(dove declaration ha la sintassi di una dichiarazione di variabile)
essa associa al tipo descritto nella declaration l’identificatore
dichiarato nella declaration stessa.
• typedef egg eggbox[6];
// esempio 1
definisce eggbox come sinonimo di un vettore di 6 egg.
• eggbox non e’ in alcun modo distinguibile da qualunque altro
vettore di 6 egg.
• typedef struct {
// esempio 2
double x; double y;
} punto;
N.B.: dal punto di vista della sintassi di trasferimento un eggbox
non e’ nemmeno distinguibile da una struct composta da 6 egg.
36
Definizione di tipi utente: typedef
•
Nella definizione di tipi
• struct
• union
• enum
esiste una sintassi alternativa all’uso di typedef, analoga a quella
utilizzata per la definizione dei nomi-tag del C.
•
In questo caso pero’, non c’e’ alcuna differenza semantica tra un
nome-tag e un nome-typedef.
•
Le due seguenti definizioni di tipo sono quindi completamente
analoghe:
typedef enum { FALSE = 0, TRUE = 1 } bool;
enum bool { FALSE = 0, TRUE = 1 };
•
La seconda sintassi e’ pero’ considerata preferibile (tradizione).
37
Dati opzionali
•
.1
Optional-data is one kind of union that occurs so frequently that we
give it a special syntax of its own for declaring it.
It is declared as follows:
type-name *identifier;
•
This is equivalent to the following union:
union switch (bool opted) {
case TRUE:
type-name element;
case FALSE: void;
} identifier;
•
It is also equivalent to the following variable-length array declaration,
since the boolean opted can be interpreted as the length of the
array (le due encodifiche sono indistinguibili!):
type-name identifier<1>;
38
Dati opzionali
.2
•
Optional-data is not so interesting in itself, but it is very useful for
describing recursive data-structures such as linked-lists and trees.
•
For example, the following defines a type stringlist that
encodes lists of arbitrary length strings:
struct *stringlist {
string item<>;
stringlist next;
};
•
The optional-data type also has a close correlation to how recursive
data structures are represented in languages such as C by use of
pointers.
•
In fact, the syntax is the same as that of the C language for pointers.
39
Dati opzionali
.3
• stringlist could have been equivalently declared as the
following union:
union stringlist switch (bool opted) {
case TRUE:
struct {
string
item<>;
stringlist next;
} element;
case FALSE:
void;
};
or as a variable-length array:
struct stringlist<1> {
string
item<>;
stringlist next;
};
40
Dati opzionali
.4
•
Anche se dal punto di vista della sintassi di trasferimento puo’ non
esserci differenza tra l’uso di strutture dati ricorsive e quello di dati
opzionali, dal punto di vista della rappresentazione concreta una
grossa differenza c’e’.
•
L’uso di dati opzionali, come indicato anche dalla notazione
sintattica, si traduce in strutture dati locali linkate.
•
Utilizzando dati opzionali e’ possibile rappresentare in XDR e
trasferire sulla rete strutture dati come liste linkate, alberi, grafi.
41
Applicazioni distribuite RPC-based
User application
7.2
RPC
RPC-based
specific
application protocol
User application
RPC-based
specific
application protocol
Layer 7
7.1
RPC protocol entity
Transport Service
RPC protocol
RPC protocol entity
Transport Service
42
Applicazioni distribuite RPC-based
• Esempi di protocolli specifici RPC-based:
• RFC 1094: NFS (Network File System)
• NIS / YP
• RFC 1057: Port Mapper
• RPC protocol:
• Binding (collegamento) dell’interfaccia remota
• Implementazione delle istruzioni di “call” e “return”
• RPC-based specific application protocol:
• Binding dell’interfaccia remota (tramite RPC protocol entity)
• Binding della singola procedura remota
• Un/marshaling dei parametri di input/output della singola
procedura remota
• Invocazione della procedura remota
• XDR e’ utilizzato per definire sia il protocollo RPC che i singoli
protocolli applicativi specifici RPC-based.
43
Protocollo Sun RPC versione 2 (RFC 1057)
.1
struct rpc_msg {
unsigned int xid;
// transaction identifier
union switch (msg_type mtype) {
case CALL:
call_body cbody;
case REPLY: reply_body rbody;
}
body;
};
• The xid of a REPLY message always matches that of the initiating
CALL message.
• NB: The xid field is only used for clients matching reply messages
with call messages or for servers detecting retransmissions;
• the service side (il chiamato) cannot treat this id as any type of
sequence number.
enum msg_type {
CALL = 0,
REPLY = 1
};
44
Protocollo Sun RPC versione 2 (RFC 1057)
.2
struct call_body {
unsigned int rpcvers;
// must be ==2
unsigned int prog;
// binding info
unsigned int vers;
// binding info
unsigned int proc;
// binding info
opaque_auth
cred;
// authen. info
opaque_auth
verf;
// authen. info
// procedure specific parameters start here
};
• Cosa significa “start here”?
• “Qui” l’rpc_msg e’ finito! “Qui” e’ “alla fine dell’rpc_msg”!
• A questo punto e’ chiaro che un “messaggio di chiamata di RPC” e’
in realta’ composto di 2 parti:
1. Un rpc_msg
2. Il parametro di ingresso in forma encodificata
• Discorso analogo varra’ ovviamente anche per il “messaggio di
45
ritorno da RPC”.
Protocollo Sun RPC versione 2 (RFC 1057)
.2’
• Il call_body sarebbe stato definito meglio come
struct call_body {
unsigned int rpcvers;
unsigned int prog;
unsigned int vers;
unsigned int proc;
opaque_auth
cred;
opaque_auth
verf;
opaque
arg<>;
};
//
//
//
//
//
//
//
must be ==2
binding info
binding info
binding info
authen. info
authen. info
input parameter
• I parametri di ingresso della procedura sarebbero cioe’ descritti
meglio come
opaque arg<>;
• In questo modo il “messaggio di chiamata di RPC” coinciderebbe
con l’rpc_msg.
• Esercizio: Si’, ma cosa significa implementativamente “start here”
46
dal punto di vista dell’interazione chiamante-chiamato?
Protocollo Sun RPC versione 2 (RFC 1057)
.2’’
• Perche’ questa strana definizione del “messaggio di chiamata di
RPC” e dell’rpc_msg?
Per ragioni di efficienza (siamo negli anni ‘80 dello scorso secolo
e i byte e il tempo di calcolo sono sono ancora preziosi).
La definizione “opaque arg<>;” implica che vengano
trasmessi in rete (e processati) comunque almeno 4 byte
(perche’?) anche se il parametro di ingresso dell’RPC e’ assente.
Non solo: la definizione “opaque arg<>;” avrebbe implicato in
ricezione una doppia decodifica del campo parametro, con una
ricopiatura e conseguente necessita’ di allocazione di memoria
(Esercizio: spiegare cosa succederebbe).
Tutto cio’ e’ evitato dalla definizione del parametro di ingresso
come semplice place holder.
• Almeno, si sarebbe potuto scrivere, come in reply_body,
opaque arg[0];
(questa notazione evita i problemi di cui sopra, ma rimane comunque
47
solo un place holder)
.2’’’
Protocollo Sun RPC versione 2 (RFC 1057)
enum auth_flavor {
AUTH_NULL
= 0,
AUTH_UNIX
AUTH_SHORT
= 2,
AUTH_DES
/* and more to be defined */
};
= 1,
= 3
struct opaque_auth {
auth_flavor
flavor;
opaque
body<400>;
};
48
Protocollo Sun RPC versione 2 (RFC 1057)
.3
union reply_body switch (reply_stat stat) {
case MSG_ACCEPTED:
accepted_reply areply;
case MSG_DENIED:
rejected_reply rreply;
} reply;
enum reply_stat {
MSG_ACCEPTED = 0,
MSG_DENIED
= 1
};
union rejected_reply switch (reject_stat stat) {
case RPC_MISMATCH:
struct {
unsigned int low;
unsigned int high;
}
mismatch_info;
case AUTH_ERROR: auth_stat stat;
};
49
Protocollo Sun RPC versione 2 (RFC 1057)
enum reject_stat {
RPC_MISMATCH = 0,
AUTH_ERROR
= 1
};
.4
// RPC version number != 2
// can't authenticate caller
enum auth_stat {
AUTH_BADCRED
= 1,
// bad credentials (seal broken)
AUTH_REJECTEDCRED = 2,
// client must begin new session
AUTH_BADVERF
= 3,
// bad verifier (seal broken)
AUTH_REJECTEDVERF = 4,
// verifier expired or replayed
AUTH_TOOWEAK
= 5
// rejected for security reasons
};
50
Protocollo Sun RPC versione 2 (RFC 1057)
.5
struct accepted_reply {
opaque_auth verf;
union switch (accept_stat stat) {
case SUCCESS:
opaque results[0];
// proc-specific results start here
// (N.B. “qui” e’ la fine dell’rpc_msg)
case PROG_MISMATCH:
struct {
unsigned int low;
unsigned int high;
}
mismatch_info;
default:
void;
// Void. Cases include PROG_UNAVAIL,
// PROC_UNAVAIL, and GARBAGE_ARGS.
}
reply_data;
51
};
Protocollo Sun RPC versione 2 (RFC 1057)
enum accept_stat {
SUCCESS
= 0,
/* RPC executed successfully
PROG_UNAVAIL = 1,
/* remote hasn't exported program
PROG_MISMATCH = 2,
/* remote can't support version #
PROC_UNAVAIL = 3,
/* program can't support procedure
GARBAGE_ARGS = 4
/* procedure can't decode params
};
.5'
*/
*/
*/
*/
*/
52
Rappresentazione concreta locale e supporti XDR
•
XDR definisce:
• Un linguaggio per specificare sintassi astratte.
• Una sintassi di trasferimento.
•
XDR non definisce:
• Una sintassi dei valori.
• Una regola per la rappresentazione concreta locale (e.g. in
linguaggio C) di una sintassi astratta.
•
Piu’ in generale, XDR non definisce alcuna API standard che
consenta di accedere alla funzionalita’ di syntax matching.
•
N.B.:
• Corba IDL definisce una API standard (cioe’ anche la rappresentazione
concreta locale dell’informazione e’ standardizzata) ma non una sintassi
dei valori
• ASN.1 non definisce una API standard ma definisce una sintassi dei valori
• In XML Schema sintassi dei valori e sintassi di trasferimento coincidono
53
Rappresentazione concreta locale e supporti XDR
•
Esistono diversi sistemi di programmazione XDR:
• XDR-C
• XDR-Java
•
Sistema di programmazione “standard” XDR-C:
• Una libreria di base: XDR library
• per la de/codifica dei tipi base e dei costruttori di tipo e
• per l’accesso a strutture dati di de/serializzazione.
• Un compilatore (rpcgen, orientato al linguaggio C) per generare
automaticamente le procedure di de/codifica per una qualunque
sintassi astratta user defined specificata in XDR.
basato sull’uso della libreria di base.
•
Un sistemi di programmazione XDR-C definisce le rappresentazioni
concrete C dei tipi base e dei costruttori di tipo definiti da XDR.
•
Perche’ sarebbe importante la standardizzazione della rappresentazione
concreta locale?
Perche’ consentirebbe la portabilita’ dell’applicazione tra diversi sistemi
54
di programmazione XDR per uno stesso linguaggio target.
Il supporto XDR di base: la XDR library
•
The XDR library enables you to write and read arbitrary C constructs
consistently.
•
The XDR library can do this because it has filter routines for strings
(null-terminated arrays of bytes), structures, unions, and arrays.
•
Using more primitive routines, you can write your own specific XDR
routines to describe arbitrary data structures, including elements of
arrays, arms of unions, or objects pointed at from other structures.
The structures themselves may contain arrays of arbitrary elements,
or pointers to other structures.
Ma in realta’ le procedure di de/codifica per i tipi user defined di
una sintassi astratta sono generate dal compilatore rpcgen.
55
Il supporto XDR di base: la XDR library
La XDR library si compone di due parti:
• Un insieme di funzioni filtro per eseguire la
•
codifica fra un valore tipizzato in rappresentazione concreta locale
C e lo stesso valore in rappresentazione canonica di rete XDR
•
decodifica fra un valore tipizzato in rappresentazione canonica di
rete XDR e lo stesso valore in rappresentazione concreta locale C
N.B.: le funzioni filtro si occupano anche della deallocazione di
memoria dinamica utilizzata nelle rappresentazioni locali
per tutti i tipi primitivi e i costruttori di tipo definiti dal linguaggio XDR.
• Un insieme di funzioni per manipolare degli XDR stream,
cioe' delle strutture dati di interfaccia verso strutture di memoria o di
I/O a stream di byte da cui leggere o su cui scrivere i dati serializzati
in formato canonico di rete XDR.
56
XDRstream, funzioni filtro, de/codifica
encodifica
Rappresentazione
concreta locale
dell’informazione
Funzioni
filtro
XDR stream
decodifica
57
XDRstream in memoria centrale
XDR stream
buffer
len
pos
x_op
pos
Parte
del
buffer
gia’
utilizzata
len
58
La XDR library: funzioni di filtro
•
La maggior parte delle funzioni filtro della libreria, e tutte le funzioni di
filtro eventualmente aggiunte, offrono uno stesso tipo di interfaccia.
•
Se il tipo XDR ha nome xxx, la relativa funzione filtro ha il seguente
prototipo:
#define bool_t int
#define TRUE
1
#define FALSE 0
bool_t xdr_xxx(XDR *xdrs, xxx *xp);
Dove
• xdrs e' un puntatore ad uno XDR stream, e
• xp e' un puntatore ad una variabile che contiene o e' destinata a
contenere la rappresentazione locale di un valore di tipo xxx.
• La funzione ritorna FALSE in caso di fallimento, TRUE in caso di
successo.
•
Le funzioni filtro sono direction-independent, la stessa funzione e' cioe'
utilizzata sia per serializzare che per deserializzare un dato ( il
secondo parametro deve essere un puntatore ad una variabile!) 59
La XDR library: funzioni di filtro
•
XDR definisce il seguente tipo puntatore a funzione filtro standard:
typedef bool_t (*xdrproc_t)(XDR *xdrs,
void *localRep);
•
Questo tipo e' utilizzato ad esempio per definire una funzione generica
di liberazione di memoria, basata sulle regole di utilizzo della memoria
dinamica da parte delle rappresentazioni locali dei tipi XDR:
void xdr_free(xdrproc_t proc, char *objp);
• The first argument is the XDR filter routine for the object being
freed.
• The second argument is a pointer to the object itself.
• Note: if a pointer is passed to this routine the pointer is not freed,
but what it points to is freed (recursively, depending on the XDR
routine).
• Non e’ la variabile *objp che viene liberata dalla chiamata a
xdr_free(), ma tutte le variabili (assunte dinamiche) riferite
direttamente o indirettamente da *objp.
60
Allocazione e liberazione della memoria
•
Il funzionamento della funzione xdr_free() e’ basato sui seguenti
fatti/ipotesi:
•
Una funzione filtro si occupa, oltre che di encodifica e decodifica,
anche della liberazione di memoria della rappresentazione
concreta locale di un valore XDR.
•
La politica di dis/allocazione della memoria da parte del
programma utente deve essere ben disciplinata, e le regole per
farlo sono specificate dal sistema di programmazione XDR (vedi
seguito).
•
La funzione xdr_free() assume che tutti gli oggetti riferiti
direttamente o indirettamente da *objp siano stati allocati
dinamicamente (tramite funzione malloc()).
•
E’ il programmatore che quando utilizza la funzione xdr_free() deve
garantire che questo sia vero.
61
La XDR library: funzioni di filtro sugli interi C
•
The first parameter, xdrs, is an XDR stream handle.
•
The second parameter is the address of the number that provides data
to the stream or receives data from it.
•
All routines return TRUE if they complete successfully, and FALSE
otherwise (N.B.: alcune funzioni non sono “in eccesso”?).
bool_t
bool_t
bool_t
bool_t
bool_t
bool_t
bool_t
bool_t
bool_t
bool_t
bool_t
bool_t
xdr_hyper(XDR *xdrs, longlong_t *hp);
xdr_u_hyper(XDR *xdrs, u_longlong_t *uhp);
xdr_int(XDR *xdrs, int *ip);
xdr_u_int(XDR *xdrs, unsigned *up);
xdr_long(XDR *xdrs, long *lip);
xdr_u_long(XDR *xdrs, u_long *lup);
xdr_longlong_t(XDR *xdrs, longlong_t *hp);
xdr_u_longlong_t(XDR *xdrs, u_long *uhp);
xdr_short(XDR *xdrs, short *sip);
xdr_u_short(XDR *xdrs, u_short *sup);
xdr_char(XDR *xdrs, char *cp);
62
xdr_u_char(XDR *xdrs, unsigned char *ucp);
La XDR library: funzioni filtro user defined
•
Basandosi sulle funzioni filtro della libreria di base e' possibile scrivere le funzioni
filtro per le proprie strutture dati.
•
Supponiamo che nel nostro programma C sia definita la struttura dati
struct gnumbers {
int g_assets;
int g_liabilities; };
// tipedef C
•
Questa corrisponderebbe al tipo XDR gnumbers, ma questo e’ irrilevante se non
per il fatto che abbiamo deciso di encodificare la struttura dati C nello stesso
modo in cui sarebbe encodificato il tipo XDR.
struct gnumbers {
// definizione XDR
int g_assets;
int g_liabilities; };
•
La funzione filtro del tipo gnumbers dovrebbe quindi essere definita cosi':
bool_t xdr_gnumbers(XDR *xdrs, struct gnumbers *gp) {
return(xdr_int(xdrs, &gp->g_assets) &&
xdr_int(xdrs, &gp->g_liabilities)? TRUE
: FALSE);
}
63
La XDR library: funzioni di filtro generate dal sistema
In realta’ e’ il sistema di programmazione XDR-C rpcgen che, a partire
dalla definizione del tipo XDR
struct gnumbers {
int g_assets;
int g_liabilities; };
genera automaticamente:
• La rappresentazione concreta locale da utilizzare per rappresentare
valori di questo tipo XDR
typedef struct gnumbers {
int g_assets;
int g_liabilities; } gnumbers;
• La funzione filtro del tipo gnumbers:
bool_t xdr_gnumbers(XDR *xdrs,
gnumbers *gp);
64
La XDR library: funzioni di filtro sui reali C
•
Il significato dei parametri di input/output e' analogo a quello delle
funzioni filtro per i numeri interi.
bool_t xdr_float(XDR *xdrs, float *cp);
bool_t xdr_double(XDR *xdrs, double *ucp);
65
La XDR library: rappresentazione concreta
•
Il tipo C float viene mappato sul tipo XDR float.
•
Il tipo C double viene mappato sul tipo XDR double.
•
Per i tipi interi il mappaggio tipo C tipo (sintassi astratta di
trasferimento) XDR e' il seguente:
Tipo C
char, short, int
Tipo XDR
int
u_char, u_short, u_int
unsigned int
long
int
u_long
unsigned int
longlong
hyper int
u_longlong
unsigned hyper int
N.B.: char, short, long, longlong (e i corrispondenti tipi
unsigned) non sono propriamente tipi XDR, ma sono comunque
supportati dal sitema di programmazione XDR-C della Sun.
66
La XDR library: funzioni di filtro su enumerati (e bool)
•
La XDR library mette a disposizione una funzione filtro generica per
tutti i tipi enumerati.
•
Il tipo booleano e' un caso particolarmente rilevante di enumerato.
Per esso e’ definita una funzione filtro ad hoc.
•
Sia la funzione filtro generica che quella specifica per i booleani
assumono che la rappresentazione concreta C di un enum XDR sia
identica alla rappresentazione concreta C di un int XDR, cioe' un
int C.
#define bool_t
#define FALSE
#define TRUE
int
0
1
// concrete local syntax
#define enum_t
int
// concrete local syntax
bool_t xdr_enum(XDR *xdrs, enum_t *ep);
bool_t xdr_bool(XDR *xdrs, bool_t *bp);
67
La XDR library: esercizio (enumerati e bool)
•
Implementare la funzione filtro xdr_bool().
bool_t xdr_bool(XDR *xdrs, bool_t *bp) {
return xdr_enum(xdrs, (enum_t *) bp);
}
•
Ma in realta’ si puo’ fare di meglio perche’ noi sappiamo quali sono
tutti e soli i valori ammissibili per l’enumerato bool (o per qualunque
altro enumerato usere defined).
bool_t xdr_bool(XDR *xdrs, bool_t *bp) {
bool_t b;
b = xdr_enum(xdrs, (enum_t *) bp);
if(!b) return FALSE;
switch (*bp) {
case TRUE:
case FALSE:
return TRUE;
default:
return FALSE;
}
68
La XDR library: Possibility of No Data
•
Occasionally, an XDR routine must be supplied to the RPC system,
even when no data is passed or required.
•
The following routine does this:
bool_t xdr_void(void);
•
/* always returns TRUE */
Esercizio: implementare la funzione xdr_void().
69
La XDR library: stringhe ASCII
•
.1
In C, una stringa di caratteri e' rappresentata tramite una sequenza di
caratteri ASCII terminata da un carattere '\0', che non e' considerato
parte della stringa ma solo la sua marca di terminazione.
Quando si vuole elaborare una stringa o passarla come parametro,
quello che si considera e' in realta' un puntatore alla (all'indirizzo
della) stringa (NULL-terminata), e non la stringa stessa.
•
Di conseguenza la XDR library considera che la rappresentazione
concreta locale C di una string<> XDR sia un puntatore alla stringa,
e non la sequenza dei caratteri contenuti nella stringa.
•
Si assume anche che la stringa non contenga alcun carattere '\0'.
• La rappresentazione locale C del tipo XDR string<> e'
inadeguata a rappresentare tutti i possibili valori del tipo XDR.
• Un caso analogo (duale) si era gia' presentato anche per gli interi:
la XDR library fornisce funzioni filtro anche per char, short, long
e longlong C (e per i corrispondenti tipi unsigned), anche se
questi tipi non corrispondono ad alcun tipo XDR.
70
La XDR library: stringhe ASCII
.1’
Rappresentazione concreta locale di una stringa di caratteri secondo
rpcgen:
stringVar
...
‘\0’
71
La XDR library: stringhe ASCII
•
.2
Le rappresentazioni interna ed esterna di una stringa sono diverse.
• Esternamente (sintassi di trasferimento XDR) le stringhe sono
rappresentate come sequenze di caratteri ASCII.
• Internamente esse sono rappresentate tramite puntatori.
•
La funzione filtro xdr_string() converte tra queste due
rappresentazioni.
bool_t xdr_string(XDR *xdrs,
char **sp, u_int maxlength);
•
Il parametro sp e' un puntatore ad una stringa, e quindi e' di tipo
char ** (un puntatore a un puntatore ad array di caratteri).
•
Il parametro maxlength specifica il numero massimo di caratteri che
possono essere contenuti nella stringa (e’ ovviamente ignorato
nell’operazione di serializzazione).
•
La funzione ritorna FALSE se il numero di caratteri eccede
maxlength, altrimenti ritorna TRUE.
72
La XDR library: stringhe ASCII
.3
• L'operazione di deserializzazione ha il seguente comportamento:
• Per prima cosa si verifica che la lunghezza della stringa ricevuta
non ecceda maxlength.
• Quindi si dereferenzia sp:
• Se *sp==NULL, allora viene allocata una stringa della
lunghezza appropriata e *sp e' fatto puntare a questa stringa.
• Se *sp!=NULL, allora XDR assume che l'area di memoria
destinata a contenere la rappresentazione locale della stringa
sia gia' stata allocata (quest'area deve poter contenere una
stringa di lunghezza massima maxlength).
• In ogni caso, la stringa XDR e' decodificata nell'area target (l'area in
cui e’ registrato il contenuto della stringa nella rappresentazione
locale) e le viene suffissato il carattere '\0'.
• Nel caso dell’operazione XDR_FREE, si dereferenzia sp.
• Se *sp!=NULL, allora XDR disalloca l'area di memoria di indirizzo
*sp e setta *sp a NULL (il valore maxlength e' ignorato).
73
• Se *sp==NULL, non si fa nulla.
La XDR library: deserializzazione di una stringa ASCII
prima
stringVar
NULL
xdr_string(xdrs, stringVar, maxlength);
dopo
stringVar
allocato dinamicamente
...
‘\0’
Il filtro in decodifica alloca una variaile dinamica di dimensione tale da
contenere esattamente la stringa ricevuta piu’ il carattere di terminazione
‘\0’.
74
La XDR library: deserializzazione di una stringa ASCII
prima
stringVar
...
maxlength+1
xdr_string(xdrs, stringVar, maxlength);
dopo
stringVar
‘\0’
maxlength+1
...
Il filtro in decodifica utilizza (per quello che gli serve) il buffer ricevuto dal
chiamante, che assume essere di dimensione >= maxlength+1. 75
La XDR library: stringhe ASCII
.4
•
La funzione filtro xdr_string() e' generica e applicabile a stringhe di
qualunque lunghezza.
•
Essa pero' non e' conforme al prototipo standard delle funzioni filtro:
Ha un parametro in piu', maxlength, lunghezza massima della
stringa.
•
Nel momento in cui definisco un tipo XDR stringa specifico, ne definisco
comunque una dimensione massima (alla peggio, quella di default).
•
Posso quindi associare al tipo stringa XDR specifico una funzione filtro
con prototipo standard, implementata tramite l'invocazione della
funzione filtro generica.
typedef string filename<255>;
bool_t xdr_filename(XDR *xdrs, char **sp,) {
return(xdr_string(xdrs, sp, 255));
}
•
Lo stesso ragionamento si applica anche a stringhe definite
direttamente in C.
76
La XDR library: disallocazione di una stringa ASCII
xdr_free(xdr_filename, &stringVar)
• Si assume che il buffer riferito da stringVar sia stato allocato
dinamicamente.
• La variabile stringVar non viene liberata, e’ solo il buffer riferito da
lei che viene liberato.
• Se si volesse disallocare la variabile stringVar (se fosse dinamica)
bisognerebbe chiamare direttamente la funzione free() del C (cosi’
come, in questo caso, era stata chiamata la funzione malloc() del C
per allocarla).
stringVar
Si presuppone allocato
dinamicamente.
E’ la parte disallocata
della rappresentazione
concreta locale.
...
‘\0’
77
La XDR library: array di lunghezza variabile
.1
• La XDR library fornisce un funzione filtro (xdr_array()) per trattare in
modo generico array
• di qualunque tipo di elementi, e
• di dimensione (numero di elementi) indefinita
(ovvio che ogni singolo array ha tipo e numero di elementi ben definiti).
• Questa funzione richiede i seguenti parametri:
• Il puntatore ad un array (ap), che si assume rappresentato
concretamente come un puntatore alla sequenza dei suoi elementi.
• Il puntatore alla variabile che indica la lunghezza (numero di elementi)
effettiva dell'array (lp) .
• Il numero massimo di elementi che possono essere contenuti
nell'array.
• La dimensione in byte della rappresentazione concreta locale di un
elemento dell'array, che e' (deve essere) uguale per tutti gli elementi!
(Ad esempio, una stringa di lunghezza variabile e' rappresentata
tramite un puntatore, cosi’ che le rappresentazioni locali di tutte le
stringhe hanno uguali dimensioni!)
78
• Il puntatore alla funzione filtro relativa al tipo degli elementi dell'array.
La XDR library: array di lunghezza variabile
.2
typedef bool_t (*xdrproc_t)(XDR *xdrs,
void *localRep);
bool_t xdr_array(XDR
*xdrs,
char
**ap,
// rif. ad array
u_int
*lp,
// lung. effett.
u_int
maxlength, // lung. max
u_int
elementsize,
xdrproc_t xdr_element
// il prototipo di xdr_element()
// deve essere quello standard
);
•
Durante la deserializzazione, se *ap e' NULL XDR alloca un array
della dimesione appropriata e setta *ap a riferire l'array. In ogni caso,
setta *lp al numero degli elementi effettivamente contenuti nell'array.
•
Durante la serializzazione il numero degli elementi dell'array e'
ottenuto dal valore di *lp.
79
La XDR library: array di lunghezza variabile
.3
• xdr_array() invoca la funzione xdr_element() per processare
ciascuno dei suoi elementi.
•
Anche la funzione xdr_array() non e' conforme al prototipo
standard.
•
Anche in questo caso, dalla definizione di uno specifico tipo array
XDR puo' essere derivata la definizione di una funzione filtro con
prototipo standard.
Che si limita a invocare la funzione filtro generica xdr_array()
passandole tutti i parametri aggiuntivi necessari.
Vedi esempio nelle pagine seguenti, dove si vede anche come le
informazioni sui parametri aggiuntivi sono deducibili dalla
definizone dello specifico tipo di array.
80
XDR library e rpcgen
•
Vedi il riferimento
SunSoft, Network Interfaces Programmer's Guide, app. B
per la definizione delle altre funzioni filtro della libreria XDR di base.
•
Ma e' veramente necessario conoscere la libreria XDR di base e
scriversi a mano le funzioni per la codifica e decodifica delle proprie
strutture dati (della propria sintassi astratta)?
•
Non esiste un sistema di programmazione XDR completo analogo ai
sistemi di programmazione ASN.1?
•
Certo che esiste!
• Basta descrivere in XDR la propria sintassi astratta e
farla processare (compilare) al compilatore rpcgen
• rpcgen generera' automaticamente le relative funzioni filtro
• oltre a fornirci la rappresentazione concreta locale in C dei tipi di
dato della sintassi astratta.
81
XDR library e rpcgen
Protocol.x
(sintassi astratta XDR)
rpcgen
produce
Protocol_xdr.c
(funzioni di
de/codifica per i tipi
definiti nella sintassi
astratta)
Protocol.h
(rappresentazione
concreta locale di
valori della sintassi
astratta)
chiama
include
chiama
XDR Library
chiama
User program
(C)
82
XDR library e rpcgen
RPC_protocol.x
(sintassi astratta XDR del
protocollo Sun RPC v2)
rpcgen
produce
RPC_protocol.h
(rappresentazione
concreta locale di
PDU del protocollo
Sun RPC v2)
RPC_protocol_xdr.c
(funzioni di
de/codifica per
rpc_msg e tipi riferiti)
chiama
include
chiama
XDR Library
chiama
Sun RPC v2
Protocol Entity
(C)
83
Uso di rpcgen: array di lunghezza variabile
•
•
•
Definizione XDR:
typedef int VarIntegerList<1000>;
Struttura dati C per la rappresentazione concreta locale del tipo XDR:
typedef struct {
u_int VarIntegerList_len;
int
*VarIntegerList_val;
} VarIntegerList;
Funzione filtro di de/codifica generata da rpcgen:
bool_t xdr_VarIntegerList(register XDR *xdrs,
VarIntegerList *objp) {
register int *buf;
if (!xdr_array(xdrs,
(char **)&objp->VarIntegerList_val,
(u_int *)&objp->VarIntegerList_len,
1000,
sizeof (int), (xdrproc_t)xdr_int))
return (FALSE);
}
return (TRUE);
84
}
Uso di rpcgen: array di lunghezza variabile
•
Rappresentazione concreta locale C del tipo XDR:
typedef int VarIntegerList<1000>;
•
Definizione della variabile:
VarIntegerList arrayVar;
arrayVar VarIntegerList_len
VarIntegerList_val
VarIntegerList_len
...
85
Uso di rpcgen: array di lunghezza fissa
•
•
•
Definizione XDR:
typedef int IntegerList[1000];
Struttura dati C per la rappresentazione concreta locale del tipo XDR:
typedef int IntegerList[1000];
Funzione di de/codifica:
bool_t xdr_IntegerList(register XDR *xdrs,
IntegerList objp) {
register int *buf;
if (!xdr_vector(xdrs,
(char *)objp,
1000,
sizeof (int),
(xdrproc_t) xdr_int))
return (FALSE);
}
return (TRUE);
}
86
Inizializzazione della rappresentazione locale
•
In generale, ogni variabile definita in un programma dovrebbe essere
correttamente inizializzata
(infatti nei linguaggi object-oriented gli oggetti sono creati da
costruttori che provvedono anche ad inizializzarli).
•
Quando si utilizza XDR l’inizializzazione della variabile target e’
necessaria prima di poter effettuare una operazione di decodifica su
di essa.
Infatti, se non fosse inizializzata correttamente, date le regole del
decoder XDR un campo puntatore con valore diverso da NULL
verrebbe interpretato come puntare al buffer su cui effettuare la
decodifica.
•
Di conseguenza la maniera corretta di inizializzare una
rappresentazione locale e’ di settare tutti i suoi byte a 0.
Questo assicura che qualunque campo puntatore presente nella
struttura sia interpretato come avere valore NULL.
•
Una rappresentazione locale deve essere azzerata ogni volta che
87
vogliamo effettuarci sopra una operazione di decodifica.
La XDR library: gestione degli XDR stream
•
La XDR library contiene un secondo insieme di funzioni dedicato alla
gestione degli XDR stream da/su cui i dati vengono de/serializzati.
•
Esistono diversi tipi di XDR stream che consentono di de/serializzare
dati su
• File del file system (compresi device di I/O),
• Connessioni TCP/IP,
• Memoria centrale.
•
Esistono operazioni per:
• creare (inizializzare) un XDR stream (di un tipo specifico),
• distruggere (terminare l'accesso a) un XDR stream.
•
Ogni XDR stream definisce anche la direzionalita' delle operazioni
che gli sono applicate: XDR_ENCODE, XDR_DECODE, XDR_FREE.
•
In ogni caso occorre per prima cosa allocare una variabile per il
descrittore dello stream:
#include <rpc/xdr.h>
XDR xdrs;
88
La XDR library: creazione di un XDR stream
•
Inizializzazione di un XDR stream su file.
#include <stdio.h>
#include <rpc/xdr.h>
void xdrstdio_create(XDR *xdrs,
FILE *fp,
enum xdr_op x_op);
dove x_op definisce la direzionalita' dello stream.
•
Inizializzazione di un XDR stream su memoria centrale.
#include <rpc/xdr.h>
void xdrmem_create(XDR *xdrs,
char *addr,
u_int len,
enum xdr_op x_op);
89
La XDR library: posizione in un XDR stream
•
Se si sta utilizzando un XDR stream su memoria e’ necessario poter
sapere quanti byte sono stati scritti nel buffer.
• ad esempio per trasmettere questi byte in rete attraverso un
socket (e’ quello che fa il supporto RPC Sun quando utilizza UDP
come trasporto).
•
La funzione xdr_getpos() fornisce questa informazione:
#include <rpc/xdr.h>
u_int xdr_getpos(XDR *xdrs);
dove, per uno stream su memoria in direzione XDR_ENCODE, il valore
tornato rappresenta il numero di byte scritti nello stream.
•
Non tutti gli XDR stream supportano il controllo della posizione, ma
quelli su memoria si’.
•
E’ possibile anche, almeno per gli stream su memoria, settare il
valore della posizione, ad esempio per iniziare la scrittura di un
nuovo messaggio:
bool_t xdr_setpos(XDR *xdrs, u_int pos);
90
XDRstream in memoria centrale
XDR stream
buffer
len
pos
x_op
pos
Parte
del
buffer
gia’
utilizzata
len
•
N.B.: se si eseguono piu’ operazioni successive di encodifica su uno stream
ciascuna di queste inizia a scrivere nel buffer a partire dalla posizione pos
corrente (e di conseguenza la fa avanzare).
•
N.B.: se si eseguono piu’ operazioni successive di decodifica su uno stream
ciascuna di queste inizia a leggere il buffer a partire dalla posizione pos
91
corrente (e di conseguenza la fa avanzare).
La XDR library: distruzione di un XDR stream
•
Uno XDR stream, di qualunque tipo, viene terminato attraverso la
chiamata della funzione:
#include <rpc/xdr.h>
void xdr_destroy(XDR *xdrs);
•
L'utilizzo di un XDR stream dopo che su di esso e' stata eseguita
l'operazione xdr_destroy() e' illegale.
92
Esempio: invio di un rpc_msg su UDP
int xid = 0;
XDR xdrEnc;
char txBuff[8192];
. . .
xdrmem_create(&xdrEnc, txBuff, 8192, XDR_ENC);
. . .
void sendCallMsg(unsigned progN, unsigned versN, unsigned procN,
XDR *xdrE, char txBuff[],
int fd, struct sockaddr_in *dst) {
rpc_msg m;
m.xid = xid++; m.body.mtype = CALL;
m.body.body_u.cbody.rpcvers = 2;
m.body.body_u.cbody.prog = progN;
m.body.body_u.cbody.vers = versN;
m.body.body_u.cbody.proc = procN;
m.body.body_u.cbody.cred.flavor = AUTH_NULL;
m.body.body_u.cbody.verf.flavor = AUTH_NULL;
xdr_setpos(xdrE, 0);
xdr_rpc_msg(xdrE, &m);
sendto(fd, txBuff, xdr_getpos(xdrE), 0, dst, sizeof(dst));
93
}
Esercizio 1
•
Implementare la funzione della libreria XDR:
void xdr_free(xdrproc_t proc, char *objp);
dove:
• The first argument is the XDR routine for the object being freed.
• The second argument is a pointer to the object itself.
• Note: if a pointer is passed to this routine the pointer is not freed,
but what it points to is freed (recursively, depending on the XDR
routine).
94
Esercizio 2
•
Esiste uno stream XDR per la serializzazione (e la trasmissione) di
informazioni su una connessione TCP,
ma non esiste uno stream analogo per operare su UDP.
•
Per utilizzare XDR su UDP:
•
Si utilizza uno stream XDR in memoria.
•
Si utilizza il buffer di memoria dello stream per de/codificare il
messaggio (il buffer serve a contenere il messaggio in
rappresentazione canonica di rete).
•
Si trasmette/riceve sul socket UDP il contenuto del buffer.
•
Il buffer di memoria associato allo stream XDR deve essere
abbastanza grande da contenere l’intero datagram UDP.
•
Come mai se l’uso di XDR su UDP e’ cosi’ semplice sono stati
introdotti degli appositi stream XDR su TCP?
•
Se volessimo utilizzare come layer di trasporto TCP ma non
volessimo utilizzare gli stream XDR appositi, di cosa avremmo
bisogno?
95