Socket Rpc Tli

Embed Size (px)

Citation preview

Outils de Communication Rseau sous Unix BSD 4.X ePhilippe Durif 1999

2

Contents1 Le 1.1 1.2 1.3 1.4 concept dInternet Internet : un rseau de rseaux (format des adresses e e IP : protocole de la couche rseau . . . . . . . . . . e UDP et TCP : protocoles de la couche transport . . Internet et Unix BSD 4.X (i.e. les Suns) . . . . . . 1.4.1 La table des machines (hosts) . . . . . . . . 1.4.2 La table des rseaux (networks) . . . . . . . e 1.4.3 La table des services (/etc/services) . . . 1.4.4 Manipulation des adresses Internet . . . . . 1.4.5 Conversions machine/rseau . . . . . . . . . e . . . . . . . . . . . . . . . . . . . . . 1 1 2 2 2 3 4 5 5 6 7 7 8 8 9 9 10 12 13 13 13 13 14 14 14 15 15 15 15 16 17 17 18 18 18

Internet) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

2 Adresses de la couche transport 2.1 Format gnral des adresses . . . . . . . . . . e e 2.2 Format des adresses du domaine Unix . . . . . 2.3 Format des adresses du domaine Internet . . . 2.4 Gestion des numros de port . . . . . . . . . . e 2.4.1 La table des services (/etc/services) 2.5 Fabriquer des adresses Internet . . . . . . . . 2.5.1 Utilitaires BSTRING(3) . . . . . . . .

3 Les sockets 3.1 Caractristiques dune socket . . . . . . . . . . . . . . . . . e 3.1.1 Domaines de communication dune socket . . . . . . 3.1.2 Types de communication dune socket . . . . . . . . 3.1.3 Protocole dune socket . . . . . . . . . . . . . . . . . 3.2 Primitives gnrales sur les sockets . . . . . . . . . . . . . . e e 3.2.1 Crer une socket : socket() . . . . . . . . . . . . . . e 3.2.2 Dtruire une socket : close() . . . . . . . . . . . . . e 3.2.3 Rduire les fonctionnalits dune socket : shutdown() e e 3.2.4 Associer une adresse a une socket : bind() . . . . . . ` 3.2.5 Consulter ladresse dune socket : getsockname() . . 3.2.6 Exemples dutilisations de bind() . . . . . . . . . . . 3.2.7 Connexion de socket : connect() . . . . . . . . . . . 3.2.8 Rception de message : recv() . . . . . . . . . . . . e 3.2.9 Emission de message : send() . . . . . . . . . . . . . 3.2.10 Lecture et criture : read(), write() . . . . . . . . . e 3.2.11 Attente slective : select() . . . . . . . . . . . . . . e 3

4

CONTENTS 3.3 Notion de client et de serveur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.4 Autres aspects des sockets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 22

4 Les 4.1 4.2 4.3

sockets de type datagram (SOCK DGRAM) 25 R`gles gnrales dutilisation des primitives . . . . . . . . . . . . . . . . . . . . . . . . 25 e e e Exemples dutilisation des sockets SOCK DGRAM . . . . . . . . . . . . . . . . . . . 26 Comment concevoir un nouveau service . . . . . . . . . . . . . . . . . . . . . . . . . . 29 33 34 34 34 34 36 38 38 41 41 41 41 42 42 42 43 43 43 43 43 44 44 44 45 45 45 45 46 47 49 49 49 50 50

5 Les sockets de type circuit virtuel (SOCK STREAM) 5.1 Les primitives spciques aux serveurs en circuits virtuels e 5.1.1 listen() . . . . . . . . . . . . . . . . . . . . . . 5.1.2 accept() . . . . . . . . . . . . . . . . . . . . . . 5.2 Un premier exemple . . . . . . . . . . . . . . . . . . . . 5.3 Utilisation des sockets SOCK STREAM . . . . . . . . . 5.4 Autres exemples . . . . . . . . . . . . . . . . . . . . . . . 5.4.1 Un serveur de piles parall`le et multi-processus . e

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

. . . . . . .

6 Le mcanisme XDR (eXternal Data Representation) e 6.1 Gnralits sur le mcanisme XDR . . . . . . . . . . . . . . . . . . . . e e e e 6.2 Fonctions de gestion des ots XDR . . . . . . . . . . . . . . . . . . . . 6.2.1 fdopen() et fflush() . . . . . . . . . . . . . . . . . . . . . . . 6.2.2 xdrstdio create() . . . . . . . . . . . . . . . . . . . . . . . . 6.2.3 xdr destroy() . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.3 Fonctions de transmission XDR . . . . . . . . . . . . . . . . . . . . . . 6.3.1 Fonctions de transmission sans allocation dynamique . . . . . . 6.3.1.1 xdr void() . . . . . . . . . . . . . . . . . . . . . . . . 6.3.1.2 xdr int() . . . . . . . . . . . . . . . . . . . . . . . . . 6.3.1.3 xdr opaque() . . . . . . . . . . . . . . . . . . . . . . . 6.3.1.4 xdr union() . . . . . . . . . . . . . . . . . . . . . . . 6.3.2 Fonctions de transmission avec allocation dynamique ventuelle e 6.3.2.1 xdr string() . . . . . . . . . . . . . . . . . . . . . . . 6.3.2.2 xdr wrapstring() . . . . . . . . . . . . . . . . . . . . 6.3.2.3 xdr array() . . . . . . . . . . . . . . . . . . . . . . . 6.3.2.4 xdr bytes() . . . . . . . . . . . . . . . . . . . . . . . 6.3.2.5 xdr reference() . . . . . . . . . . . . . . . . . . . . . 6.3.2.6 xdr pointer() . . . . . . . . . . . . . . . . . . . . . . 6.3.3 Libration de zone xdr free() . . . . . . . . . . . . . . . . . . e 6.4 Un exemple complet . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.5 Construction de nouvelles fonctions XDR . . . . . . . . . . . . . . . . . 6.5.1 XDR de structures . . . . . . . . . . . . . . . . . . . . . . . . . 6.5.2 XDR de tableau de taille variable . . . . . . . . . . . . . . . . . 6.5.3 XDR de structures contenant un tableau . . . . . . . . . . . . . 6.5.4 XDR de liste cha ee . . . . . . . . . . . . . . . . . . . . . . . . n

. . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . .

CONTENTS 7 Appel de procdure loigne SunOS 4.x (RPC) e e e 7.1 Rappel . . . . . . . . . . . . . . . . . . . . . . . . 7.2 Architecture des RPC de SunOS 4.x . . . . . . . 7.2.1 Situation des RPC . . . . . . . . . . . . . 7.2.2 Nommage des services RPC . . . . . . . . 7.2.3 Le processus portmap . . . . . . . . . . . 7.2.4 Lenregistrement dun serveur . . . . . . . 7.2.5 Le mcanisme dappel vu du client . . . . e 7.2.6 Smantique des appels . . . . . . . . . . . e 7.3 Primitives RPC de haut niveau . . . . . . . . . . 7.3.1 Le client . . . . . . . . . . . . . . . . . . . 7.3.1.1 callrpc() . . . . . . . . . . . . 7.3.1.2 clnt perrno() . . . . . . . . . . 7.3.2 Le serveur . . . . . . . . . . . . . . . . . . 7.3.2.1 registerrpc() . . . . . . . . . . 7.3.2.2 svc run() . . . . . . . . . . . . . 7.3.3 Exemple : un compte bancaire . . . . . . . 7.4 Primitives RPC de bas niveau . . . . . . . . . . . 7.4.1 Le client . . . . . . . . . . . . . . . . . . . 7.4.1.1 clnt create () . . . . . . . . . 7.4.1.2 clnt control () . . . . . . . . . 7.4.1.3 clnt pcreateerror () . . . . . 7.4.1.4 clnt destroy() . . . . . . . . . 7.4.1.5 clnt call() . . . . . . . . . . . 7.4.1.6 clnt freeres() . . . . . . . . . 7.4.1.7 clnt perror() . . . . . . . . . . 7.4.2 Le serveur . . . . . . . . . . . . . . . . . . 7.4.2.1 svctcp create() . . . . . . . . . 7.4.2.2 svc register() . . . . . . . . . 7.4.2.3 Le dispatcher . . . . . . . . . . . 7.4.3 Exemple : le compte bancaire . . . . . . . 7.5 RPC non bloquant . . . . . . . . . . . . . . . . . 7.6 Diusion dappel . . . . . . . . . . . . . . . . . . 7.7 Rtro appel . . . . . . . . . . . . . . . . . . . . . e 7.8 RPC scuriss . . . . . . . . . . . . . . . . . . . . e e 7.8.1 Exemple : le compte bancaire scuris . . . e e 8 Le 8.1 8.2 8.3

5 53 53 53 53 53 54 55 55 55 55 56 56 56 56 56 57 57 58 58 59 59 60 60 60 60 60 60 61 61 61 62 63 64 64 64 64

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

gnrateur RPCGEN e e 67 Ecriture de linterface en rpcgen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67 Ecriture des services . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 Ecriture dun client . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71 71 71 72 72

9 Linterface TLI 9.1 Architecture des TLI . . . . . . . . . . . . . . . . . . . . . . . 9.1.1 Fournisseurs de transport ou TP (Transport Provider) 9.1.2 Ouverture dun tep : t open() . . . . . . . . . . . . . . 9.1.3 Publication dun tep : t bind() . . . . . . . . . . . . .

6 9.1.4 Les vnements : t look() . . . . . . . e e 9.1.5 Les tats . . . . . . . . . . . . . . . . . e 9.1.6 Les structures de donnes : t alloc() e 9.2 Environnement de dveloppement . . . . . . . e 9.3 Un exemple de communication en T COTS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

CONTENTS . . . . . . . . . . . . . . . . . . . . . . . . . 72 73 76 77 77

Chapter 1 Le concept dInternet1.1 Internet : un rseau de rseaux (format des adresses e e Internet)

Lide de lInternet (projet DARPA) est de fdrer dirents rseaux htrog`nes dans un seul e e e e e ee e super-rseau ; en particulier, la technologie Internet ne fait pas dispara les rseaux existant, elle e tre e sappuie sur eux. Internet propose dune part une technique dadressage de rseaux et de machines, e dautre part une suite de protocoles. Gnralement les adresses rseaux ne dsignent pas des machines mais plutt des points dacc`s e e e e o e au rseau, un point dacc`s est, par exemple, une carte (carte Ethernet, carte Token Ring). Ainsi, e e une machine quipe de plusieurs cartes une passerelle par exemple aura plusieurs adresses. e e Par abus de langage, on parle souvent dadresse de machine (host). Lors de sa fabrication, chaque carte Ethernet est marque dun numro unique de 48 bits. Lunicit e e e de ce numro fait quil peut reprsenter ladresse de la carte, quel que soit lenvironnement dans lequel e e elle est plonge. e En revanche, une carte Token Ring porte un numro logiciel aect par ladministrateur local du e e rseau. Une telle adresse nest valable que dans ce rseau. e e Dans Internet, une adresse de point dacc`s est code sur 32 bits et poss`de deux niveaux, les bits e e e de poids forts permettent didentier le rseau parmi lensemble des rseaux connus par Internet, les e e bits de poids faible permettent de dsigner un point dacc`s particulier (une machine) dans ce rseau. e e e La gestion des adresses Internet est hirarchique : le NIC (Network Information Control Center) e distribue des fourchettes dadresses rseau a des organismes ociels (pour la France cest lINRIA) e ` qui se chargent dallouer des adresses aux organismes demandeurs. La partie machine dune adresse est gre localement. Ce fonctionnement assure lunicit dune adresse Internet. ee e Internet propose trois formats dadresse ce qui autorise une certaine souplesse vis a vis de la taille ` des rseaux physiques (voir gure 1.1). e Les protocoles de lInternet sont nombreux, dont : IP qui est le protocole rseau de base, e UDP et TCP sont les protocoles de transport construits sur IP, ICMP protocole de contrle (message derreur, rgulation de ux, ...), o e ARP (resp. RARP) spciques au rseau Ethernet et qui permet, connaissant ladresse Internet e e (resp. Ethernet) dune machine, dobtenir son adresse Ethernet (resp. Internet). 1

2 Classe dadresses A B C format

CHAPTER 1. LE CONCEPT DINTERNET nombre de rseaux de points dacc`s e e 128 16M 16K 64K 2M 256

0rrrrrrr mmmmmmmm mmmmmmmm mmmmmmmm 10rrrrrr rrrrrrrr mmmmmmmm mmmmmmmm 110rrrrr rrrrrrrr rrrrrrrr mmmmmmmm

Figure 1.1: Les trois classes dadresses Internet, les r dsignent les bits dadresse de rseau, les m ceux e e de point dacc`s. La classe A correspond aux rares tr`s grands rseaux, le rseau de Lille 1 est de e e e e classe B.

1.2

IP : protocole de la couche rseau e

IP Internet Protocol est un protocole de commutation de paquet de point dacc`s a point e ` dacc`s (couche rseau de lISO). Ses caractristiques sont son manque de abilit (pas daccus de e e e e e rception), lordre darrive nest pas forcment le mme que lordre de dpart, mais il y a prservation e e e e e e des fronti`res denregistrement. Typiquement un paquet IP poss`de une borne suprieure pour sa e e e taille, son format est indiqu a la gure 1.2. e` adresse Internet metteur e adresse Internet destinataire information utile

Figure 1.2: Format simpli dun paquet IP e

1.3

UDP et TCP : protocoles de la couche transport

Les protocoles UDP User Datagram Protocol et TCP Transport Control Protocol se situent au dessus dIP ; ils assurent tous deux la transmission dinformation dapplication ` a application et non plus de point dacc`s a point dacc`s ; plutt que dapplication on parlera de e ` e o service ou encore de SAP1 . Plusieurs services pouvant tourner sur une mme machine, on les distingue e grce a un numro de port (sur Sun cest un u short). Ladresse dun service est donc un couple a ` e adresse Internet, numro de port. e UDP est une simple surcouche de IP (commutation de paquet) ; TCP assure une commutation de circuit virtuel avec les qualits qui en dcoulent (abilit, respect de lordre dmission) mais il ne e e e e prserve pas les fronti`res denregistrement. e e Les services ociels (well-known), comme FTP ou TELNET, portent des numros de port ale lous par un organisme centralisateur et sont infrieurs a IPPORT_RESERVED, constante dnie dans e e ` e . Les ports au del` de IPPORT_USERRESERVED sont en principe utilisables librement. a

1.4

Internet et Unix BSD 4.X (i.e. les Suns)

Le syst`me Unix BSD 4.X des Suns int`gre le concept Internet, le rseau physique tant la e e e e plupart du temps Ethernet. Chaque Sun poss`dent donc deux adresses par point dacc`s : une adresse e e Ethernet (pour les communications de la couche liaison de donnes : 802.3) et une adresse Internet. e1

selon la terminologie ISO : Service Access Point.

1.4. INTERNET ET UNIX BSD 4.X (I.E. LES SUNS)

3

Les outils de dveloppement rseau BSD 4.X proposent donc une approche Internet qui se matrialise e e e dans la notion de socket donnant acc`s aux protocoles UDP et TCP. e Dautre part, les Macs du labo sont sur un rseau AppleTalk qui ne conna pas Internet, il est e t cependant interconnect avec Ethernet par une passerelle FastPath qui simule un adressage Internet e des dirents Macs. e Pour ladministration du rseau on dispose de plusieurs tables qui mettent en relation des noms e externes symboliques (comme homel, ftpd, ...) et des noms internes (adresses Internet, numro de e port, ...). Les tables qui nous intressent sont celles des machines, des rseaux et des services. Pour e e chacune de ces tables on dispose dun interface shell et dun interface programme.

1.4.1

La table des machines (hosts)

Elle met en relation les noms symboliques des machines et leurs adresses Internet. linterface shell Il propose le chier /etc/hosts et la commande ypcat hosts qui utilise le NIS 2 ; cette derni`re forme est plus s re, en eet le chier /etc/hosts local nest pas forcment a jour. e u e ` Chaque ligne correspond a une machine et contient de gauche a droite ladresse Internet compl`te (4 ` ` e champs dcimaux spars par des points), le nom symbolique ociel puis une liste ventuellement e e e e vide des noms alias. Voici les formats dadresses correspondant aux trois classes A, B et C : Classe A B C avec ri et si compris entre 0 et 255. linterface programme Il propose les primitives de GETHOSTENT(3N) qui utilise la structure struct hostent { char *h name ; /* official name of host */ char **h aliases ; /* alias list, terminee par NULL */ int h addrtype ; /* address type i.e. AF INET */ int h length ; /* length of address : 4 octets pour Internet */ char **h addr list ; /* liste des addresses, pour Internet la premiere */ /* est la bonne et est en format reseau. */ /* ATTENTION ! ! ! il faut y acceder avec : */ /* (unsigned long **) p->h addr list */ #define h addr h addr list[0] /* address, for backward compatiblity */ };2

Format r1 .s3 .s2 .s1 r2 .r1 .s2 .s1 r3 .r2 .r1 .s1

Network Information System : base de donne accessible par toutes les machines du rseau e e

4 Les primitives sont :

CHAPTER 1. LE CONCEPT DINTERNET

#include #include #include struct hostent *gethostent (void), *gethostbyname (char *i name) ; Elles renvoient chacune un pointeur sur une structure hostent qui contient les champs dune ligne du chier /etc/hosts. gethostent() permet un parcours squentiel des entres et renvoie NULL e e apr`s la derni`re. e e Attention : le pointeur renvoy rep`re une zone statique dont le contenu est cras a chaque appel. e e e e` Les adresses (32 bits) sont fournies dans le format du rseau (cf BYTEORDER), pas dans celui e de la machine. Exemple tir de la commande ypcat hosts e 127.0.0.1 134.206.1.1 ... 134.206.10.65 134.206.10.66 ... 134.206.11.247 ... 134.206.12.1 134.206.12.10 localhost citil # CYBER 962-32 brigant gordon mac27 homel ms_mailhost ms9

localhost reprsente la machine sur laquelle tourne le processus (` cause de 127 qui est un numro e a e spcial). e homel est le nom ociel, il a un alias : ms_mailhost. 134 (1000 0110b) correspond a des adresses ` de classe B, ladresse rseau de homel est donc 134.206, cest en fait ladresse, tout a fait ocielle, e ` du rseau lilnet (le rseau de LILLE 1). Il est possible de grer localement des sous-rseaux, par e e e e exemple le premier octet de ladresse machine correspond a ladresse de sous-rseau : 10 dsigne le ` e e rseau Ethernet du laboratoire, 11 est le rseau Appletalk des Macs, 12 est le rseau Ethernet de e e e lenseignement.

1.4.2

La table des rseaux (networks) e

Elle met en relation les noms symboliques de rseau et leurs adresses Internet. e linterface shell Il propose le chier /etc/networks et la commande ypcat networks. Chaque ligne correspond a un rseau et contient de gauche a droite le nom symbolique ociel, la partie ` e ` rseau de ladresse Internet (au plus 3 champs dcimaux spars par des points), puis une liste e e e e eventuellement vide des noms alias. linterface programme Il propose les primitives de GETNETENT(3N) qui utilisent la structure :

1.4. INTERNET ET UNIX BSD 4.X (I.E. LES SUNS) struct netent { char *n name ; /* official name of net */ char **n aliases ; /* alias list, terminee par zero */ int n addrtype ; /* net number type i.e. AF INET */ long n net ; /* net number en format interne et cadre */ /* dans les octets de poids faibles */ }; Les primitives sont : #include struct netent *getnetent (void), *getnetbyname (char *name) ;

5

qui renvoient un pointeur sur une structure statique correspondant a une ligne de la table des ` rseaux. e Exemple tir de ypcat networks e arpanet ucb-ether loopback reunir lilnet irisanet 10 arpa 46 ucbether 127 128.201 134.206 localnet 131.254

Moralit : les stations du M3 sont bien des sites du rseau lilnet ! e e

1.4.3

La table des services (/etc/services)

Elle est prsente dans le chapitre suivant. e e

1.4.4

Manipulation des adresses Internet

La manipulation des adresses Internet est facilite par les macros suivantes dnies dans : e e

6

CHAPTER 1. LE CONCEPT DINTERNET /* Definitions of bits in internet address integers. */ #define #define #define #define #define #define #define #define #define #define #define #define IN IN IN IN IN IN IN IN IN IN IN IN CLASSA(i) (((long)(i) & 0x80000000) == 0) CLASSA NET 0xff000000 CLASSA NSHIFT 24 CLASSA HOST 0x00ffffff CLASSB(i) (((long)(i) & 0xc0000000) == 0x80000000) CLASSB NET 0xffff0000 CLASSB NSHIFT 16 CLASSB HOST 0x0000ffff CLASSC(i) (((long)(i) & 0xe0000000) == 0xc0000000) CLASSC NET 0xffffff00 CLASSC NSHIFT 8 CLASSC HOST 0x000000ff

1.4.5

Conversions machine/rseau e

Internet dnit un ordre rseau standard des octets constituant les entiers 32 et 16 bits dune e e adresse Internet ou dun numro de port. Cet ordre nest pas forcment identique a celui de la machine. e e ` Les primitives suivantes eectuent les conversions ncessaires et sont a utiliser avec gethostent et e ` getservent. #include #include netlong = htonl(hostlong) ; u long netlong, hostlong ; netshort = htons(hostshort) ; u short netshort, hostshort ; hostlong = ntohl(netlong) ; u long hostlong, netlong ; hostshort = ntohs(netshort) ; u short hostshort, netshort ; Exercice : crire une commande lh (listhost) qui tant donn un nom de rseau ache la liste e e e e des machines de ce rseau (ide, consulter la table des rseaux puis celle des machines). e e e

Chapter 2 Adresses de la couche transportOn trouvera des informations utiles dans le manuel en ligne (man 4 avec intro, unix, inet, ip, tcp, udp) ainsi que dans les chiers dinclusions mentionns. e Un point dacc`s (SAP) de la couche transport est ce qui permet a une application dutiliser le e ` rseau pour envoyer ou recevoir des donnes. e e Pour que deux applications puissent changer des donnes via la couche transport, chacune delle e e doit avoir acc`s a un SAP et tre en mesure dadresser le SAP de lautre. e ` e Le format dune adresse de SAP dpend de la famille de protocole utilise. Par exemple, pour les e e communications intra-Unix, une adresse de SAP est implante comme une entre dans le syst`me de e e e chiers, par contre, dans Internet, une adresse de SAP est un couple (adresse Internet, numro de e port). Un numro de port est un entier qui identie un SAP sur une machine pour un protocole partie culier : udp et tcp ont chacun leur propre espace de numros de port. e Le format dadresse est orthogonal a lAPI (Application Programming Interface) utilise pour ` e accder a la couche transport. Par exemple, les adresses de SAP Internet sont implantes par la e ` e mme structure C, quon utilise lAPI des sockets (Unix BSD) ou bien lAPI tli (Unix Syst`me V). e e Un point de transport (socket ou tli) nest accessible de lextrieur que si on lui a associ une e e adresse explicitement (bind() pour lAPI socket) ou implicitement (dans le domaine internet et pour lAPI socket, cette association est faite automatiquement lors dun connect(), dun send(), ...).

2.1

Format gnral des adresses e e

Le format gnral des adresses est dcrit par la structure sockaddr 1 qui a pour seul rle de e e e o permettre lcriture des prototypes des primitives ayant une adresse en param`tre : e e struct sockaddr { short sa family ; /* famille dadresse (AF INET, AF UNIX,...) */ char sa data [14] ; /* contient ladresse effective */ };1 Le nom sockaddr semble indiquer quil sagit dadresse de socket. Il nen est rien puisque ces mme formats e dadresse sont aussi utilisables pour lAPI tli. Cette ambigu e est probablement due a des raisons historiques. t `

7

8

CHAPTER 2. ADRESSES DE LA COUCHE TRANSPORT

En fait, chaque famille de protocoles poss`de son propre format dadresse. Le premier champs de e la structure sockaddr permet justement de distinguer entre ces dirents formats : e

AF INET pour la famille Internet, AF UNIX pour la famille Unix.

2.2

Format des adresses du domaine Unix

Dans le domaine Unix, une adresse est une entre2 dans le syst`me de chiers cre lors de e e ee lexcution du bind(). Par exemple, le chemin /users/graphix/durif/ma_socket. e Dans on trouve le format de ladresse Unix struct sockaddr un { short sun family ; /* AF UNIX ou AF UNSPEC pour casser */ /* une pseudo-connexion datagram */ char sun path [108] ; /* chemin */ };

2.3

Format des adresses du domaine Internet

Dans le domaine Internet, une adresse est un couple form de ladresse Internet de la machine, et e du numro de port. Un numro de port est un identiant unique dans le syst`me et pour un protocole e e e donn (un mme numro de port peut donc dsigner une socket TCP et une socket UDP, ceci est le e e e e cas des services prdnis qui fonctionnent dans les deux protocoles comme sunrpc). e e Dans on trouve le format des adresses Internet.

2

le type dune entre socket est s, celui des rpertoires est d, celui des chiers est -, ... e e

2.4. GESTION DES NUMEROS DE PORT /* Internet address */ struct in addr { union { struct { u char s b1,s b2,s b3,s b4 ; } S un b ; struct { u short s w1,s w2 ; } S un w ; u long S addr ; } S un ; #define s addr S un.S addr /* should be used for all code */ }; struct sockaddr in { short sin family ; /* AF INET ou AF UNSPEC pour casser */ /* une pseudo-connexion datagram */ u short sin port ; /* port de la socket en format reseau */ struct in addr sin addr ; /* adresse internet de la machine en */ /* format reseau (sin addr.s addr est u long) */ char sin zero [8] ; };

9

2.4

Gestion des numros de port e

La famille de protocole Internet dnit un certain nombre de protocoles (ou applications) stane dards comme ftp, telnet et dautres. Dautres protocoles (comme sunrpc) ne font pas partie de la famille Internet bien qutant largement diuss. e e Chacun de ces protocoles utilise un (ou plusieurs) SAP qui doivent tre adressables. Pour cela, e les SAP de ces protocoles sont aects a des numros de port bien connus qui doivent tre identiques e ` e e quel que soit le site qui les implmente. e La table des services rpertorie lensemble de ces protocoles et les numros de port qui leur ont e e ociellement t allous. ee e

2.4.1

La table des services (/etc/services)

Elle met en relation les noms des services ociels (well-known) avec les couples (numros de port, e protocole de transport) (principalement UDP et TCP). Les services sont soit spciques Unix comme e rlogin, rsh, rcp, soit simplement Internet comme ftp, telnet ... linterface shell Il propose le chier /etc/services ou ypcat services. Sur chaque ligne, on trouve le nom symbolique de service, son port ociel et le protocole dans lequel il est disponible. linterface programme Il propose les primitives de GETSERVENT(3N), la structure utilise est e la suivante

10

CHAPTER 2. ADRESSES DE LA COUCHE TRANSPORT struct servent { char *s name ; /* official name of service */ char **s aliases ; /* alias list terminee par NULL */ int s port ; /* port service resides at : format reseau */ char *s proto ; /* protocol to use "udp", "tcp" */ }; les primitives sont #include struct servent *getservent (void), *getservbyname (char *i name, char *i proto) ;

Elles sont similaires a celles de GETHOSTENT mais pour le chier /etc/services. ` Si i_proto est NULL il nest pas pris en compte dans la recherche, sinon cest le nom du protocole souhait (udp, tcp, ...). e Attention : le pointeur renvoy rep`re une zone statique dont le contenu est cras a chaque appel. e e e e` Les numros de port (32 bits) sont fournis dans le format du rseau (cf ntoh et hton), pas dans e e celui de la machine. Exemple tir de ypcat services e ftp telnet sunrpc sunrpc 21/tcp 23/tcp 111/udp 111/tcp

ftp (File Transfer Protocol) est disponible sur le port 21 uniquement avec le protocole TCP. sunrpc (Remote Procedure Call) est disponible sur le port 111 dans les deux protocoles UDP et TCP : chaque protocole poss`de son propre ensemble de numros de port. e e

2.5

Fabriquer des adresses Internet

La fabrication dadresse Internet dpend du contexte : e soit on veut associer une adresse a un SAP local pour le rendre accessible de lextrieur, ` e soit on veut fabriquer ladresse dun SAP loign pour dialoguer avec une application distante. e e Dans ces deux cas il faut garnir les champs de la structure sockaddr_in, cest a dire le numro ` e de port sin_port et ladresse Internet sin_addr. Voici un tableau rsumant la mani`re de garnir le numro de port sin_port. e e e

2.5. FABRIQUER DES ADRESSES INTERNET localisation du SAP local loign e e ociel 21 getservbyname ("ftp") ocieux statique constante > 5000 dynamique 0 serveur de nom

11

nature du service

Dans le cas ociel, on utilisera de prfrence le nom symbolique du service ("ftp" dans le tableau). ee Dans le cas local et ocieux, on utilisera de prfrence lallocation dynamique du numro de port ee e par bind() en donnant 0 dans sin_port ce qui a lavantage dviter tout conit. Linconvnient est e e quil faudra publier ce numro. e Voici un tableau rsumant la mani`re de garnir ladresse internet sin_addr. e e localisation du SAP local loign e e gethostname (moi) gethostbyname(moi) gethostbyname(nom serveur) INADDR ANY Il est videmment prfrable dinterroger symboliquement la table des hosts pour obtenir ladresse e ee Internet dune machine. Dans le cas o` un serveur doit tre accessible par toutes ses interfaces rseau, u e e qui est probablement le plus frquent, on donne la constante INADDR_ANY comme adresse machine. e Voici un utilitaire pour fabriquer une adresse du domaine Internet. void makeAddress (struct sockaddr_in *o_pa, const char *host, int port) { struct hostent *h ; bzero ((char *) o_pa, sizeof (struct sockaddr_in)) ; o_pa -> sin_family = AF_INET ; o_pa -> sin_port = htons (port) ; if ((h = gethostbyname (host)) == 0) { fprintf (stderr, "%s: machine inconnue\n", host) ; exit (1) ; } ; bcopy ((char *) h->h_addr, (char *) &o_pa->sin_addr, h->h_length) ; } Le param`tre port est suppos tre en format machine. Remarquez lutilisation de la macro htons() e ee qui traduit la reprsentation machine de port en reprsentation rseau (Host TO Network Short). e e e Voici un utilitaire pour fabriquer ladresse dun SAP bien connu du domaine Internet. void wellKnwonAddress (struct sockaddr_in *o_pa, const char *host, const char *serv, const char *proto) { struct servtent *s ; if ((s = getservbyname (serv, proto)) == 0) { fprintf (stderr, "%s/%s: service inconnu\n", serv, proto) ;

12 exit (1) ;

CHAPTER 2. ADRESSES DE LA COUCHE TRANSPORT

} ; makeAddress (o_pa, host, ntohs (s->s_port)) ; } Exercice Ecrire un utilitaire similaire pour le domaine Unix.

2.5.1

Utilitaires BSTRING(3)bcopy (const char *source, char *dest, int length) ; bzero (char *zone, int length) ;

Deux utilitaires pour recopier et mettre a zro des cha ` e nes doctets.

Chapter 3 Les socketsOn trouvera des informations utiles dans le manuel en ligne (man 4 avec intro, unix, inet, ip, tcp, udp) ainsi que dans les chiers dinclusions mentionns. e Les sockets (prise ou SAP) sont lincarnation Unix BSD 4.2 des couches rseau et transport e de lOSI. Elles autorisent lutilisation de direntes familles de protocoles et en particulier ceux e de lInternet. Elles permettent une communication inter-processus bidirectionnelle soit sur une mme machine soit a travers le rseau. e ` e Toute communication met en jeu deux sockets qui doivent possder les mmes caractristiques, e e e en particulier elle doivent utiliser la mme famille de protocole et le mme protocole. e e Un processus dsigne par un descripteur (un simple emtier) la socket locale quil a lui-mme e e cre ou dont il a hrit lors dun fork(). En revanche, pour dsigner une socket loigne, il doit ee e e e e e spcier son adresse. e

3.1

Caractristiques dune socket e

Les trois caractristiques dune socket sont son domaine, son type et son protocole. Des sockets e destines a communiquer doivent possder les mmes caractristiques. e ` e e e

3.1.1

Domaines de communication dune socket

Une socket peut tre destine a communiquer avec des sockets qui sont sur la mme machine ou e e ` e sur dautres machines. Domaine Unix (PF UNIX) la socket ne peut communiquer quavec une socket situe sur la mme e e machine, on ne passe pas par le rseau (tout se passe en mmoire centrale). e e Domaine Internet (PF INET) la socket peut communiquer sur la mme machine ou sur dautres e machines par lintermdiaire du rseau. e e A chaque domaine correspond, dune part, un format dadresse (voir chapitre prcdent) et, e e dautre part, une famille de protocoles, do` le nom des constantes (PF comme Protocol Family). En u particulier le domaine PF_INET correspond a la suite des protocoles de lInternet (i.e. TCP/IP). `

3.1.2

Types de communication dune socket

Le type de communication xe les caractristiques logiques de la transmission. e 13

14

CHAPTER 3. LES SOCKETS

datagram (SOCK DGRAM) il ny a pas de circuit virtuel entre les deux sockets et chaque paquet est envoy indpendamment des autres, cest a dire non able, non squenc mais prservation e e ` e e e des fronti`res de paquet. e circuit virtuel (SOCK STREAM) il y a dabord tablissement dune connexion xe, la commue nication fonctionne ensuite comme un pipe Unix, cest a dire able, la squence est prserve, ` e e e pas de doublons mais il ny a pas prservation des fronti`res denregistrement (par exemple, le e e processus metteur peut envoyer 2 octets par 2 octets, et le rcepteur peut lire 3 par 3). e e e e` raw (SOCK RAW) utilise directement le protocole IP (rserv a root). sequenced paquet (SOCK SEQPACKET) cumule les avantages des circuits virtuels et la prservation e des fronti`res. e Le type de communication permet de savoir quels protocoles du domaine sont utilisables pour implanter la communication dsire. e e

3.1.3

Protocole dune socket

Le protocole permet de choisir le protocole dans lensemble des protocoles dnis par le domaine e et le type de communication. Les dirents protocoles existant sont rpertoris dans la table des protocoles (voir GETPROe e e TOENT(3N)). En gnral il nexiste quun protocole par domaine et par type ; dautre part il existe un protocole e e par dfaut dtermin par le domaine et le type de la socket (voir table 3.1). e e e protocole par dfaut SOCK DGRAM SOCK STREAM e PF INET UDP TCP SOCK SEQPACKET ? SOCK RAW IP

Table 3.1: les protocoles par dfaut. e On remarque quil ny a pas de ligne pour le domaine PF UNIX, en eet, la notion de protocole na pas de sens pour les sockets du domaine Unix.

3.2

Primitives gnrales sur les sockets e e

Les primitives qui suivent sont disponibles a la fois pour les sockets SOCK DGRAM et ` e e e e SOCK STREAM. En gnral, leur smantique di`re suivant le type de la socket. En gnral, une fonction renvoie une valeur positive ou nulle en cas de succ`s, une valeur ngative e e e e en cas dchec, dans ce cas on utilise perror(char *) pour imprimer un message appropri. e e Sauf option particuli`re de socket, les primitives connect(), recv(), send(), read(), write() e et accept() sont bloquantes. Lusage des primitives dcrites ci-apr`s est illustr dans les deux chapitres qui suivent. e e e

3.2.1

Crer une socket : socket() e

Cest a la cration dune socket quon xe ses trois caractristiques. ` e e

3.2. PRIMITIVES GENERALES SUR LES SOCKETS #include #include int socket (int domain, int type, int protocol) ;

15

cre une socket (i.e. alloue les ressources syst`mes ncessaires) et renvoie son descripteur, ses e e e caractristiques sont xes par les param`tres, en particulier on prcisera 0 pour protocol an e e e e dobtenir le protocole par dfaut. Exemple : e s = socket (PF_UNIX, SOCK_DGRAM, 0) ;

3.2.2

Dtruire une socket : close() eclose (int s) ;

ferme la socket dsigne par s et restitue les ressources associes au syst`me. On voit ici que la e e e e socket se comporte comme un simple descripteur de chier ; ceci sera vrai dans certains cas pour dautres primitives et permettra de confondre la notion de socket et celle de chier.

3.2.3

Rduire les fonctionnalits dune socket : shutdown() e eshutdown (int s, int how) ;

Rduit les fonctionnalits de s suivant la valeur de how. e e how fonction 0 criture seule e 1 lecture seule 2 plus rien

3.2.4

Associer une adresse ` une socket : bind() a#include #include bind (int s, struct sockaddr *i saddr, int i saddrlen) ;

Associe ladresse i_saddr a la socket s. i_saddrlen est la taille de la structure *i_saddr eec` tivement passe. Contre toute apparence, tous les param`tres sont en entre 1 . e e e

3.2.5

Consulter ladresse dune socket : getsockname()getsockname (int s, struct sockaddr *o saddr, int *io saddrlen) ;

qui range dans o_addr ladresse associe a s. e `1 en cas dambigu e possible, les noms de param`tres formels sont prcds de i, io ou o suivant quils sont de mode t e e e e in, in out ou out. Par exemple pour la primitive socket il ny a pas dambigu e : tous ses param`tres sont en entre t e e puisque le C ne poss`de que le passage par valeur. Une ambigu e appara lorsquun param`tre est ladresse dune e t t e variable.

16

CHAPTER 3. LES SOCKETS

3.2.6

Exemples dutilisations de bind()

La fabrication dadresse du domaine Unix ne pose aucun probl`me et nest pas illustre. e e Le code suivant pourrait tre le dbut du dmon telnet ; le bind() na aucune initiative a prendre, e e e ` ladresse etant totalement spcie. e e {int s ; struct sockaddr_in snom ; s = socket (PF_INET, SOCK_STREAM, 0) ; { char hostname [MAXHOSTNAMELEN + 1] ; struct servent *serv = getservbyname ("telnet", "tcp") ; gethostname (hostname, MAXHOSTNAMELEN) ; /* nom machine locale */ makeAddress (&snom, hostname, ntohs (serv->s_port)) ; } bind (s, (struct sockaddr *) &snom, sizeof snom) ; ... Dans ce second exemple, cest bind() qui alloue un numro de port. Il faut ensuite le publier e avec printf(). {int s ; struct sockaddr_in snom ; int snom_taille = sizeof snom ; s = socket (PF_INET, SOCK_STREAM, 0) ; { char hostname [MAXHOSTNAMELEN + 1] ; gethostname (hostname, MAXHOSTNAMELEN) ; /* nom machine locale */ makeAddress (&snom, hostname, 0) ; } bind (s, (struct sockaddr *) &snom, sizeof snom) ; getsockname (s, (struct sockaddr *) &snom, &snom_taille) ; printf ("port alloue : %d\n", ntohs (snom.sin_port)) ; ... Linconvnient majeur de cette technique est que le numro de port publi est dirent a chaque e e e e ` excution, il faut alors donner le bon numro aux futurs clients qui voudront se connecter a cette e e ` adresse. Lintroduction dun serveur de noms qui g`re une table de correspondances nom symbolique, e numro de port, permet de supprimer ce probl`me : les dirents intervenants de lapplication nont e e e plus qu` conna le nom symbolique invariable du port avec lequel ils veulent communiquer. a tre La carte hosts du NIS est un exemple de serveur de nom ; malheureusement, si vous ntes pas e super utilisateur (root) vous ne pouvez pas ajouter de nouveaux services dans /etc/services. Dans ce cas, on peut programmer soit mme son propre serveur de numro de port. Supposons e e que ce serveur existe, appelons le snp (comme Serveur de Numro de Port). Voici une partie de e linterface quil propose : /* interface destine aux clients */

3.2. PRIMITIVES GENERALES SUR LES SOCKETS unsigned int snp_consulter (char *nom, char *host) ; /* renvoie le numero de port correspondant a nom sur host */ /* interface destine aux serveurs */ void snp_enregistrer (char *nom, unsigned int port) ; /* enregistre la correspondance (nom.host_local, port) dans la table */ void snp_supprimer (char *nom) ; /* supprime la correspondance (nom.host_local, port) de la table */

17

Lorsquun serveur alloue un numro de port dynamiquement, il le publie aupr`s de snp en lui associant e e un nom symbolique. snp enregistre ce couple (nom symbolique, numro de port) dans sa table. e Lorsquun client dsire contacter un serveur dont il conna le nom symbolique, il interroge snp pour e t rcuprer le numro de port correspondant. On se servira de ces primitives dans la suite. e e e

3.2.7

Connexion de socket : connect()

La connexion permet dindiquer avec quel partenaire unique (socket loigne) les changes ultrieurs e e e e auront lieu ; les primitives send() et write() pourront d`s lors tre utilises et cest le partenaire e e e qui en sera la cible. #include #include connect (int s, struct sockaddr *i peer, int peerlen) ; s est la socket locale, i_peer et peerlen forment ladresse dune socket loigne. e e Si s est de type SOCK STREAM la connexion est obligatoire pour quune communication puisse avoir lieu. Le processus qui excute ce connect() est considr comme un client de la socket loigne e ee e e qui appartient au processus serveur. La demande de connexion dun client naboutit que quand le serveur lhonore avec la primitive accept(), ceci correspond tr`s prcisment a ltablissement dun e e e ` e circuit virtuel. Une fois cr, ce circuit ne peut tre modi, la seule possibilit est de dtruire la ee e e e e socket ainsi connecte (close()). e Si s est de type SOCK DGRAM il ne sagit pas dune connexion mais dune facilit qui permet e dallger les communications ultrieures ; les envois se feront sur i_peer et les rceptions seulement e e e depuis i_peer ; s peut par la suite tre connecte avec un autre partenaire par un nouvel appel a e e ` connect(), ou bien, simplement dconnecte si on spcie AF UNSPEC dans i_peer->sa_family. e e e

3.2.8

Rception de message : recv() e#include #include int recv (int s, char *o buf, int lbuf, int flags) ; int recvfrom (int s, char *o buf, int lbuf, int flags, struct sockaddr *o from, int *io fromlen) ;

18

CHAPTER 3. LES SOCKETS

Ces primitives reoivent sur s un message dune autre socket. recv() ne peut fonctionner que si c s est connecte (connect()). Le message est stock dans le tampon o_buf de taille lbuf, la longueur e e eective du message est renvoye par la primitive. e flags est 0 ou un ou bits a bits dune ou plusieurs des valeurs suivantes ` MSG OOB qui spcie quun message urgent doit tre reu (out-of-band data), seules les sockets e e c SOCK STREAM dans INERNET supportent cette option, MSG PEEK qui spcie une rception non destructive. e e Si o_from nest pas le pointeur NULL, o_from et io_fromlen permettent de rcuprer ladresse e e de lmetteur. e

3.2.9

Emission de message : send()#include #include int send (int s, char *i msg, int lmsg, int flags) ; int sendto (int s, char *i msg, int lmsg, int flags, struct sockaddr *i to, int tolen) ;

Ces primitives envoient le message i_msg, lmsg sur la socket s. Pour send(), s doit tre connecte e e (connect()). i_to et tolen reprsentent ladresse de la socket cible. e flags est 0 ou vaut MSG OOB : le message est urgent (out-of-band), cette derni`re possibilit e e nexiste que sur les sockets SOCK STREAM et PF INET.

3.2.10

Lecture et criture : read(), write() e

Le descripteur dun ordre read() ou write() peut tre une socket a condition quelle soit e ` connecte. e Attention, read() renvoie le nombre doctets lus qui peut tre infrieur au nombre doctets quon e e a demand a lire, pour mener la lecture a son terme il est alors sens de mettre le read() dans une e` ` e boucle qui sarrte d`s quon a lu le nombre doctets souhait. e e e

3.2.11

Attente slective : select() e

Cette primitive est gnrale aux descripteurs dentre sortie dUnix : descripteurs de chier (obe e e tenus par open()), descripteurs de tube (obtenus par pipe()) et descripteurs de socket (obtenus par socket() et accept()). Cette primitive est particuli`rement utile lorsquon a plusieurs descripteurs ouverts et quon ne e sait pas a priori sur lequel une lecture ou une criture sera possible (on ne veut pas xer un ordre a e priori sur les ordres de lecture et/ou dcriture). e Le select() et les macros associes permettent de conna e tre les descripteurs sur lesquels une opration dentre/sortie non bloquante est possible. Cette primitive est bloquante avec la possibilit e e e de xer un dlai de garde (timeout). e

3.2. PRIMITIVES GENERALES SUR LES SOCKETS #include #include int select (int width, fd set *io readfds, fd set *io writefds, fd set *io exceptfds, struct timeval *io timeout) ; FD SET(fd,&fdset) /*positionne le descripteur fd dans le masque fdset*/ FD CLR (fd, &fdset) /* raz du descripteur fd dans le masque fdset */ FD ISSET (fd, &fdset) /* test du descripteur fd dans le masque fdset */ FD ZERO (&fdset) /* raz du masque fdset */ int fd ; fd set fdset ;

19

Un masque de type fd_set reprsente un ensemble de descripteurs, les oprations sur un tel e e ensemble sont lajout et la suppression dun descripteur (macros FD SET et FD CLR), le test dappartenance (FD ISSET) et linitialisation a vide de lensemble (macro FD ZERO). ` En entre, le masque io_readfds (resp. io_writefds et io_exceptfds), contient les descripe teurs pour lesquels select() doit tester la possibilit dune lecture non bloquante (resp. criture et e e vnement exceptionnel). e e En sortie, le mme masque io_readfds (resp. io_writefds et io_exceptfds), contient les dese cripteurs pour lesquels une lecture non bloquante est possible (resp. criture et vnement excepe e e tionnel). Un des trois masques peut tre le pointeur NULL sil est sans intrt. e ee width indique le nombre de bits signicatifs de chaque masque, sa valeur est donne par getdtae blesize(2). io_timeout donne un dlai de garde au select(), si cest le pointeur NULL, lattente nest pas e limite dans le temps. e select() renvoie le nombre total de descripteurs prts, en particulier 0 si le dlai de garde e e (io_timeout) a expir. select() renvoie -1 en cas derreur, en particulier sil a t interrompu par e ee un signal. Dans lexemple suivant, on veut accder au descripteur (fd1 ou fd2) qui, le premier, dispose dune e donne a lire : e ` ... while (1) { fd_set lecture ; FD_ZERO (&lecture) ; FD_SET (fd1, &lecture) ; FD_SET (fd2, &lecture) ; if (select (getdtablesize (), &lecture, NULL, NULL, NULL) >= 0) { if (FD_ISSET (fd1, &lecture)) traiter_1 (fd1) ; else if (FD_ISSET (fd2, &lecture)) traiter_2 (fd2) ; else /* theoriquement impossible puisque timeout infini */

20

CHAPTER 3. LES SOCKETS } else if (errno == EINTR) { /* select a ete interrompu par un signal : reprendre la boucle */ } } Exercice : On remarque que fd1 est favoris par rapport a fd2, comment viter ce favoritisme ? e ` e

3.3

Notion de client et de serveur

Conceptuellement, un client est une entit active qui prend des initiatives de son propre chef, e par exemple il demande a un serveur de lui rendre un service ; en revanche, un serveur est une entit ` e passive qui attend quon lui demande un service. Techniquement, le serveur est le processus qui eectue un bind(), le client est celui qui excute un e connect(). Un serveur est souvent implant comme un processus bouclant indniment et pouvant e e rpondre a de multiples clients. Un client conna ncessairement le serveur quil dsire utiliser, alors e ` t e e quun serveur ne peut prvoir a priori a quels clients il aura a faire. e ` ` En principe, le processus serveur doit tre lanc avant quun client ne lui fasse une demande. e e Un processus peut tre a la fois serveur et client. e ` Voici un module utilInet qui permet la cration de sockets destines a des clients ou des serveurs e e ` dans le domaine Internet : tout dabord utilInet.h #include #include #include #include #include #include

typedef int SOCKETI ; /* socket Internet */ SOCKETI mkClient (int type, char *rhost, char *nom) ; SOCKETI mkServeur (int type, char *nom) ; void delServeur (SOCKETI s, char *nom) ; Limplmentation du module est dans utilInet.c : e #include "utilInet.h" #include "Iaddress.h" #include "snp.h" static void test (int diag, char *mess) { if (diag < 0) { perror (mess) ; exit (1) ; } } static void rendrePublicInet (SOCKETI s, char *nom) { struct sockaddr_in nom_public ; int lg = sizeof nom_public ;

3.3. NOTION DE CLIENT ET DE SERVEUR { char hostname [MAXHOSTNAMELEN + 1] ; gethostname (hostname, MAXHOSTNAMELEN) ; /* nom machine locale */ makeAddress (&nom_public, hostname, 0) ; } test (bind (s, (struct sockaddr *) & nom_public, sizeof nom_public), "rendrePublicInet:bind") ; /* publier le port alloue par bind() */ test (getsockname (s, (struct sockaddr *) &nom_public, &lg), "rendrePublicInet:getsockname") ; snp_enregistrer (nom, nom_public.sinport) ; /* ou, si pas de snp : * printf ("Numero de port alloue: %d\n", ntohs (nom_public.sin_port)) ; */ } static void connecterInet (SOCKETI s, char *rhost, char *nom) { struct sockaddr_in nom_public ; struct hostent *he ; /* creation du nom public de la socket partenaire */ makeAddress (&nom_public, rhost, ntohs (snp_consulter (nom, rhost))) ; test (connect (s,(struct sockaddr*) &nom_public, sizeof nom_public), "connecterInet:connect") ; } SOCKETI mkClient { SOCKETI s ; (int type, char *rhost, char *nom)

21

tester (s = socket (PF_INET, type, 0), "socket()") ; connecterInet (s, rhost, nom) ; return s ; } SOCKETI mkServeur (int type, char *nom) { SOCKETI s ; tester (s = socket (PF_INET, type, 0), "socket()") ; rendrePublicInet (s, nom) ; return s ; }

22

CHAPTER 3. LES SOCKETS

void delServeur (SOCKETI s, char *nom) { snp_supprimer (nom) ; close (s) ; }

3.4

Autres aspects des sockets

fermeture en douceur On peut demander a ce que le close dune socket soit retard (linger) jusqu` ` e a ce que tous les messages en mission aient eectivement t envoys (ou bien jusqu` ce quun e ee e a dlai de garde soit atteint). e struct linger { int l_onoff; /* Linger active */ int l_linger; /* How long to linger for (seconds) */ }; struct linger ling = {1, 10} ; setsockopt (s, SOL_SOCKET, SO_LINGER, &ling, sizeof ling) ; Le verbre anglais linger signie sattarder. donnes urgentes (OOB ou Out Of Band) Possibles avec le send() et le recv() ; avec le ag e ` e MSG_OOB et seulement dans PF INET et en SOCK STREAM (cest a dire avec tcp). Ce mcanisme permet de faire raliser au rcepteur des traitements asynchrones par rapport au ux squentiel e e e des donnes transmises. Le rcepteur est averti de larrive dune donn urgente (en fait un e e e e seul caract`re) par le signal SIGURG. Pour recevoir ce signal le rcepteur doit armer le signal e e (signal()) et demander a la socket de le dclencher lors de larrive dune donne urgente. ` e e e Voici le code de lmetteur : e send (sock, "%", 1, MSG_OOB) ; et le code du rcepteur : e #include #include #include static int socket_a_tester1, socket_a_tester2 ; static void handler_urgent (int sig) { fd_set evenements_urgents ; char buf ; /* rearmer le signal */ signal (sig, handler_urgent) ; FD_ZERO (&evenements_urgents) ; FD_SET (socket_a_tester1, &evenements_urgents) ;

3.4. AUTRES ASPECTS DES SOCKETS FD_SET (socket_a_tester2, &evenements_urgents) ; /* determiner la socket */ select (getdtablesize(), 0, 0, &evenements_urgents, 0) ; if (FD_ISSET (socket_a_tester1, &evenements_urgents)) { recv (socket_a_tester1, &buf, 1, MSG_OOB) ; ... } if (FD_ISSET (socket_a_tester2, &evenements_urgents)) { recv (socket_a_tester2, &buf, 1, MSG_OOB) ; ... } } void main() { /* armer le signal */ signal (SIGURG, handler_urgent) ; ... /* pour recevoir le SIGURG */ fcntl (socket_a_tester1, F_SETOWN, getpid()) ; fcntl (socket_a_tester2, F_SETOWN, getpid()) ; ... }

23

sockets non bloquantes il sagit dune proprit gnrale aux descripteurs dE/S, et donc dispoee e e nible sur les sockets. On lindique avec fcntl (s, F_SETFL, FNDELAY | fcntl (s, F_GETFL, 0)) ; les primitives read(), write(), recv(), send() et accept() ne sont plus bloquantes sur la socket s et renvoient une valeur particuli`re si lopration tait impossible. e e e diusion (broadcasting) La diusion nest possible quen communication datagramme (SOCK DGRAM) et si le processus appartient au super utilisateur, on rend une socket s diusante avec : { int on = 1 ; setsockopt (s, SOL_SOCKET, SO_BROADCAST, &on, sizeof on) ;

24

CHAPTER 3. LES SOCKETS

Chapter 4 Les sockets de type datagram (SOCK DGRAM)En type datagram les primitives ne supportent pas la distinction client/serveur (alors quen circuit virtuel cette asymtrie est tout a fait explicite). En fait la distinction ventuelle entre client et serveur e ` e est purement du domaine de lapplication.

4.1

R`gles gnrales dutilisation des primitives e e e

Une socket peut tre dans un des trois tats suivant : e e anonyme cest son tat initial apr`s sa cration par socket(), e e e publique si elle est associe a une adresse mais nest pas connecte, e ` e connecte elle poss`de ncessairement une adresse et sa socket partenaire est connue du syst`me. e e e e Dans le domaine Internet exclusivement, les primitives connect() et sendto() ont pour eet de bord dassocier une adresse a la socket locale. ` Les r`gles dutilisations sont relativement intuitives : une socket peut recevoir si elle est publique e ou connecte ; elle peut mettre si elle est connecte ou bien si elle utilise sendto(). Voici un e e e diagramme de transition dcrivant lvolution de ltat dune socket. Une primitive est autorise e e e e dans un certain tat si le nouvel tat est dni. e e e bind() connect() sendto() recvfrom() recv() read() send() write() anonyme publique connecte e publique publique connecte e publique publique publique connecte e connecte e connecte e connecte e connecte e connecte e connecte e connecte e

Par exemple, le recv() nopre que sur une socket connecte et la socket ne change pas dtat et le e e e read() ne peut se faire que sur une socket publique ou connecte, toute chose logique. e Attention : dans le domaine Unix, le bind() est obligatoire pour recevoir. 25

26

CHAPTER 4. LES SOCKETS DE TYPE DATAGRAM (SOCK DGRAM)

4.2

Exemples dutilisation des sockets SOCK DGRAM

Pour arer les sources prsents ci-dessous, aucun traitement derreur sur le rsultat des primitives e e e e nest eectu. Il est cependant clair quun programme rel doit dtecter et traiter (perror()) toute e e e erreur due a lexcution dune primitive, ceci vite bien des ennuis en cas de bug. ` e e Les deux premiers exemples prsentent les sources dun client qui envoie un message a un serveur, e ` et du serveur correspondant qui reoit ce message et lenvoie sur sa sortie standard, dabord dans le c domaine Unix puis dans le domaine Internet. Le troisi`me exemple propose un service de question/rponse o` le client envoie une question e e u puis reoit la rponse ; le serveur reoit la question, la pose a loprateur puis renvoie la rponse de c e c ` e e celui-ci. Exemple 1 mission/ rception en datagramme dans le domaine Unix. e e Texte du processus metteur : e 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 /* * emet.c */ #include #include #include #include main (void) { int sock ; struct sockaddr_un nom_public ; char * buf = "un message vers le recepteur ....." ; sock = socket (PF_UNIX, SOCK_DGRAM, 0) ; /* creation du nom de la socket eloignee, puis connexion */ nom_public.sun_family = AF_UNIX ; strcpy (nom_public.sun_path, "socket") ; connect (sock, (struct sockaddr*) &nom_public, sizeof nom_public) ; /* communication : strlen (buf) + 1 pour envoyer le \0 terminateur */ write (sock, buf, strlen (buf) + 1) ; close (sock) ; }

On peut viter la connexion en remplaant les lignes 20 a 23 par : e c ` sendto (sock, buf, strlen (buf) + 1, 0, (struct sockaddr *)&nom_public, sizeof nom_public) ; Texte du processus rcepteur : e /* * recep.c */

4.2. EXEMPLES DUTILISATION DES SOCKETS SOCK DGRAM #include #include #include #include

27

main (void) { int sock ; struct sockaddr_un nom_public ; char buf [1024] ; sock = socket (PF_UNIX, SOCK_DGRAM, 0) /* creation du nom public de la socket, puis connexion */ nom_public.sun_family = AF_UNIX ; strcpy (nom_public.sun_path, "socket") ; bind (sock, (struct sockaddr*) &nom_public, sizeof nom_public) ; /* communication: on recupere evidemment le caractere \0 final */ read (sock, buf, 1024) ; printf ("le recepteur a recu: %s\n", buf) ; close (sock) ; unlink ("socket") ; } Le serveur dtruit sa socket avant de se terminer. e Exemple 2 mission/rception en datagramme dans le domaine Internet. e e Texte du processus metteur : e /* * emet.c: emet host */ #include "utilInet.h" main (int argc, char *argv[]) { SOCKETI sock ; char * buf = "un message vers le recepteur ....." ; if (argc != 2) { printf ("usage: emet host\n") ; exit (1) ; } sock = mkClient (SOCK_DGRAM, argv [1], "service"); send (sock, buf, strlen (buf) + 1, 0) ; close (sock) ;

28 }

CHAPTER 4. LES SOCKETS DE TYPE DATAGRAM (SOCK DGRAM)

Texte du processus rcepteur : e /* * recep.c */ #include "utilInet.h" main (void) { SOCKETI sock ; char buf [1024] ; sock = mkServeur (SOCK_DGRAM, "service") ; read (sock, buf, 1024) ; printf ("le recepteur a recu: %s\n", buf) ; delServeur (sock, "service") ; } Exemple 3 service de question/rponse dans le domaine Internet e Texte du client /* * qr.c: qr noport question * * !!! REMARQUE: le serveur doit tourner sur "homel" * */ #include "utilInet.h" #define HOTE_SERVEUR "homel" main (int argc, char *argv []) ; { SOCKETI sock ; char reponse [1024] ; if (argc != 3) { printf ("usage: qr question\n") ; exit (1) ; } sock = mkClient (SOCK_DGRAM, HOTE_SERVEUR, "qr"); send (sock, argv [2], strlen (argv [2]) + 1, 0) recv (sock, reponse, 1024, 0) < 0) ; printf ("La reponse: %s\n", reponse) ; close (sock) ; } Texte du serveur /*

4.3. COMMENT CONCEVOIR UN NOUVEAU SERVICE * qrserveur.c: qrserveur * * mode demploi: doit etre lance en interactif et sur "homel" */ #include "utilInet.h" #define HOTE_SERVEUR "homel" main (void) {SOCKETI sock ; sock = mkServeur (SOCK_DGRAM, "qr") ; while (1) { struct sockaddr_in client ; int lg = sizeof client ; char question [1024], reponse [1024] ; recvfrom (sock, question, 1024, 0, (struct sockaddr*) &client, &lg) ; printf ("Question: %s\nReponse? ", question) ; scanf (" %[^\n]", reponse) ; sendto(sock, reponse, strlen(s)+1, 0, (struct sockaddr*) &client, lg) ; } }

29

4.3

Comment concevoir un nouveau service

Les deux exemples prcdents sont particuli`rement simples, il ny quasimenent pas de protoe e e cole ! Considrons un exemple plus complexe. Le probl`me consiste a implanter un service de mini e e ` confrence : chaque machine peut possder ce service. Les commandes destines aux utilisateurs sont : e e e show host qui ache le dernier message stock par la machine host, e over host message qui crase le dernier message de la machine host avec message. e La rsolution dun tel probl`me peut passer par plusieurs tapes e e e 1. crire les prototypes des souches (stub) dacc`s au serveur ; les souches sont des sous-programmes e e dont le seul but est de faciliter lutilisation du serveur en cachant les sockets (on obtient ici conf.h). Ces souches sont donc destines exclusivement aux clients chaque souche correspond e a un service. ` 2. dduire des services attendus un protocole de communication avec le serveur (ici le rsultat est e e un chier confd.h), le chier .h obtenu est destin a la fois au serveur et aux fonctions souches e` (voir plus loin). 3. crire le code du serveur (ici confd.c), il sagit dobtenir un excutable qui implante les services. e e 4. crire les corps des souches (on obtient conf.c). e 5. crire le code des commandes clientes (ici show.c et over.c). e Voici ce que ca donne avec le probl`me de confrence : e e 1. les prototypes des souches sont dans conf.h

30

CHAPTER 4. LES SOCKETS DE TYPE DATAGRAM (SOCK DGRAM) /* * conf.h */ void conf_show (char *rhost, char *o_texte) ; void conf_over (char *rhost, char *texte) ; 2. le chier confd.h contient le protocole clients/serveur /* * confd.h */ #define LG_TEXTE 100 #define CONF_NAME "confd" enum COMMANDE { CONF_SHOW, /* commande */ CONF_OVER, /* commande */ CONF_OK, /* compte-rendu */ CONF_ERROR /* compte-rendu */ } ; /* lunique type de mesage transfere entre un client et le serveur */ struct conf_message { enum COMMANDE com ; char texte [LG_TEXTE] ; } ; /* protocole unique (inefficace mais simple): * 1) client >---- message (CONF_SHOW/CONF_OVER) ----> confd * 2) execution du service par confd * 3) cliend = 1 */ { while (1) { int lgeff = send (s, buf, lgbuf, 0) ; if (lgeff == -1) { perror ("envoyer()") ; exit (1) ; } buf += lgeff ; lgbuf -= lgeff ; if (lgbuf == 0) break ; } } void main (int argc, char *argv []) ; { SOCKETI sock ; char * buf = "un message vers le recepteur ....." ; if (argc != 3) { printf ("usage: emet host noport\n") ; exit (1) ; } sock = mkClient (SOCK_STREAM, argv [1], atoi (argv [2])); envoyer (sock, buf, strlen (buf) + 1) ; close (sock) ; } Texte du processus rcepteur (cest le serveur) : e /* * recep.c */ #include "utilInet.h" void recevoir (int s, char *buf, int lgbuf) { while (1) { int lgeff = read (s, buf, lgbuf) ; if (lgeff == -1) { perror ("recevoir()") ; exit (1) ; } if (lgeff != 0 && buf [lgeff-1] == 0) break ; lgbuf -= lgeff ; buf += lgeff ; } }

35

36

CHAPTER 5. LES SOCKETS DE TYPE CIRCUIT VIRTUEL (SOCK STREAM) void main (void) { SOCKETI sockConn ; char buf [1024] ; sockConn = mkServeur (SOCK_STREAM) ; /* a partir dici notez la difference avec la version SOCK_DGRAM */ listen (sockConn, 1) ; { SOCKETI s = accepter (sockConn) ; recevoir (s, buf, 1024) ; printf ("le recepteur a recu: %s\n", buf) ; close (s) ; } close (sockConn) ; } Exercice : adaptez le service de question/rponse pour quil fonctionne en circuit virtuel. e

5.3

Utilisation des sockets SOCK STREAM

Gnralement, un serveur est un dmon qui tourne indniment. Un tel serveur peut servir e e e e plusieurs clients. On peut distinguer plusieurs types de serveurs suivant la mani`re dont les clients e sont servis. serveur squentiel : les clients sont satisfaits les uns a la suite des autres. La boucle dun tel serveur e ` a typiquement la forme suivante : while (1) { int s = accepter (sockConn) ; servir_client (s) ; close (s) ; } serveur parall`le : plusieurs clients client peuvent tre servis simultanment. Le serveur peut tre e e e e mono-processus (utilisation du select()) ou multi-processus (utilisation du fork()). multi-processus cest le plus simple a raliser et aussi le plus co teux, il a la forme suivante : ` e u void recupere (void) { while (wait (0) != -1) ; } void main (void) { ... signal (SIGCHLD, recupere) ; /* pour eviter les zombies */ while (1) { int s = accepter (sockConn) ; if (fork() == 0) {

5.3. UTILISATION DES SOCKETS SOCK STREAM close (sockConn) ; servir_client (s) ; exit (0) ; } close (s) ;

37

} mono-processus il utilise le select() pour determiner le client a servir. Il doit donc grer ` e lensemble des clients en cours de connexion (autrement dit lensemble des sockets connectes), e il a la forme suivante : void main (void) { fd_set clients ; ... FD_ZERO (&clients) while (1) { fd_set lecture = clients ; FD_SET (sockConn, &lecture) ; if (select (getdtablesize (), &lecture, NULL, NULL, NULL) > 0) { if (FD_ISSET (sockConn, &lecture)) { int nouveau_client = accepter (sockConn) ; FD_SET (nouveau_client, &clients) ; } else { honorer_la_demande_de (un_client_pret (&lecture)) ; } } } } static int un_client_pret (fd_set *masque) { unsigned fd = 0 ; while (! FD_ISSET (fd, masque)) fd++ ; return fd ; } On utilise deux ensembles de descripteurs : lecture qui est temporaire et ne sert que pour le select() et clients qui est permanent et permet de mmoriser lensemble des e clients connects. A nouveau, on remarque linquit de la fonction un_client_pret(). e e e Fonctionnellement, la principale dirence entre le mono et le multi-processus est la possibilit e e ou non de partager les donnes entre les clients. e Exercice : faire que le serveur parall`le mono-processus rponde de faon plus quitable a ses e e c e ` clients. Lavantage vident dun serveur parall`le est quun client nest pas bloqu par ceux qui le e e e prc`dent. e e Exercice : adaptez ces schmas de serveurs squentiel, parall`le multi-processus et parall`le monoe e e e processus au type de communication datagramme.

38

CHAPTER 5. LES SOCKETS DE TYPE CIRCUIT VIRTUEL (SOCK STREAM)

5.4

Autres exemples

Pour la conception des exemples suivants, on applique la mthode dcrite prcdemment pour les e e e e sockets datagramme : numration des services, choix du protocole, implantation du serveur et des e e sous-programmes souches.

5.4.1

Un serveur de piles parall`le et multi-processus e

Voici un serveur de piles dentiers (on lappelle piled), chaque client dispose de sa propre pile, en eet le serveur est parall`le multi-processus ; cet exemple illustre lutilisation conjointe du accept() e et du fork() et lutilisation bidirectionnelle des sockets. 1. piled.h, le chier dinterface du serveur /* * piled.h: protocole du serveur de piles dentiers */ #define NOM_PILE "piled" enum COMMANDE { EMPILER, /* DEPILER, /* DETRUIRE, /* PILEVIDE /* } ; #define TMESS sizeof 2. piled.c, le code du serveur /* * piled.c: le code du serveur */ #include "utilInet.h" #include "piled.h" #define TPILE 10 struct pile { int som ; int p [TPILE] ; } ; static void servir_un_client (SOCKETI sclient) ; { struct pile p ; p.som = -1 ; while (1) {enum COMMANDE c ; int vide, sortir = 0 ; read (sclient, (char *) &c, sizeof c) ; switch (c) {

un parametre, par de retour */ retour de lancien sommet */ pas de retour */ retour dun entier */ (int) /* un message est un simple entier ! */

5.4. AUTRES EXEMPLES case EMPILER: read (sclient, (char *) &p.p [++p.som], sizeof (int)) ; break ; case DEPILER: write (sclient, (char *) &p.p [p.som--], sizeof (int)) ; break ; case DETRUIRE: sortir = 1 ; break ; case PILEVIDE: vide = p.som == -1 ; write (sclient, (char *) &vide, sizeof vide) ; break ; default: break ; } if (sortir) break ; } close (sclient) ; } main (void) { SOCKETI sockConn ; sockConn = mkServeur (SOCK_STREAM, NOM_PILE) ; listen (sockConn, SOMAXCONN) ; while (1) { SOCKETI sclient = accepter (sockConn) ; if (fork () == 0) { close (sockConn) ; servir_un_client (sclient) ; exit (0) ; } close (sclient) ; } }

39

Exercice 1 : crire la partie souche du serveur (pile.h et pile.c), e Exercice 2 : adaptez ce serveur pour quil soit mono-processus, ainsi, les dirents clients partageront e une seule et mme pile (ceci peut-il avoir un sens pratique ? ?) e Exercice 3 : adaptez ce serveur pour le type de communication datagramme. Exercice : il est possible de rediriger lentre standard dune commande sur une socket avec e dup2(). Faites le pour utiliser la commande more pour acher le contenu dun chier distant avec une commande du genre plus host file.

40

CHAPTER 5. LES SOCKETS DE TYPE CIRCUIT VIRTUEL (SOCK STREAM)

Chapter 6 Le mcanisme XDR (eXternal Data e Representation)On lappelle aussi mcanisme dempaquetage/dpaquetage, de marshalling ou tout simplement e e de transmission. Dans la suite, le terme transmission dsigne a la fois lmission et la rception. e ` e e Le but du mcanisme XDR est double : e permettre a deux machines ayant des reprsentations internes de donnes direntes de se ` e e e comprendre. En eet, que se passe-t-il si un Sun Sparc envoie un nombre ottant vers un Vax avec un bte send() ? Pour cela, le mcanisme XDR propose une reprsentation externe e e e standard des donnes. e permettre la transmission de structures de donnes volues (par exemple a base de pointeurs). e e e ` Les primitives du type de send() et recv() ne permettent de transmettre que des squences e doctets sans signication particuli`re. e Lacc`s au XDR se fait avec e #include

6.1

Gnralits sur le mcanisme XDR e e e e

Lobjet central du mcanisme XDR est le ot XDR de type XDR. En fonctions des param`tres e e xs lors de sa cration, un ot XDR ne peut assurer quune fonctionnalit parmi les trois suivantes : e e e lmission, la rception ou la libration de mmoire. Cette derni`re fonctionnalit semble trange, e e e e e e e elle est d e au fait que, lors de la rception, un ot XDR peut tre amen a allouer dynamiquement u e e e` la zone de mmoire rceptrice ; il est alors ncessaire par la suite de librer cette zone. e e e e

6.2

Fonctions de gestion des ots XDR

Avant dtre utilisable, un ot XDR doit tre construit au dessus dun FILE * qui lui-mme est e e e construit au dessus dun descripteur (descripteur de chier, de socket, de mmoire partage, ...). e e

6.2.1

fdopen() et fflush()FILE *fdopen (int fd, char *mode /* "r", "w" ou "r+" */) ; 41

Pour construire un FILE * au dessus dune socket on utilise la fonction :

42

CHAPTER 6. LE MECANISME XDR (EXTERNAL DATA REPRESENTATION)

o` fd est le descripteur de la socket. u Puisquun ot XDR utilise un FILE * qui poss`de un tampon local, il est parfois ncessaire de e e vider ce tampon explicitement an de sassurer quune donne a bien t envoye. Pour cela, on e ee e utilise la fonction int fflush (FILE *f) ; qui vide le tampon sur le descripteur associ. e

6.2.2

xdrstdio create()void xdrstdio create (XDR *o xdrs, FILE *filep, enum xdr op op) ;

cre dans o_xdrs un ot XDR. Les futures oprations de lecture (rception) ou dcriture e e e e (mission) auront lieu sur le ot dentre sortie standard filep. Le param`tre op xe le fonctionnee e e ment du ot : XDR ENCODE le ot fonctionne en mission, il traduit la reprsentation interne des objets dans e e leur reprsentation standard externe, e XDR DECODE le ot fonctionne en rcption, il traduit la reprsentation externe standard des e e e objets dans leurs reprsentations interne. Il peut y avoir allocation dynamique de la zone de e mmoire destine a lobjet reu. APRES ALLOCATION, CETTE ZONE EST AUTOMAe e ` c TIQUEMENT MISE A ZERO, ceci est indispensable lorsque lobjet contient lui-mme des e pointeurs qui doivent eux aussi tre allous. e e e e e e e ` XDR FREE la fonction lib`re la mmoire prcdemment alloue lors de la a un objet, *pobjet est considr comme un param`tre donne/rsultat. Ceci permet de librer la mmoire alloue ee e e e e e e lors dun dcodage antrieur. e e Les constantes prcdentes sont dnies dans le chier rpc/xdr.h. e e e

6.2.3

xdr destroy()void xdr destroy (XDR *xdrs) ;

vide le tampon du ot dentre sortie standard sous-jacent (fflush()) puis dtruit le ot XDR e e lui-mme. Attention, un fclose() reste ncessaire pour fermer le ot dentre sortie standard, ainsi e e e quun close() pour le descripteur.

6.3

Fonctions de transmission XDR

Ce sont les fonctions de transmission o` simplement fonctions XDR qui assurent lencou dage/dcodage dobjets sur un ots XDR. e Ces fonctions renvoient vrai si la transmission sest correctement droule et faux si une erreur e e sest produite. Certaines de ces fonctions ont un param`tre qui est lui-mme une fonction XDR, le type de ce e e param`tre est xdrproc_t qui est dni par : e e typedef bool t (*xdrproc t) (XDR *, void *) ;

6.3. FONCTIONS DE TRANSMISSION XDR

43

Puisquune unique fonction XDR sert a la fois pour lmission et la rception, lobjet quelle ` e e transmet doit toujours tre un pointeur : e bool t xdr TYPE OBJET (XDR *x, TYPE OBJET *po) ; Les fonctions de transmission se rpartissent en deux groupes : celles qui ne font jamais aucune e allocation dynamique pour lobjet a recevoir et celles qui en font ventuellement une. ` e

6.3.1

Fonctions de transmission sans allocation dynamique

Ces fonctions de transmissions ne font pas dallocation dynamique car elles sappliquent a des ` donnes de taille xe et connue par avance. e 6.3.1.1 xdr void() bool t xdr void(void) ; cette fonction indique labsence de param`tre : elle ne transmet rien. e 6.3.1.2 xdr int() bool t xdr int(XDR *xdrs, int *ip) ; une fonction de ce genre existe pour chacun des types de base de C (char, short, long, float, double, bool_t, u_int, ...). 6.3.1.3 xdr opaque() bool t xdr opaque (XDR *xdrs, char *cp, u int cnt) ; transmet telle quelle une donne opaque dsigne par cp de longueur cnt. Elle est utilise par e e e e un programme qui a besoin de stocker une information mais qui ne la consulte jamais lui-mme, par e exemple les handles de chiers NFS sont des informations opaques pour les clients NFS. 6.3.1.4 xdr union() #define NULL xdrproc t ((xdrproc t)0) struct xdr discrim { int value ; xdrproc t proc ; }; bool t xdr union(XDR *xdrs, int *dscmp, char *unp, struct xdr discrim *choices, bool t (*defaultarm) () /* may equal NULL */ );

44

CHAPTER 6. LE MECANISME XDR (EXTERNAL DATA REPRESENTATION)

pour traduire des unions avec discrimant, dscmp rep`re le discrimant, unp lunion, choices est e une table dont chaque entre contient une valeur de discrimant et le pointeur de fonction XDR e correspondant, la derni`re entre sert de sentinelle : son pointeur de fonction est NULL ; la fonction e e XDR defaultarm sera utilise si la valeur pointe par dscmp nest pas trouve dans choices. Par e e e exemple : enum type {ENTIER, FLOTTANT} ; struct exemple { enum type t ; union { int eval ; float fval ; } val ; } struct xdr_discrim choices [] = { {ENTIER, xdr_int }, {FLOTTANT, xdr_float }, {0, NULL_xdrproc_t} } ; bool_t xdr_exemple (XDR *xdrs, struct exemple *e) { return xdr_union (xdrs, &e->t, (char *) &e->val, choices, NULL) ; }

6.3.2

Fonctions de transmission avec allocation dynamique ventuelle e

Ces fonctions de transmissions sapplique a des donnes de taille variables (comme des listes ` e cha ees) ou non connues par avance. n Il est alors possible de leur demander, du ct rcepteur, dallouer dynamiquement les donnes oe e e reues. Cette allocation dynamique na lieu que si le pointeur sur la zone est nul ; la zone alloue est c e immdiatement mise a zro avant que son contenu ne soit reu (ce qui autorise la transmission de e ` e c donnes rcursives). e e 6.3.2.1 xdr string() typedef char *string ; bool t xdr string (XDR *xdrs, string *ps, u int maxsize) ; transmet une cha de caract`res C pas plus longue que maxsize. ne e 6.3.2.2 xdr wrapstring() typedef char *wrapstring ; bool t xdr wrapstring (XDR *xdrs, wrapstring *pws) ; comme xdr_string() mais sans limitation sur la taille de la cha ne.

6.3. FONCTIONS DE TRANSMISSION XDR 6.3.2.3 xdr array() typedef bool t (*xdrproc t)(XDR *, caddr t *) ; typedef elem array [] ; bool t xdr array(XDR *xdrs, array *pa, u int *effsize, u int maxsize, u int elemsize, xdrproc t xdr elem );

45

effsize indique le nombre eectif dlments du tableau *pa et doit tre infrieur a maxsize, ee e e ` xdr_elem est lXDR a appliquer a chacun des lments du tableau, la taille dun lment est ` ` ee ee elemsize. Remarquons que elemsize nest pas transmis puisque cest une information spcique e a la machine locale. `

6.3.2.4

xdr bytes() typedef char bytes [] ; bool t xdr bytes(XDR *xdrs, bytes *pb, u int *effsize, u int maxsize) ;

comme xdr_array() sauf que cette fois le type des lments est x : cest loctet (char), il nest ee e donc pas ncessaire de spcier la taille dun lment ni la fonction XDR correspondante. e e ee

6.3.2.5

xdr reference()

Pour transmettre un pointeur qui doit tre non nul lors de lmission. e e typedef objet *POINTEUR NON NULL ; bool t xdr reference(XDR *xdrs, POINTEUR NON NULL *pp, u int objetsize, xdrproc t xdr objet ); transmet, avec la fonction XDR xdr_objet, lobjet dsign par le pointeur *pp. ATTENTION, e e lors de lencodage, cette primitive ne reconna pas le pointeur NULL, cest a dire que la zone repre t ` ee par *pp sera eectivement transmise mme si elle est a ladresse 0. Lors du dcodage, *pp peut tre e ` e e NULL, dans ce cas, la primitive alloue dynamiquement lobjet, objetsize donne alors la taille de zone mmoire a allouer. e `

6.3.2.6

xdr pointer()

Pour transmettre des donnes rcursives : un pointeur nul est correctement interprt. e e ee

46

CHAPTER 6. LE MECANISME XDR (EXTERNAL DATA REPRESENTATION) typedef objet *POINTEUR ; bool t xdr pointer(XDR *xdrs, POINTEUR *pp, u int objetsize, xdrproc t xdr objet );

comme xdr_reference() sauf quelle traite correctement le cas o` *pp est NULL en mission, u e et peut donc servir a transmettre des structures rcursives. ` e

6.3.3

Libration de zone xdr free() etypedef ... objet ; void xdr free (xdrproc t xdr objet, objet *po) ;

cette fonction applique lXDR xdr_objet passe en param`tre pour librer lobjet repr par le e e e ee pointeur *po ; ATTENTION, xdr_objet doit tre une fonction a deux param`tres XDR* et objet*. e ` e Par exemple on peut librer une cha de la mani`re suivante : e ne e char *s = NULL ; xdr_wrapstring (&xdrs, &s) ; /* reception avec allocation dans s */ ... xdr_free (xdr_wrapstring, &s) ; ... Exercice corrig : crire le source suppos de xdr_array(). e e e #define XDR(xdr_fun, objet) \ { \ if (! (* xdr_fun) (xdrs, (objet))) return FALSE ; \ } bool_t xdr_array_suppose (XDR *xdrs, char **ppo, u_int *sizep, u_int maxsize, u_int elsize, xdrproc_t elproc) { char *ptr ; unsigned cpt ; XDR (xdr_u_int, sizep) ; if (*sizep > maxsize) return FALSE ; if (xdrs->x_op == XDR_DECODE && *ppo == 0) { *ppo = calloc (*sizep, elsize) ; /* la zone est mise a 0 */ } for (ptr = *ppo, cpt = 0 ; cpt < *sizep ; ptr += elsize, cpt++) { XDR (elproc, ptr) ;

6.4. UN EXEMPLE COMPLET } if (xdrs->x_op == XDR_FREE) free (*ppo) ; return TRUE ; } Exercice : crire le code de xdr_pointer(). e bool_t xdr_pointer(XDR *xdrs, char **ppo, u_int osize, xdrproc_t xdr_o) { bool_t present ; switch (xdrs->x_op) { case XDR_ENCODE : present = *ppo != 0 ; XDR (xdr_bool, &present) ; if (present) XDR (xdr_o, *ppo) ; break ; case XDR_DECODE : XDR (xdr_bool, &present) ; if (present) { if (*ppo == 0) { *ppo = malloc (osize) ; bzero (*ppo, osize) ; } XDR (xdr_o, *ppo) ; } else *ppo = 0 ; break ; case XDR_FREE : if (*ppo != 0) { XDR (xdr_o, *ppo) ; free (*ppo) ; *ppo = 0 ; } break ; } return TRUE ; }

47

6.4

Un exemple complet/* * exdr.c: emetteur avec XDR (usage : exdr rhost) */

Toujours le mme de service dimpression dun message a lcran. e ` e

48

CHAPTER 6. LE MECANISME XDR (EXTERNAL DATA REPRESENTATION) #include #include #include "utilInet.h" void main (int argc, char *argv[]) { int sock ; char *b = "un message ... vers recepteur" ; XDR x ; FILE *f ; if (argc != 2) { fprintf (stderr, "usage : exdr rhost\n") ; exit (1) ; } sock = mkClient (SOCK_DGRAM , argv [1], "impression") ; shutdown (sock, 0) ; xdrstdio_create (&x, fdopen (sock, "w"), XDR_ENCODE) ; xdr_wrapstring (&x, &b) ; xdr_destroy (&x) ; /* effectue le fflush() */ fclose (f) ; close (sock) ; } /* * rxdr.c: recepteur avec XDR (usage : rxdr) */ #include #include #include "utilInet.h" void main (void) { int sock = mkServeur (SOCK_DGRAM, "impression") ; XDR x ; FILE *f ; shutdown (sock, 1) ; xdrstdio_create (&x, fdopen (sock, "r"), XDR_DECODE) ; while (1) { char *b = NULL ; xdr_wrapstring (&x, &b) ; fprintf (stderr, "le recepteur a recu: %s\n", b) ; xdr_free (xdr_wrapstring, &b) ; sleep (2) ; } xdr_destroy (&x) ;

6.5. CONSTRUCTION DE NOUVELLES FONCTIONS XDR fclose (f) ; close (sock) ; }

49

Exercice 1 : adapter ce qui prc`de au SOCK STREAM, e e Exercice 2 : tranformer rxdr en serveur de question/rponse, ne pas oublier les fflush(), le e serveur doit ventuellement conna le client qui a envoy une question pour pouvoir lui renvoyer e tre e la rponse (ajouter linformation ncessaire dans le message transmis), il est possible de connece e ter/dconnecter la socket sous-jacente puisquelle est en datagram. e

6.5

Construction de nouvelles fonctions XDR

On fabrique de nouvelles fonctions de transmission en combinant les fonctions prdnies ; ces e e nouvelles fonctions doivent respecter le prototype gnral des fonctions XDR. e e

6.5.1

XDR de structures

Par exemple, pour transmettre des nombres complexes typedef struct complexe { float r, i ; } complexe ; on implante lXDR bool_t xdr_complexe (XDR *xdrs, complexe *pc) { return xdr_float (xdrs, &pc->r) && xdr_float (xdrs, &pc->i) ; }

6.5.2

XDR de tableau de taille variable

Le tableau est a lextrieur de la structure et il peut donc tre allou si on est en dcodage. ` e e e e #define TAILLE_MAX 10 typedef struct tableau_flexible { u_int nb ; /* nombre delements de t t, &tc->nb, TAILLE_MAX, sizeof (complexe), xdr_complexe) ; }

50

CHAPTER 6. LE MECANISME XDR (EXTERNAL DATA REPRESENTATION)

6.5.3

XDR de structures contenant un tableau

Un exemple un peu plus compliqu a cause du tableau p qui appartient a la structure : pour e ` ` transmettre des piles struct pile { int nb ; complexe p [20] ; } ; La solution suivante est en fait errone, puisque si on utilise un xdr_free (xdr_pile, ...), le e tableau qui est dans la structure sera libr ! ee bool_t xdr_pile (XDR *xdrs, struct pile *p) { complexe *pc = p->p ; /* xdr_array() a besoin dun DOUBLE pointeur */ /* on ne transmet que les "cases pleines" de la pile */ return xdr_array (xdrs, &pc, &p->nb, 20, sizeof (complexe), xdr_complexe) ; } Une solution raisonnable consiste a ne pas utiliser xdr_array(). On crit directement une boucle ` e sur le tableau : bool_t xdr_pile (XDR *xdrs, struct pile *p) { int i ; /* on ne transmet que les "cases pleines" de la pile */ if (! xdr_int (xdrs, &p->nb)) return 0 ; for (i = 0 ; i < p->nb ; i++) { if (! xdr_complexe (xdrs, &p->p [i])) return 0 ; } return 1 ; }

6.5.4

XDR de liste cha ee n

Pour transmettre une liste cha ee, le plus simple est dcrire une XDR rcursive, par exemple : n e e typedef struct elem *liste ; bool_t xdr_liste (XDR *xdrs, liste *pl) ; typedef struct elem { complexe c ; liste s ; } elem ; bool_t xdr_elem (XDR *xdrs, elem *pe) { return xdr_complexe (xdrs, &pe->c) && xdr_liste (xdrs, &pe->s) ; }

6.5. CONSTRUCTION DE NOUVELLES FONCTIONS XDR

51

bool_t xdr_liste (XDR *xdrs, liste *pl) { /* rappelons-nous que si on est en decodage et que *pl est nul * xdr_pointer() alloue la zone receptrice puis la met a zero * ce qui assure que les elements suivant seront correctement alloues. */ return xdr_pointer (xdrs, pl, sizeof (elem), xdr_elem) ; } Le malaise vient ici de lexploration rcursive de la liste qui peut tre longue. Lalgorithme non e e rcursif ci-dessous utilise le double pointeur ps qui rep`re toujours le champ s du dernier lment e e ee transmis (encod, dcod ou libr) : e e e ee typedef struct elem *liste ; bool_t xdr_liste (XDR *xdrs, liste *pl) ; typedef struct elem { complexe c ; liste s ; } elem ; bool_t xdr_elem (XDR *xdrs, elem *pe) ; { return xdr_complexe (xdrs, &pe->c) ; /* on ne transmet plus le pointeur */ } bool_t xdr_liste (XDR *xdrs, liste *p) { while (1) { if (xdrs->x_op == XDR_ENCODE || xdrs->x_op == XDR_DECODE) { if (! xdr_pointer (xdrs, p, sizeof (elem), xdr_elem)) return FALSE ; if (*p == 0) return TRUE ; p = & (*p)->s ; } else if (xdrs->x_op == XDR_FREE) { if (*p != 0) { liste old = *p ; *p = (*p)->s ; if (! xdr_pointer (xdrs, &old, sizeof (elem), xdr_elem)) return FALSE ; } else return TRUE ; } } } Exercice les param`tres traditionnels int argc et char *argv[] de toute fonction main() e reprsentent la commande dont on a demand lexcution au syst`me. Ceci peut tre une mani`re de e e e e e e mmoriser une commande. Ecrire les structures de donnes permettant de reprsenter un historique e e e

52

CHAPTER 6. LE MECANISME XDR (EXTERNAL DATA REPRESENTATION)

de commandes, et les fonctions XDR associes. e

Chapter 7 Appel de procdure loigne SunOS 4.x e e e (RPC)7.1 Rappel

Les appels de procdures loignes, ou RPC (Remote Procedure Call), permettent la commue e e nication interprocessus en utilisant un outil bien connu de la programmation structure : le souse programme. Les deux processus ainsi mis en communication peuvent tourner sur une mme machine ou sur e deux machines relies par un rseau. e e En gnral, le processus qui fait lappel de procdure est appel client, le processus qui excute le e e e e e corps de la procdure est appel serveur, une procdure fournie par un serveur est appele service. e e e e Un serveur peut fournir plusieurs services. Un appel RPC se droule comme suit : lobjet donne est convoy par le rseau du client au e e e e serveur, le service construit lobjet rsultat puis le renvoie vers le client. e Ces objets sont transmis avec le mcanisme XDR. e En r`gle gnrale, les appels de procdure sont donc synchrones. e e e e

7.27.2.1

Architecture des RPC de SunOS 4.xSituation des RPC

Le fonctionnement des RPC est bas sur les sockets : les passages de param`tres utilisent des e e sockets connectes entre le client et le serveur. Les RPC correspondent a la couche session du mod`le e ` e de lOSI.

7.2.2

Nommage des services RPC

Sous SunOS 4.x, les services sont dsigns par les cinq informations suivantes : e e nom de la machine supportant le serveur, nom du serveur (ou programme), version du programme : plusieurs versions dun mme serveur peuvent coexister sans ambigu e, e t service demand (un serveur propose plusieurs services), e protocole utilis par la (les) socket(s) sous-jacente(s). e 53

54

CHAPTER 7. APPEL DE PROCEDURE ELOIGNEE SUNOS 4.X (RPC)

Les noms de serveur sont en fait des numros, les serveurs ociels comme NFS (Network File e System) poss`dent des numros quil ne faut pas rutiliser. Voici une table rsumant les 4 tranches e e e e de numros disponibles. e limites de la tranche usage 0x0000 0000 a 0x1FFF FFFF serveurs ociels enregistrs par Sun ` e 0x2000 0000 a 0x3FFF FFFF ` libre 0x4000 0000 a 0x5FFF FFFF ` rserv aux serveurs transitoires e e 0x6000 0000 a 0xFFFF FFFF ` rserv par Sun e e Les serveurs transitoires utilisent des numros allous dynamiquement et ne peuvent donc tre connus e e e qu` lexcution. Par exemple, si un serveur doit appeler une procdure de son client (il sagit dun a e e rtro appel), le client demande un numro de programme transitoire puis le communique a son e e ` serveur, celui-ci peut ensuite appeler le client en utilisant ce numro. e Les numros de des serveurs ociels se trouvent dans les chiers du rpertoire e e /usr/include/rpcsvc. Par exemple on trouve dans rusers.h : #define RUSERSPROG 100002 qui est le numro du serveur RUSERS, et dans nfs_proto.x crit en rpcgen : e e program NFS_PROGRAM { version NFS_VERSION { void NFSPROC_NULL(void) = 0 ; ... } = 2; } = 100003; ce qui indique que le programme NFS poss`de le numro 100003, il ny a que la deuxi`me version e e e (NFS VERSION) qui propose, entre autres, le service NFSPROC NULL de numro 0. e Un tel service de numro 0 sans param`tre et ne retournant aucune valeur devrait en principe e e tre fourni par tout serveur ; il permet de tester la prsence du serveur et est utilis par la commande e e e rpcinfo. Un serveur RPC peut tre disponible avec un des protocoles TCP et UDP ou les deux. e

7.2.3

Le processus portmap

Chaque machine susceptible de proposer des serveurs RPC doit supporter le dmon portmap qui e g`re la table des serveurs RPC disponibles sur la machine. Une entre de cette table a la forme e e suivante : [numero de programme, numero de version, protocole, numero de port] La commande rpcinfo -p imprime le contenu du portmap local. Le portmap fournit des services permettant lutilisation de sa table : crer une ent