Sophie

Sophie

distrib > * > 2010.0 > * > by-pkgid > a412ceb851151854794ced2a242192bb > files > 2411

howto-html-fr-20080722-1mdv2010.0.noarch.rpm

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>6. Programmation multicast</title>
<link rel="stylesheet" href="style.css" type="text/css">
<meta name="generator" content="DocBook XSL Stylesheets V1.64.1">
<link rel="home" href="index.html" title="Guide pratique du multicast sur les réseaux TCP/IP">
<link rel="up" href="index.html" title="Guide pratique du multicast sur les réseaux TCP/IP">
<link rel="previous" href="ar01s05.html" title="5. Applications multicast">
<link rel="next" href="ar01s07.html" title="7. Fonctionnement interne">
</head>
<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF">
<div class="navheader">
<table width="100%" summary="Navigation header">
<tr><th colspan="3" align="center">6. Programmation multicast</th></tr>
<tr>
<td width="20%" align="left">
<a accesskey="p" href="ar01s05.html">Précédent</a> </td>
<th width="60%" align="center"> </th>
<td width="20%" align="right"> <a accesskey="n" href="ar01s07.html">Suivant</a>
</td>
</tr>
</table>
<hr>
</div>
<div class="sect1" lang="fr">
<div class="titlepage">
<div><div><h2 class="title" style="clear: both">
<a name="id2513680"></a>6. Programmation multicast</h2></div></div>
<div></div>
</div>
<p>
Programmation multicast… ou comment écrire vos propres applications multicast.
  </p>
<p>
Diverses extensions de l'API de programmation sont nécessaires pour utiliser le multicast. Ces extensions sont utilisables par le biais de deux appels systèmes : setsockopt() (utilisé pour envoyer des informations au noyau) et getsockopt() (utilisé pour recevoir des informations du voisinage multicast). Cela ne signifie pas que ces 2 nouveaux appels systèmes ont été ajoutés pour le fonctionnement du multicast. La paire <span class="emphasis"><em>setsockopt() / getsockopt()</em></span> est là depuis des années. Depuis la BSD 4.2 au moins. L'ajout consiste en un nouveau jeu d'options (options multicast) qui est passé à ces appels systèmes et que le noyau doit comprendre.
  </p>
<p>
Les deux lignes suivantes donnent le prototypage des fonctions <span class="emphasis"><em>setsockopt() / getsockopt()</em></span>.
  </p>
<pre class="programlisting">
int getsockopt(int s, int level, int optname, void* optval, int* optlen);
int setsockopt(int s, int level, int optname, const void* optval, int optlen);
</pre>
<p>
Le premier paramètre, <span class="emphasis"><em>s</em></span>, est la prise réseau (socket) à laquelle l'appel système s'applique. Pour le multicast, la prise doit être de la famille <span class="emphasis"><em>AF_INET</em></span> et peut être de type <span class="emphasis"><em>SOCK_DGRAM</em></span> ou <span class="emphasis"><em>SOCK_RAW</em></span>. Le plus courant est l'utilisation d'une prise <span class="emphasis"><em>SOCK_DGRAM</em></span>, mais si votre but est d'écrire un démon de routage ou d'en modifier un, vous aurez plutôt besoin d'une prise de type <span class="emphasis"><em>SOCK_RAW</em></span>.
  </p>
<p>
Le deuxième paramètre, <span class="emphasis"><em>level</em></span>, identifie la couche qui capturera l'option, le message, ou la requète, ou tout ce que vous désirez appeler. De fait, <span class="emphasis"><em>SOL_SOCKET</em></span> est pour la couche de la prise réseau, <span class="emphasis"><em>IPPROTO_IP</em></span> est pour la couche IP, etc. Pour la programmation multicast, la valeur est toujours <span class="emphasis"><em>IPPROTO_IP</em></span>.
  </p>
<p>
<span class="emphasis"><em>optname</em></span> identifie l'option que nous passons ou demandons. Sa valeur, soit fournie par le programme, soit retournée par le noyau, est <span class="emphasis"><em>optval</em></span>. Les valeurs pour <span class="emphasis"><em>optname</em></span> qui peuvent être invoquées pour la programmation multicast sont les suivantes :
  </p>
<p>
   </p>
<div class="informaltable"><table border="1">
<colgroup>
<col align="center">
<col align="center">
<col align="center">
</colgroup>
<tbody>
<tr>
<td align="center" valign="top"> </td>
<td align="center" valign="top">
        setsockopt()
       </td>
<td align="center" valign="top">
        getsockopt()
       </td>
</tr>
<tr>
<td align="center" valign="top">
        IP_MULTICAST_LOOP
       </td>
<td align="center" valign="top">
        oui
       </td>
<td align="center" valign="top">
        oui
       </td>
</tr>
<tr>
<td align="center" valign="top">
        IP_MULTICAST_TTL
       </td>
<td align="center" valign="top">
        oui
       </td>
<td align="center" valign="top">
        oui
       </td>
</tr>
<tr>
<td align="center" valign="top">
        IP_MULTICAST_IF
       </td>
<td align="center" valign="top">
        oui
       </td>
<td align="center" valign="top">
        oui
       </td>
</tr>
<tr>
<td align="center" valign="top">
        IP_ADD_MEMBERSHIP
       </td>
<td align="center" valign="top">
        oui
       </td>
<td align="center" valign="top">
        non
       </td>
</tr>
<tr>
<td align="center" valign="top">
        IP_DROP_MEMBERSHIP
       </td>
<td align="center" valign="top">
        oui
       </td>
<td align="center" valign="top">
        non
       </td>
</tr>
</tbody>
</table></div>
<p>
  </p>
<p>
<span class="emphasis"><em>optlen</em></span> transporte la taille de la structure de données à laquelle <span class="emphasis"><em>optval</em></span> fait référence. Notez que pour <span class="emphasis"><em>getsockopt()</em></span>, c'est un résultat et non une donnée : le noyau écrit la valeur de <span class="emphasis"><em>optname</em></span> dans la zone tampon pointée par <span class="emphasis"><em>optval</em></span> et nous informe de la longueur de la valeur par <span class="emphasis"><em>optlen</em></span>.
  </p>
<p>
Aussi bien <span class="emphasis"><em>setsockopt()</em></span> que <span class="emphasis"><em>getsockopt()</em></span> retournent 0 en cas de succès et -1 en cas d'erreur.
  </p>
<div class="sect2" lang="fr">
<div class="titlepage">
<div><div><h3 class="title">
<a name="id2514065"></a>6.1. IP_MULTICAST_LOOP</h3></div></div>
<div></div>
</div>
<p>
Vous devez décider, en tant que programmeur, si les données que vous voulez émettre doivent être retournées ou non sur votre hôte. Si vous pensez avoir plus d'un processus ou utilisateur en écoute, alors un retour doit être activé. À contrario, si vous émettez des images que votre caméra vidéo produit, vous ne nécessiterez probablement pas de retour, à moins que vous désiriez les voir sur votre écran. Dans ce dernier cas, votre application recevra déjà certainement les images depuis le périphérique connecté à votre ordinateur pour les envoyer à la prise réseau. De ce fait, l'application possède déjà les données, et cela devient improbable que vous vouliez les recevoir de nouveau au travers de la prise réseau.
   </p>
<p>
Il est à noter que le retour est activé par défaut.
   </p>
<p>
Comme <span class="emphasis"><em>optval</em></span> est un pointeur, vous ne pouvez pas écrire :
   </p>
<pre class="programlisting">
setsockopt(socket, IPPROTO_IP, IP_MULTICAST_LOOP, 0, 1);
</pre>
<p>
pour désactiver le loopback vous devez écrire :
   </p>
<pre class="programlisting">
u_char loop;
setsockopt(socket, IPPROTO_IP, IP_MULTICAST_LOOP, &amp;loop, sizeof(loop));
</pre>
<p>
et donnez la valeur 1 à <span class="emphasis"><em>loop</em></span> pour activer le retour et 0 pour le désactiver.
   </p>
<p>
Pour savoir si une prise réseau a activé ou non son retour, utilisez quelque chose comme :
   </p>
<pre class="programlisting">
u_char loop;
int size;
getsockopt(socket, IPPROTO_IP, IP_MULTICAST_LOOP, &amp;loop, &amp;size)
</pre>
</div>
<div class="sect2" lang="fr">
<div class="titlepage">
<div><div><h3 class="title">
<a name="id2514157"></a>6.2. IP_MULTICAST_TTL</h3></div></div>
<div></div>
</div>
<p>
Si rien n'est précisé, les datagrammes multicast sont envoyés avec comme valeur par défaut 1, dans le but d'éviter qu'ils soient transférés sur tout le réseau local. Pour changer la valeur du <span class="emphasis"><em>TTL</em></span> (de 0 à 255), mettez cette valeur dans une variable (ici nommée « ttl ») et écrivez dans votre programme :
   </p>
<pre class="programlisting">
u_char ttl;
setsockopt(socket, IPPROTO_IP, IP_MULTICAST_TTL, &amp;ttl, sizeof(ttl));
</pre>
<p>
Le comportement avec <span class="emphasis"><em>getsockopt()</em></span> est similaire à celui vu avec <span class="emphasis"><em>IP_MULTICAST_LOOP</em></span>.
   </p>
</div>
<div class="sect2" lang="fr">
<div class="titlepage">
<div><div><h3 class="title">
<a name="id2514199"></a>6.3. IP_MULTICAST_IF</h3></div></div>
<div></div>
</div>
<p>
Traditionnellement, l'administrateur système indique l'interface multicast par défaut sur laquelle les datagrammes sont envoyés. Le programmeur peut surcharger cela et choisir ainsi à l'aide de cette option une interface de sortie concrête pour une prise réseau donnée.
    </p>
<pre class="programlisting">
struct in_addr interface_addr;
setsockopt (socket, IPPROTO_IP, IP_MULTICAST_IF, &amp;interface_addr,
            sizeof(interface_addr));
</pre>
<p>
Ainsi, tout le trafic multicast généré par cette prise réseau sera envoyé sur l'interface choisie. Pour revenir au comportement par défaut et laisser le noyau choisir l'interface de sortie (choix basé sur la configuration de l'administrateur système), il suffit d'appeler <span class="emphasis"><em>setsockopt()</em></span> avec la même option et <span class="emphasis"><em>INADDR_ANY</em></span> comme valeur pour l'interface.
    </p>
<p>
Pour connaitre ou sélectionner une interface de sortie, les <span class="emphasis"><em>ioctls</em></span> suivants peuvent être utililisés : <span class="emphasis"><em>SIOCGIFADDR</em></span> (pour obtenir les adresses des interfaces), <span class="emphasis"><em>SIOCGIFCONF</em></span> (pour obtenir la liste de toutes les interfaces) et <span class="emphasis"><em>SIOCGIFFLAGS</em></span> (pour obtenir les fonctionnalités d'une interface et, de fait, permet de déterminer si l'interface supporte le multicast ou non. Fonction indiquée par l'option <span class="emphasis"><em>IFF_MULTICAST</em></span>).
   </p>
<p>
Si un hôte a plus d'une interface et que l'option <span class="emphasis"><em>IP_MULTICAST_IF</em></span> n'est pas mentionnée, alors les transmissions multicast sont émises sur l'interface par défaut, bien que les interfaces restantes puissent être utilisées pour des retransmissions multicast si l'hôte joue le rôle de routeur multicast.
   </p>
</div>
<div class="sect2" lang="fr">
<div class="titlepage">
<div><div><h3 class="title">
<a name="IP-ADD-MEMBERSHIP"></a>6.4. IP_ADD_MEMBERSHIP</h3></div></div>
<div></div>
</div>
<p>
Rappelez vous que vous devez informer le noyau des groupes multicast qui vous intéressent. Si aucun processus n'est intéressé par un groupe donné, les paquets provenant de ce groupe et qui arrivent sur notre hôte sont rejetés. Pour informer le noyau, des groupes qui vous intéressent, et de fait, pour devenir membre de ce groupe, vous devez d'abord remplir une structure <span class="emphasis"><em>ip_mreq</em></span> qui sera passée plus tard au noyau dans le champ <span class="emphasis"><em>optval</em></span> avec l'appel système <span class="emphasis"><em>setsockopt()</em></span>.
   </p>
<p>
La structure <span class="emphasis"><em>ip_mreq</em></span> (provenant de /usr/include/linux/in.h) a les membres suivants :
   </p>
<pre class="programlisting">
struct ip_mreq
{
  struct in_addr imr_multiaddr;   /* adresse IP du groupe multicast */
  struct in_addr imr_interface;   /* adresse IP de l'interface locale */
};
</pre>
<p>
Note : la définition « physique » de la structure est contenue dans le fichier spécifié ci-dessus. Vous ne devez en aucun cas inclure &lt;linux/in.h&gt; si vous souhaitez que votre code soit portable. En lieu et place, incluez &lt;netinet/in.h&gt;, qui inclut à son tour &lt;linux/in.h&gt;.
   </p>
<p>
Le premier membre, <span class="emphasis"><em>imr_multiaddr</em></span>, contient l'adresse du groupe que vous désirez joindre. Souvenez-vous que l'appartenance à un groupe se fait aussi par le biais d'une interface, pas uniquement sur l'adresse du groupe. C'est la raison pour laquelle vous devez fournir une valeur au second membre : <span class="emphasis"><em>imr_interface</em></span>. De cette façon, si vous êtes sur un hôte ayant plusieurs interfaces multicast, vous pouvez joindre un même groupe par le biais de différentes interfaces. Si vous ne désirez pas indiquer d'interface, vous pouvez toujours remplir ce dernier membre avec un joker (<span class="emphasis"><em>INADDR_ANY</em></span>), alors le noyau choisira de lui même l'interface.
   </p>
<p>
Une fois la structure remplie (définie en tant que <span class="emphasis"><em>struct ip_mreq mreq;</em></span>) vous avez juste à appeler <span class="emphasis"><em>setsockopt()</em></span> de cette manière :
   </p>
<pre class="programlisting">
setsockopt (socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, &amp;mreq, sizeof(mreq));
</pre>
<p>
Notez que vous pouvez joindre plusieurs groupes à travers la même prise réseau. Cette limite supérieure est fixée par <span class="emphasis"><em>IP_MAX_MEMBERSHIPS</em></span> et, a pour la version 2.0.33 du noyau Linux, la valeur de 20.
   </p>
</div>
<div class="sect2" lang="fr">
<div class="titlepage">
<div><div><h3 class="title">
<a name="id2514426"></a>6.5. IP_DROP_MEMBERSHIP</h3></div></div>
<div></div>
</div>
<p>
La procédure nécessaire pour quitter un groupe est semblable à celle nécessaire pour le joindre.
   </p>
<pre class="programlisting">
struct ip_mreq mreq;
setsockopt (socket, IPPROTO_IP, IP_DROP_MEMBERSHIP, &amp;mreq, sizeof(mreq));
</pre>
<p>
Où <span class="emphasis"><em>mreq</em></span> est une structure contenant les mêmes données que celles utilisées lorsque ce groupe fût joint. Si le membre <span class="emphasis"><em>imr_interface</em></span> contient la valeur <span class="emphasis"><em>INADDR_ANY</em></span>, alors le premier groupe pouvant correspondre est supprimé.
   </p>
<p>
Si vous avez rejoint un grand nombre de groupes sur une même prise réseau, il n'est pas nécessaire de supprimer toutes les souscriptions pour terminer. Lorsque vous fermez une prise réseau, toutes les souscriptions associées à cette prise sont supprimées par le noyau. Il en est de même si le processus qui a ouvert la prise est tué.
   </p>
<p>
Cependant, gardez à l'esprit que si un processus abandonne la souscription à un groupe, cela n'implique pas forcément l'arrêt de la transmission des datagrammes du groupe en question à l'hôte. En effet, si une autre prise a joint ce groupe avec la même interface avant le <span class="emphasis"><em>IP_DROP_MEMBERSHIP</em></span>, alors l'hôte continuera à être membre de ce groupe.
   </p>
<p>
<span class="emphasis"><em>ADD_MEMBERSHIP</em></span> (aussi bien que <span class="emphasis"><em>DROP_MEMBERSHIP</em></span>) est une opération non-bloquante. Il renvoie un résultat, immédiatement, indiquant s'il a réussi ou échoué.
   </p>
</div>
</div>
<div class="navfooter">
<hr>
<table width="100%" summary="Navigation footer">
<tr>
<td width="40%" align="left">
<a accesskey="p" href="ar01s05.html">Précédent</a> </td>
<td width="20%" align="center"><a accesskey="u" href="index.html">Niveau supérieur</a></td>
<td width="40%" align="right"> <a accesskey="n" href="ar01s07.html">Suivant</a>
</td>
</tr>
<tr>
<td width="40%" align="left" valign="top">5. Applications multicast </td>
<td width="20%" align="center"><a accesskey="h" href="index.html">Sommaire</a></td>
<td width="40%" align="right" valign="top"> 7. Fonctionnement interne</td>
</tr>
</table>
</div>
</body>
</html>