La programmation en Perl: Les trucs du réseau
(Première partie)

by Hal Pomeranz, traduction Philippe Bereski

Cet article traite en plusieurs parties de la programmation réseau en Perl. Celle-ci ressemble de près à la programmation réseau en C, mais la structure de Perl permet de se concentrer sur les fonctions purement réseau et de s'affranchir des aspects de gestion des interruptions et de formatage des données.

Réfléchissons à la programmation réseau

Les discussions sur la programmation réseau sont toujours obscurcies par l'utilisation d'un nombre incroyable d'acronymes mystérieux, pourtant les concepts sont très simples. La plus simple des relations réseau s'établie entre deux hôtes : un serveur, qui traite des données et un client, qui a une requête à faire passer au serveur, d'où la notion de client-serveur.

Le système audio des avions est une bonne illustration de ce principe. Le système audio est le serveur. Il possède une collection de bandes originales de film et de toutes sortes d'autres musiques qu'il propose aux passagers- les clients. Le client demande l'information en connectant le casque musical et en choisissant son morceau sur une roue numérotée.

Dans cet exemple le casque actualise le concept nommé socket par les programmeurs réseau. Les clients établissent une connexion avec la sockette du serveur en branchant le cable au serveur à une adresse bien connue (la prise dans l'accoudoir du siège), avec un numéro de port, celui choisi via la roue numérotée. Ils écoutent leur musique à l'autre bout du pipe représenté par les écouteurs.

Comme dans mon exemple de système audio embarqué, un serveur bien conçu peut traiter simultanément plusieurs clients. Par contre, contrairement à cet exemple, un client peut également s'adresser à plusieurs serveurs ou s'adresser plusieurs fois au même serveur.

Mettons la en oeuvre

Indubitablement la partie la plus compliquée de la création d'une sockette est la construction de la structure de données binaires indiquant au système d'exploitation l'adresse du serveur auquel on souhaite se connecter. Toutes les bidouilles du code ci dessous ne ressemblent pas beaucoup à du Perl. Ceci est du au fait que nous prèparons des structures de données C pour les fonctions réseau directement importées de la bibliothèque socket écrite en C.

     use Socket;
     $server = "www.netmarket.com"; 
     $port = 80;

     $server_addr =(gethostbyname($server))[4];
          $server_struct = pack("S n a4 x8", AF_INET, $port, $server_addr);
     $proto = (getprotobyname(`tcp'))[2]; 	
     socket(MYSOCK, PF_INET, SOCK_STREAM, $proto)|| die "Failed to initialize socket: $!\n";
     connect(MYSOCK, $server_struct) || die "Failed to connect() to server: $!\n"; 
La première ligne de cet exemple ne fait qu'invoquer le module Socket.pm de perl. Ce module définit un certain nombre de constantes utiles pour la suite du programme. Ensuite vient le nom du serveur et le numéro du port auquel le client souhaite se connecter. Notez que le port 80 correspond au port sur lequel, par défaut, le serveur web écoute. Cette partie de l'information peut être passée au programme par ligne d'arguments et ensuite passée en argument à une fonction de votre programme principal.

Pour pouvoir se connecter au serveur, le programme doit traduire le nom humain du serveur (www.netmarket.com) en une adresse réseau. La fonction gethostbyname() recherche le nom du serveur et retourne une liste d'informations : l'adresse réseau du serveur étant la cinquième. (Nous ne nous préocuperons pas des autres pour le moment).

La structure C est crée en appliquant la fonction pack() à cette adresse. Elle possède trois champs : une description du type de l'adresse réseau, le numéro du port et l'adresse du serveur à contacter (le reste de la structure est simplement remplie de zéros). AF_INET est une constante définie dans le module Socket.pm, qui représente les adresses du type de Protocol Internet (IP) (n'oublions pas que de pauvres malheureux doivent programmer pour d'autres réseaux comme AppleTalk, DECnet ou X.25, tous ont leur propre constante AF_* dans Socket.pm). A moins que le programmeur ne spécifie le type de l'adresse dans l'entête de sa donnée, le système d'exploitation n'est pas capable de la décoder et l'établissement de la connexion à la sockette échouera.

Maintenant que nous nous sommes débarrassé de tous ce fatras de pack(), nous allons pouvoir nous concentrer sur l'établissement de la sockette. Tout d'abord le client initialise son côté de la sockette dans une structure de file handle Perl, MYSOCK. Le second argument de la fonction socket() spécifie le type de connexion réseau, c'est à dire la façon dont la sockette va être utilisée et le protocole de transmission. PF_INET est une autre constante provenant du module Socket.pm. Elle est liée à AF_INET et indique que cette sockette sera du type IP ( en fait, au début d'IP, AF-INET était utilisé à la fois dans la structure C et dans la fonction socket(), cette pratique est déconseillée). SOCK_STREAM est une 3ème constante qui indique que la communication se déroulera sur le mode d'une communication téléphonique. Chaque correspondant peut parler en même temps que l'autre et la communication cesse quand l'un des deux raccroche. (SOCK_STREAM est la méthode de communication la plus commune, mais il existe d'autres méthode comme SOCK_DGRAM qui ressemble plus à une communication par signaux, le client et le serveur s'échangent des messages mais il n'y a pas de garantie qu'ils les reçoivent).

Au bout du compte la communication est établie. Ce n'est pas le propos de cet article que de discuter des vertus comparées de TCP et d'UDP. Sachez simplement que TCP est toujours la bonne solution à moins que vous n'ayez de bonnes raisons de choisir UDP. Prenez toujours la peine d'utiliser la fonction getprotobyname(). Les programmeurs paresseux codent cette valeur en dur parce qu'elle est constante sur la majorité des UNIX. Les gens comme moi les maudissent quand ils ont à porter leur code sur des UNIX exotiques ou sur d'autres systèmes.

En tenant fermement un des côtés de la sockette (sous la forme du file handle MYSOCK), le client invoque connect() pour contacter le serveur. La fonction connect() prend comme argument le file handle et la structure C que nous venons d'examiner. En supposant que l'appel à connect() réussisse, le client a établi sa connexion avec le serveur.

Utilisons la

MYSOCK peut maintenant être manipulée comme n'importe quel file handle de Perl avec en plus la possibilité de pouvoir écrire et lire dans la même sockette. Dans ce context d'entrées sorties réseau, il est particulièrement important que vous terminiez vos accès par un close() pour économiser les ressources réseau et système.

Comme ce client s'est connecté au serveur Web www.netmarket.com, souvenez vous du port 80, le programme client peut causer avec le serveur en utilisant le protocole HTTP :

     select(MYSOCK);
     $| = 1;
     select(STDOUT);
     print MYSOCK "GET /\n\n";
     while (<MYSOCK>) { 
          print; 
     } 
     close(MYSOCK);
Les 3 premières lignes arrêtent la bufferisation standard des entrées sorties. Habituellement, il est intéressant de lire et d'écrire dans un fichier par gros paquets (en lisant plus de données que nécessaire ou en regroupant en une seule de nombreuses petites lectures) et la plus part des systèmes UNIX pratiquent cette optimisation par défaut. Cependant cette option peut être désactivée, par exemple dans le cas d'un dialogue client serveur ou les deux parties échangent des petits messages. Le mécanisme de bufferisation et règlé en Perl par la variable $|. Il suffit de la passer à une valeur non nulle (zéro est sa valeur par défaut). L'affectation de cette variable influe sur le file handle du dernier select() effectué (STDOUT est utilisé par défaut). Il faut donc d'abord sélectionner MYSOCK, affecter $|, puis selectionner à nouveau STDOUT.

Ceci étant fait, le client demande un fichier au serveur Web en utilisant la command GET du protocole http. L'argument de la commande GET est le nom du fichier réclamé. Dans ce cas le client demande simplement le fichier racine de l'arbre des documents, mais il aurait tout aussi bien pu demander :

 
     /some/other/file.html.
La requête GET est suivie par 2 lignes vides.

Une fois que le client a passé sa requête, le serveur envoie le contenu du fichier dans la sockette ( ou un message d'erreur si le fichier n'est pas trouvé ou si une autre erreur s'est produite). Le standard HTTP stipule que le serveur coupe la communication après qu'il a envoyé le contenu du fichier. Le client à l'autre bout de la sockette, interprète cette rupture de communication comme une lecture ayant atteint la fin du fichier. Dans le programme précédant, le contenu du fichier est simplement affiché sur le standard output.

Pratiquons la

L'exemple que nous venons de voir couvre les bases de l'écriture d'un programme client. Il y a bien d'autres choses à savoir sur ce sujet, mais il y a également beaucoup de personnes fort bien payées qui n'en savent pas d'avantage. Dans mon prochain article j'aborderai l'écriture d'un serveur et nous verrons un serveur Web simplifié.

En attendant, pratiquez ces concepts en prenant exemple sur ce programe pour écrire une application qui prend sur la ligne de commandes le nom du serveur, le numéro du port, le nom du fichier et qui récupère ce fichier depuis le serveur Web distant. Impressionnez vos amis, et améliorez votre productivité, en écrivant un robot qui surfera sur le Web à la recherche de tag HTML dans les documents que vous téléchargerez puis qui récupèrera également ces documents pointés. Prenez bien garde d'arrêter la recherche à un moment, faute de quoi vous finirez par télécharger tout le Web.

Traduit de ;login: Vol. 21 No. 4, August 1996.


Dernière édition: 23 décembre 1997 phb
Original 11/22/96pc
Back to the original
Retour à l'index