<h3>Les échanges inter-processus</h3>
<p class="spip">Deux programmes ont besoin de communiquer entre eux.</p>

<p class="spip">Pour cela, ils peuvent utiliser plusieurs méthodes (je ne recence ici que les trois plus utilisées mais il en existe d’autres)</p>

<hr>
<p class="spip"><u><strong class="spip">1) Fichiers d’échanges</strong></u></p>

<p class="spip">Un ou deux fichiers servent de "boite au lettre" pour échanger l’information. Pour éviter les problèmes de concurrence, on peut :
<br /></p>
<li> Utiliser un seul fichier et employer la technique du "verrouillage/déverrouillage" (fichier lock)
<br /><li> Utiliser un fichier en écriture pour chaque programme. L’autre ne peut que lire le fichier de son partenaire.
<ul class="spip"><li class="spip"> Avantages :<ul class="spip"><li class="spip"> simple à mettre en oeuvre (opérations sur des fichiers standards)</li><li class="spip"> utilisable entre machines distantes (ntfs, smb, http ...)</li></ul></li><li class="spip"> Inconvénients :<ul class="spip"><li class="spip"> lent (pas de handshake donc boucles de scrutation)</li><li class="spip"> difficile à sécuriser (droits sur les fichiers)</li></ul></li></ul>
<hr>
<p class="spip"><u><strong class="spip">2) Les flux internes</strong> (par invocation)</u></p>

<p class="spip">Un programme (le père) appelle un second programme (le fils) et établit deux canaux de communication (flux entrant/sortant). Le fils utilise ses entrées/sorties standards comme fichiers de communication.</p>

<ul class="spip"><li class="spip"> Avantages :<ul class="spip"><li class="spip"> rapidité des échanges</li><li class="spip"> sécurité des échange (flux privés)</li></ul></li><li class="spip"> Inconvénients :<ul class="spip"><li class="spip"> échanges internes uniquement (une seule machine).</li></ul></li></ul>
<p class="spip">Voir : execve, fork, assignstream, shell (cf R2)</p>

<p class="spip"><i class="spip">Remarque 1 :</i> cette méthode est très utilisé pour écrire des front-end. Ex : Apollo ou GQmpeg et mpg123
<br /><i class="spip">Remarque 2 :</i> la commande "shell" n’est qu’un simple appel. On ne peut pas parler de réelle communication par flux.</p>

<hr>
<p class="spip"><u><strong class="spip">3) Les sockets</strong></u></p>

<p class="spip">Un programme <strong class="spip">serveur</strong> crée un socket  (point de communication), paramètre ce socket (bind) et attend (listen) la connexion d’un client.
<br />Le programme <strong class="spip">client</strong> crée un socket puis essaye de le connecter (connect) au socket du serveur. En l’absence de serveur, il y a échec de connexion. Le serveur accepte (accept) la demande de connexion.</p>

<p class="spip">Chaque socket est vu comme deux fichiers (entrant/sortant) au travers desquels se font les échanges.</p>

<ul class="spip"><li class="spip"> Avantages :<ul class="spip"><li class="spip"> rapidité des échanges</li><li class="spip"> relative sécurité des échange (flux privés)</li><li class="spip"> clients multiples</li></ul></li><li class="spip"> Inconvénients :<ul class="spip"><li class="spip"> complexe à mettre en oeuvre.</li></ul></li></ul>
<p class="spip">Les sockets peuvent fonctionner en mode en interne (pf_local) ou externe (pf_inet).
<br /></p>
<li> En mode interne, le client identifie le serveur par son nom. La connexion reste interne à la machine.
<br /><li> En mode externe, le client identifie le serveur par son adresse IP et par son port de connexion.
<p class="spip">Les deux processus peuvent échanger :
<br /></p>
<li> des lignes de texte (les lectures/écritures se font comme pour des fichiers de texte standards)
<br /><li> des bolcs de données (grace à deux instructions spéciales recv() et send() )
<p class="spip">Voir : socket, bind, listen, connect, accept</p>

<hr>
<h3>Exemples</h3>
<p class="spip">Pour une fois et au vu de la complexité du sujet, j’ai décidé de présenter deux exemples en <strong class="spip">FreePascal</strong>. Etant un langage très didactique et donc lisible, les programmeurs n’auront aucun mal à le traduire (les fonctions principales sont génériques).</p>

<p class="spip">Par contre, je n’aborde pas ici le cas pourtant classique du multiclient car il nécessite l’utilisation d’une technique de duplication spécifique au sysex hote (fork, thread...)</p>

<p class="spip"><u><strong class="spip">1) Communication par flux internes</strong></u></p>

<p class="spip">Un premier process appelé "père" invoque un second process indépendant appelé "fils". Tout ce que le fils envoie au père est affiché à l’écran par le père. Les deux process sont tués quand le fils envoie la ligne "Termine".</p>

<div style='text-align: left;' class='spip_code' dir='ltr'><code><br />
Program interprocess_pere;<br />
<br />
{ utilisation de la fonction AssignStream }<br />
<br />
Uses oldlinux;<br />
<br />
Var Si,So : Text;<br />
    S : String;<br />
        <br />
begin<br />
  Writeln ('Pere : J''appelle le fils...');<br />
  Assignstream (Si,So,'./fils_p');<br />
  if linuxerror<>0 then<br />
    begin<br />
      writeln ('AssignStream failed !');<br />
      halt(1);<br />
    end;<br />
<br />
  readln (si,S);                                 // Bloc 1<br />
  writeln ('Pere : le fils dit : [',S,']');      //<br />
<br />
  writeln ('Pere : Je parle au fils ;-)');<br />
<br />
  writeln (so,'Salut fils !');<br />
  if ioresult<>0 then writeln ('Pere : Peux pas parler au fils. :-(');<br />
<br />
  readln (si,S);                                 // Bloc 2<br />
  writeln ('Pere : le fils dit : [',S,']');      //<br />
<br />
  while not (S='Termine.')  do<br />
    begin<br />
      readln (S);<br />
      writeln (so,S);<br />
      readln (si,S);                             // Bloc 3<br />
      writeln ('Pere : le fils dit : [',S,']');  //<br />
    end;<br />
  <br />
  Writeln ('Pere : Je stoppe la conversation');<br />
end.<br />
</code></div>
<div style='text-align: left;' class='spip_code' dir='ltr'><code><br />
Program interprocess_fils;<br />
<br />
Var S : String;<br />
        <br />
begin<br />
    Writeln ('Fils : C''est le fils');   // Bloc 1<br />
    S:='';<br />
    While not (S='Fin') do <br />
      begin<br />
      readln (s);<br />
      if pos ('Salut fils !',S)<>0<br />
         then  Writeln ('Salut pere !')  // Bloc 2<br />
         else  Writeln ('Recu : ',S);    // Bloc 3<br />
      end;<br />
<br />
  writeln ('Termine.');<br />
<br />
  while not eof (input) do  readln (s);<br />
<br />
  close (output);<br />
end.<br />
</code></div>
<p class="spip"><u><strong class="spip">2) Communication par Sockets</strong></u></p>

<p class="spip">On crée ici un programme serveur capable d’accepter un client. Ce serveur est en écoute sur le port 6543. Il utilise l’interface réseau 127.0.0.1 (lo) ou la première carte réseau trouvée (eth0)</p>

<div style='text-align: left;' class='spip_code' dir='ltr'><code><br />
Program socksvr_inet_IP;  { MODE PF_INET = communication reseau }<br />
                           {Un seul client à la fois}<br />
<br />
{ Programme pour tester l'unit Sockets -  Version Serveur }<br />
{ En premier, lancer socksvr_inet_IP pour créer le socket }<br />
{ puis lancer sockcli_inet_IP qui se connectera au socket }<br />
{ Le serveur reste en ligne même si le client disparait.  }<br />
{ Le serveur ne quitte que si le client envoie "FIN"      }<br />
<br />
 uses Sockets,BaseUnix,errors;<br />
<br />
 const Port=6543;<br />
<br />
 function SwapWord(w:word):word;<br />
   begin SwapWord:=((w and $ff) shl 8)+(w shr 8); end;<br />
<br />
 Var<br />
   Buffer   : string[255];<br />
   Addr     : TInetSockAddr;<br />
   S        : Longint;<br />
   Sin,Sout : Text;<br />
<br />
 procedure Perror (const S:string);<br />
 begin<br />
   writeln(S,strerror(SocketError),'(',SocketError,')');<br />
   halt(100);<br />
 end;<br />
<br />
begin <br />
  Addr.family:=AF_INET;<br />
  Addr.port:=SwapWord(Port);<br />
  Addr.addr:=0;  //Le serveur ecoute sur toute la plage IP<br />
<br />
  {création du socket - com reseau - mode connecté}<br />
  { S va contenir le num indentification du socket}<br />
  S:=Socket (AF_INET,SOCK_STREAM,0); <br />
  if SocketError<>0 then Perror ('Serveur : Erreur Socket : ');<br />
<br />
  {Fourni au socket S les infos de connexion}<br />
  if not Bind(S,Addr,sizeof(Addr))<br />
    then Perror ('Serveur : Erreur Bind : ');<br />
<br />
  {mise en ecoute en mode passif}<br />
  if not Listen (S,1) then  Perror ('Serveur : Erreur Listen : ');<br />
  Writeln('J''attend la connection du Client (le lancer dans une autre console)');<br />
<br />
  repeat           {accepter une connexion d'un client}<br />
    if not Accept (S,Addr,Sin,Sout) then Perror ('Serveur : Erreur Accept :');<br />
    Reset(Sin);<br />
    ReWrite(Sout);<br />
<br />
    Writeln(Sout,'Message du Serveur');<br />
    Flush(SOut);<br />
    repeat<br />
      Readln(Sin,Buffer);<br />
      Writeln('le Server a recu : ',buffer);<br />
    until (eof(sin))or(buffer='FIN');<br />
  until (buffer='FIN');<br />
  closeSocket(S);<br />
end.<br />
</code></div>
<p class="spip">La connection à ce serveur peut se faire :
<br /></p>
<li> avec un “telnet IPserveur 6543”
<br /><li> avec le petit client ci-dessous (en précisant juste l’adresse IP du serveur)
<div style='text-align: left;' class='spip_code' dir='ltr'><code><br />
Program sockcli_inet_IP; { MODE PF_INET = communication reseau }<br />
<br />
{ Program pour tester l'unit Sockets  - Version Client    }<br />
{ En premier, lancer socksvr_inet_IP pour créer le socket }<br />
{ puis lancer sockcli_inet_IP qui se connectera au socket }<br />
{ Le serveur reste en ligne même si le client disparait.  }<br />
{ Le serveur ne quitte que si le client envoie "FIN"      }<br />
{ Le client peut quitter sans tuer le serveur en tapant "QUITTE"}<br />
<br />
 uses Sockets, errors; <br />
<br />
 const Port=6543; <br />
<br />
 procedure PError(const S : string); <br />
  begin<br />
    writeln(S,strerror(SocketError),'(',SocketError,')'); <br />
    halt(100); <br />
  end; <br />
<br />
 function SwapWord(w:word):word;<br />
   begin SwapWord:=((w and $ff) shl 8)+(w shr 8); end;<br />
<br />
 function StrToAddr(s : String) : LongInt;<br />
  var<br />
     r, i, p, c : LongInt;<br />
     t : String;<br />
  begin<br />
     StrToAddr := 0;<br />
     r := 0;<br />
     for i := 0 to 3 do<br />
     begin<br />
        p := Pos('.', s);<br />
        if p = 0 then p := Length(s) + 1;<br />
        if p <= 1 then exit;<br />
        t := Copy(s, 1, p - 1);<br />
        Delete(s, 1, p);<br />
        Val(t, p, c);<br />
        if (c <> 0) or (p < 0) or (p > 255) then exit;<br />
        r := r or p shl (i * 8);<br />
     end;<br />
     StrToAddr := r;<br />
  end;<br />
<br />
 Var <br />
   Buffer   : string [255];<br />
   Addr     : TInetSockAddr;<br />
   S        : Longint;<br />
   Sin,Sout : Text;<br />
   i        : integer;<br />
   msg      : string;<br />
<br />
begin<br />
  Addr.family:=AF_INET;<br />
  Addr.port:=SwapWord(Port);<br />
  write('Entrer adresse IP (127.0.0.1 pour localhost): '); readln(msg); <br />
  Addr.addr:=StrToAddr(msg);<br />
<br />
  {création du socket - com reseau - mode connecté}<br />
  S:=Socket (AF_INET,SOCK_STREAM,0);<br />
  if SocketError<>0 then Perror('Client : Erreur Socket : ');<br />
<br />
  {connection au serveur}<br />
  if not Connect (S,Addr,Sin,Sout)<br />
   then PError('Client : Erreur Connect : ');<br />
<br />
  Reset(Sin);<br />
  ReWrite(Sout);<br />
<br />
  Buffer:='le Client est en ligne.';<br />
  for i:=1 to 10 do Writeln(Sout,Buffer);<br />
<br />
  repeat<br />
    Readln(Buffer);<br />
    Writeln(Sout,buffer);<br />
    Flush(Sout);<br />
  until (Buffer='FIN')or(Buffer='QUITTE');<br />
<br />
  Readln(SIn,Buffer);<br />
  WriteLn(Buffer);<br />
  close(sout);<br />
  closeSocket(S);<br />
end.<br />
</code></div>
<hr>
<p class="spip">Dans ce domaine très large qu’est la communication par sockets, je n’ai rien inventé bien sûr et vous trouverez sur le net beaucoup d’exemples et de tutoriaux.</p>

<p class="spip">Pour en savoir plus, voilà quelques bonnes adresses :</p>

<li> http://www.linux-france.org/article/man-fr/man2/Index-2.html
<li> http://www.commentcamarche.net/sockets/sockcomm.php3
<li> http://www.bastisoft.de/pascal/pasinet.html