Linux Principes et Programmation 5. Programmation réseau Socket, RPC CNAM NSY103 2009/2010 Yann GAUTHERON Extraits : http://fr.wikipedia.org/, LINUX Programmation système et réseau (Joëlle DELACROIX) http://www.cnam.fr/
Modèle clients serveurs Notions Interconnexion de réseaux Quelques principes de base Programmation socket Sous linux Interfaces Différents modes de communications (connecté, non connecté) Appel de procédure à distance (RPC) Mécanisme et principe Les difficultés
Programmation réseau Modèle client serveur
Interconnexion de réseau
Interconnexion de réseau Structure hiérarchique fondée sur le domaine (DNS) objet.sous-domaine.domaine ex: fermi.cnam.fr objet : nom d'une machine. domaine : géographique (fr, jp) ou institutionnel (com, mil, edu, name) Adresses IP : 32 bits Paire (adresse réseau, adresse machine dans le réseau) Forme pointée : 10000000 00001010 00000010 00011110 128.10.2.30
Interconnexion de réseau IP : adressage de machine à machine via adresse IP TCP/UDP : adressage d'applications à applications notion de ports (entier 16 bits)
Interconnexion de réseau : couche UDP User Datagram Protocol Protocole transport de bout en bout Adressage d'application à application via les ports UDP Ports UDP réservés exemple : port 513 application who Protocole transport non fiable basé sur IP Datagrammes indépendants Perte de datagrammes Datagrammes dupliqués Pas de remise dans l'ordre d'émission exemple : le courrier
Interconnexion de réseau : couche TCP Transfert Control Protocol Protocole transport de bout en bout Adressage d'application à application via les ports TCP Ports TCP réservés exemple : port 21 application FTP Protocole transport fiable orienté connexion basée sur IP Connexion / Flux d'octets Pas de perte de messages Pas de duplication Remise dans l'ordre d'émission Analogie : le téléphone
Interconnexion de réseau
Interconnexion de réseau : couche Application
Les processus démons réseaux Durant toutes ces étapes, le processus inetd est toujours actif
Interface socket Interface de programmation (ensemble de primitives) Point de communication (adresse d'application) Compatible SGF
Interface socket : Création sock = socket (af, type, protocole); Famille TCP-IP : AF_INET UNIX : AF_UNIX Type de service : SOCK_STREAM, SOCK_DGRAM ou SOCK_RAW Protocole : IPPROTO_TCP, IPPROTO_UDP ou 0 int socket (af, type, protocole) int af; int type; int protocole; Retourne un descripteur de socket ayant les mêmes propriétés qu'un descripteur de fichier ( héritage ) Accessible par le créateur et les fils de celui-ci
Interface socket : Attachement d'une @ d'application int bind (int sock, struct sockaddr_in *p_adresse, int lg); sock : descripteur de la socket p_adresse : Pointeur en mémoire sur l'adresse, dans domaine TCP/IP struct sockaddr_in lg : Longueur de la structure adresse L'opération d'attachement permet d'étendre le groupe des processus pouvant accéder à la socket
Interface socket : Attachement d'une @ d'application struct sockaddr_in { short sin_family; -- AF-INET ushort sin_port; -- n de port struct in_addr sin_addr; -- @ IP char sin_zero [8]; -- Huit 0 } Le numéro de port est un entier en dehors des numéros de port réservés (>IPPORT_RESERVED). Le champ peut être mis à 0 (Choix laissé au système). L'adresse IP de la machine : constante INADDR_ANY utilisation du fichier /etc/hosts ou serveur de noms (DNS)
Interface socket : Attachement d'une @ d'application /etc/hosts : Correspondance adresse IP et nom symbolique - Adresse IP, nom symbolique, alias, commentaire 163.173.128.6 asimov.cnam.fr cnam iris struct hostent { char *h_name; /* nom de la machine */ char **h_aliases; /* alias */ int h_addrtype; /* type de l'adresse */ int h_length; /* longueur de l'adresse */ char **h_addr_list; /* liste des adresses IP DNS */ #define h_addr h_addr_list[0] /* 1er de la liste IP */ }; Obtenir l'entrée /etc/host de la machine de nom "nom" struct hostent *gethostbyname (nom) char *nom;
Interface socket : Attachement d'une @ d'application
Interface socket : Communication en UDP
Interface socket : Communication en UDP int sendto(sock, msg, lg, option, p_dest, lgdest); int sock; -- socket d'émission char *msg; -- @ zone mémoire contenant le message int lg; -- taille en octets du message int option; -- 0 struct sockaddr_in *p_dest; -- @ du destinataire int lgdest; -- taille de l'adresse du destinataire int recvfrom(sock, msg, lg, option, p_exp, p_lgexp); int sock; -- socket de réception char *msg; -- @ zone mémoire pour recevoir le message int lg; -- taille en octets du message int option; -- 0 ou MSG_PEEK struct sockaddr_in *p_exp; -- @ de l'expéditeur int *p_lgdest; -- taille de l'@ de l'expéditeur
Programmation réseau Interface socket : Communication en UDP #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> #define PORTS 2058 main() { // SERVEUR int sock, lg, n; char buf[20]; struct sockaddr_in adr_s, adr_c; /* Création socket */ sock = socket(af_inet,sock_dgram, IPPROTO_UDP); } /* Attachement socket */ construit_adresse_locale (adr_s, PORTS); - adr = INADDR_ANY bind(sock, &adr_s, sizeof(adr_s)); for (;;) { lg = sizeof(adr_c); n = recvfrom (sock, buf, 20, 0, &adr_c, &lg); printf("chaine reçue %s\n", buf); } close(sock);
Programmation réseau Interface socket : Communication en UDP #include <errno.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> #define PORTS 2058 main() { // CLIENT struct hostent *h; struct sockaddr_in adr_s,adr_c; char buf[20]; int sock; /* création socket */ sock = socket(af_inet, SOCK_DGRAM, IPPROTO_UDP); /* Attachement socket */ construit_adresse_locale (adr_c, 0) ; -- adr_c = INADDR_ANY bind(sock, &adr_c, sizeof(adr_c)); } /* Construction adresse du serveur */ h=gethostbyname("fermi.cnam.fr"); construit_adresse_serveur(adr_s, PORTS, h) ; -- adr_s = h.addr sendto(sock, "bonjour, serveur!! ", 20, 0, &adr_s,sizeof(adr_s)); close(sock);
Interface socket : Communication en TCP
Interface socket : Communication en TCP
Interface socket : Communication en TCP int listen (sock, nb); int sock; -- socket d'écoute int nb; -- connexions pendantes max int accept (sock, p_adr, p_lgadr); -- retourne un descripteur socket de service int sock; -- socket d'écoute du serveur struct sockaddr_in *p_addr; -- @ socket connectée client int *p_lgadr; -- taille de l'adresse int connect (sock, p_adr, lgadr); int sock; -- socket client struct sockaddr_in *p_addr; -- @ socket du serveur avec lequel s'établit la connexion int lgadr; -- taille de l'adresse
Interface socket : Communication en TCP int write (sock, msg, lg); int sock; -- socket locale char *msg; -- @ mémoire de la zone contenant le message int lg; -- taille du message en octets int read (sock, msg, lg); int sock; -- socket locale char *msg; -- @ mémoire de la zone où recevoir le message int lg; -- taille du message en octets
Interface socket : Communication en TCP
Interface socket : Communication en TCP
Programmation réseau Interface socket : Communication en TCP #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> #define PORTS 2058 Main() { // SERVEUR int namelen, sock, nsock, lg, n; char buf[20]; struct sockaddr_in adr_s, adr_c; sock=socket(af_inet,sock_stream,ipproto_tcp);/* Création socket */ construit_adresse_locale(adr_s, PORTS) ; --adr_s = INADDR_ANY bind(sock, &adr_s, sizeof(adr_s)); /* Attachement socket */ listen(sock,5); /* Boucle d'acceptation d'une connexion */ while (TRUE) { /* Attente de question sur la socket */ lg = sizeof (adr_c); nsock = accept(sock, &adr_c, &lg); pid = fork(); if (pid == 0) { close (sock); read(nsock, buf, 20); close (nsock); exit(); } } }
Programmation réseau Interface socket : Communication en TCP #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> #define PORTS 2058 main() { // CLIENT struct hostent *h; int sock; struct sockaddr_in adr_s,adr_c; char buf[20]; Exemple de construction : bzero(&adr_c,sizeof(adr_c)); adr_c.sin_family=af_inet; adr_c.sin_port=htons(port); adr_c.sin_addr.s_addr=inaddr_any; /* Création socket */ sock = socket(af_inet,sock_stream,ipproto_tcp); /* Attachement socket */ construit_adresse_locale (adr_c, 0) ; -- adr_c = INADDR_ANY bind(sock, &adr_c, sizeof(adr_c)); } /* Construction adresse du serveur */ h=gethostbyname("fermi.cnam.fr"); construit_adresse_serveur(adr_s, PORTS, h) ; -- adr_s = h.addr connect(sock, &adr_s, sizeof(adr_s)); write(sock, "bonjour, serveur!! ", 20); close (sock);
Appels de procédures à distance (RPC) C'est un mécanisme général de communication entre processus dans les systèmes répartis. Permettre à un processus de réaliser, au cours de son exécution, un appel de procédure avec ses paramètres et valeurs de retour éventuels, dont l'exécution a lieu sur une autre machine. Protocole défini chez Sun Microsystems, à la base de l'implantation du système de fichiers répartis sous Unix, le protocole NFS (Network File System). Ensemble de services s'appuyant sur l'interface des sockets Paradigme de l'appel de procédure Remplace les primitives (ex : send et receive)
Appels de procédures à distance (RPC) Permettre l'appel de procédures à travers le réseau : Modèle client / serveur Transparence : comme en local Notion de souche/ subrogé (stub) client et serveur
Appels de procédures à distance (RPC) Etapes d'un appel d'une procédure Proc(x,y) à travers le réseau : La souche client intercepte l'appel de procédure local, et transforme la requête émise sur le réseau vers le serveur. Il y a un assemblage (ou marshalling, ou serialization) des paramètres x et y ; Le noyau client transmet sur le réseau (send) ; La souche serveur désassemble les paramètres, et appelle effectivement la procédure souhaitée ; La procédure Proc(x,y) s'exécute et retourne le résultat à la souche serveur ; La souche serveur assemble la réponse ; Le noyau serveur transmet la réponse sur le réseau ; La souche client réceptionne, désassemble le résultat, et le retourne au client en tant que «retour d'appel de procédure locale».
Appels de procédures à distance (RPC) Etapes d'un appel d'une procédure Proc(x,y) à travers le réseau :
Appels de procédures à distance (RPC) Les difficultés rencontrées : Passage des paramètres Comment faire pour les pointeurs? Gestion des pannes De réseaux Du client Du serveur Localisation des services Serveur de désignation
Appels de procédures à distance (RPC) Problème du passage des paramètres net net
Appels de procédures à distance (RPC) Problème des pannes de réseau : les services idempotents
Appels de procédures à distance (RPC) Problème des pannes de réseau : les services idempotents On réémet la demande à expiration d'un timer, mais le service a pu être exécuté! Exemples : Lecture des 1 024 premiers octets d'un fichier Service bancaire : transférer 3 000 euros sur le compte du percepteur Solution : Placer un numéro de séquence sur les opérations. Le serveur refuse de refaire deux fois de suite une demande de service portant un même numéro de séquence.
Appels de procédures à distance (RPC) Problème des pannes du serveur
Appels de procédures à distance (RPC) Problème des pannes du serveur Exécution au moins une fois : la nouvelle demande du client est rééxécutée Exécution au plus une fois : la nouvelle demande du client est rejetée
Interface RPC via les sockets int rpc_call ( char *host, // Name of server host u_long prognum, // Server program number u_long versnum, // Server version number xdrproc_t inproc, // XDR filter to encode arg char *in, // Pointer to argument xdr_proc_t outproc, // Filter to decode result char *out, // Address to store result char *nettype); // For transport selection bool_t rpc_reg( u_long prognum, u_long versnum, u_long procnum, char *procname, xdrproc_t inproc, xdrproc_t outproc, char *nettype); // Server program number // Server version number // server procedure number // Name of remote function // Filter to encode arg // Filter to decode result // For transport selection
Programmation réseau Exemple RCP // Serveur #define RMTPROGNUM (u_long)0x3fffffffl #define RMTPROGVER (u_long)0x1 #define RMTPROCNUM (u_long)0x1 #include <stdio.h> #include <rpc/rpc.h> int *rmtproc(int *param) /* remote procedure */ { static int result; result = *param + *param; return(&result); } main() { int *rmtprog(); } /* Register remote program with RPCBind */ if (rpc_reg(rmtprognum, RMTPROGVER, RMTPROCNUM, rmtprog, xdr_int, xdr_int, "VISIBLE") == -1) { fprintf(stderr, "Could not Register\n"); exit(1); } svc_run(); exit(1);
Programmation réseau Exemple RCP // Client #define RMTPROGNUM (u_long)0x3fffffffl #define RMTPROGVER (u_long)0x1 #define RMTPROCNUM (u_long)0x1 #include <stdio.h> #include <rpc/rpc.h> main() { int inproc=100, outproc; enum clnt_stat rstat;... } /* Service request to host RPCSERVER_HOST */ if (rstat = rpc_call("fermi.cnam.fr", RMTPROGNUM, RMTPROGVER, RMTPROCNUM, xdr_int, (char *)&inproc, xdr_int, (char *)&outproc, "VISIBLE")!= RPC_SUCCESS){ fprintf(stderr,"rpc_call() failed\n"); exit(1); }...