<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="fr">
	<id>https://tala-informatique.fr/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Magsss</id>
	<title>The Linux Craftsman - Contributions [fr]</title>
	<link rel="self" type="application/atom+xml" href="https://tala-informatique.fr/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Magsss"/>
	<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=Sp%C3%A9cial:Contributions/Magsss"/>
	<updated>2026-04-04T11:23:39Z</updated>
	<subtitle>Contributions</subtitle>
	<generator>MediaWiki 1.43.6</generator>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=Atmega328_registers&amp;diff=3301</id>
		<title>Atmega328 registers</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=Atmega328_registers&amp;diff=3301"/>
		<updated>2018-11-01T18:46:54Z</updated>

		<summary type="html">&lt;p&gt;Magsss : /* Performances */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Lorsque l&#039;on commence à écrire des programmes qui sortent de l&#039;ordinaire ou que l&#039;on veut pousser un microcontrôleur à la limite de ses capacités, il est obligatoire de descendre au niveau des registres.&lt;br /&gt;
Seulement voilà, la manipulation de registres ne s&#039;improvise pas, nécessite de connaître le hardware que l&#039;on utilise et, de par sa nature, est &#039;&#039;&#039;spécifique&#039;&#039;&#039; à un type de microcontrôleur !&lt;br /&gt;
&lt;br /&gt;
Manipuler les registres implique donc de sacrifier la portabilité du code, offerte par l&#039;utilisation des fonctions &#039;&#039;haut niveau&#039;&#039; comme &#039;&#039;digitalWrite()&#039;&#039;, au profit de la vitesse d&#039;exécution et de la compacité du code.&lt;br /&gt;
&lt;br /&gt;
Les fonctions comme &#039;&#039;digitalWrite()&#039;&#039;, &#039;&#039;digitalRead()&#039;&#039;, &#039;&#039;pinMode()&#039;&#039; permettent au développeur d&#039;accomplir une action sans avoir à se soucier du type de microcontrôleur utilisé mais, in fine, elles vont elles-mêmes manipuler les registres pour accomplir ces tâches !&lt;br /&gt;
&lt;br /&gt;
= Le cas de l&#039;ATmega328 =&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
L&#039;AtMega328 est le microcontrôleur utilisé sur les Arduino Uno, pro, nano et bon nombre de circuits électroniques d&#039;autres marques ! Ci à droite, l&#039;association entre le nom des broches et leurs positions dans le registre.&lt;br /&gt;
&lt;br /&gt;
On peut remarquer que :&lt;br /&gt;
* les broches D0, D1, D2, D3, D4, D5 et D6 font partie du groupe &#039;&#039;&#039;D&#039;&#039;&#039; (P&#039;&#039;&#039;D&#039;&#039;&#039;);&lt;br /&gt;
* les broches D8, D9, D10, D11, D12 et D13 font partie du groupe &#039;&#039;&#039;B&#039;&#039;&#039; (P&#039;&#039;&#039;B&#039;&#039;&#039;);&lt;br /&gt;
* les broches analogiques font partie du groupe &#039;&#039;&#039;C&#039;&#039;&#039; (P&#039;&#039;&#039;C&#039;&#039;&#039;).&lt;br /&gt;
A chacun de ces groupes on peut associer trois registres :&lt;br /&gt;
* PORT &amp;amp;rarr; pour positionner l&#039;état du port (HIGH ou LOW);&lt;br /&gt;
* PIN &amp;amp;rarr; pour lire l&#039;état d&#039;un port;&lt;br /&gt;
* DDR (&#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;D&#039;&#039;&#039;irection &#039;&#039;&#039;R&#039;&#039;&#039;egister) &amp;amp;rarr; pour positionner le sens d&#039;un port (INPUT, OUTPUT);&lt;br /&gt;
On se retrouve donc avec 9 registres manipulables:&lt;br /&gt;
* PORTD, PIND et DDRD pour les broches D0, D1, D2, D3, D4, D5 et D6;&lt;br /&gt;
* PORTB, PINB et DDRB pour les broches D8, D9, D10, D11, D12 et D13;&lt;br /&gt;
* PORTC, PINC et DDRC pour les broches analogiques.&lt;br /&gt;
La position du bit dans le registre est déterminé par le numéro attenant à la lettre du groupe. Par exemple, si on prend le cas de la broche D13, c&#039;est le 5ème bit du groupe B (PB&#039;&#039;&#039;5&#039;&#039;&#039;) &lt;br /&gt;
[[Fichier:Mapping D13 atmega328.png]]&lt;br /&gt;
.&lt;br /&gt;
||&lt;br /&gt;
[[Fichier:Atmega328 pin mapping.png|centré]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Comment manipuler un registre ? =&lt;br /&gt;
Maintenant que l&#039;on a identifié le groupe et la position du bit des broches dans les registres, nous pouvons passer à la manipulation !&lt;br /&gt;
&lt;br /&gt;
Reprenons l&#039;exemple de la broche D13. Pour modifier son état, il suffit de modifier la valeur de PORTB:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB = B00100000; // pour mettre D13 à HIGH&lt;br /&gt;
PORTB = B00000000; // pour mettre D13 à LOW&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Seulement voilà, on sait que sa position dans les registres correspond au bit numéro 6 mais on n&#039;a absolument aucune idée de la valeur de PORTB au moment de la modification !&lt;br /&gt;
La manipulation précédente va certainement modifier l&#039;état de D13 mais va aussi modifier l&#039;état des autre broches du groupe &#039;&#039;&#039;B&#039;&#039;&#039;. On ne peut donc pas agir de la sorte, il faut utiliser un opérateur logique pour modifier les registres et on va distinguer deux cas de figures: le passage à &#039;&#039;1&#039;&#039; et celui à &#039;&#039;0&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Passage à &#039;&#039;1&#039;&#039; ==&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;40%&amp;quot;|&lt;br /&gt;
Pour modifier l&#039;état d&#039;un registre à &#039;&#039;1&#039;&#039; sans modifier l&#039;état des autres registres nous allons utiliser l&#039;opérateur &#039;&#039;OU&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Si on reprend l&#039;exemple précédent, nous allons plutôt faire cela:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB |= B00100000; // pour mettre seulement D13 à HIGH&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Imaginons que PORTB est la valeur suivante &#039;&#039;B00010101&#039;&#039;, PORTB |= B00100000 revient à faire:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||0||1||0||1||0||1&lt;br /&gt;
|-&lt;br /&gt;
||ou||0||0||1||0||0||0||0||0&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;9&amp;quot;|&amp;lt;hr&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||1||1||0||1||0||1&lt;br /&gt;
|}&lt;br /&gt;
On a donc réussi à modifier la valeur de D13 sans modifier l&#039;état des autres broches !&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|width=&amp;quot;12%&amp;quot; valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
Table de vérité de OU&lt;br /&gt;
{|border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
A&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
B&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
A OU B&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|}&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;40%&amp;quot;|&lt;br /&gt;
On peut également effectuer cette manipulation par décalage de bit. Dans l&#039;exemple précédent nous avons utilisé &#039;&#039;B00100000&#039;&#039; pour faire le OU mais on aurait pu utiliser l&#039;écriture &#039;&#039;(1&amp;lt;&amp;lt;5)&#039;&#039; ou même la constante prévue à cet effet &#039;&#039;(1&amp;lt;&amp;lt;PB5)&#039;&#039;.&lt;br /&gt;
Le chiffre &#039;&#039;1&#039;&#039; est un entier qui s&#039;écrit en binaire comme cela:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||position||7||6||5||4||3||2||1||0&lt;br /&gt;
|-&lt;br /&gt;
||valeur||0||0||0||0||0||0||0||1&lt;br /&gt;
|}&lt;br /&gt;
Il nous suffit donc de le décaler de 5 positions vers la gauche pour obtenir B00100000:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||position||7||6||5||4||3||2||1||0&lt;br /&gt;
|-&lt;br /&gt;
||valeur||0||0||1||0||0||0||0||0&lt;br /&gt;
|}&lt;br /&gt;
Les trois écritures suivantes sont donc équivalentes:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB |= B00100000;&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;5);&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Passage à &#039;&#039;0&#039;&#039; ==&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;40%&amp;quot;|&lt;br /&gt;
Pour modifier l&#039;état d&#039;un registre à &#039;&#039;1&#039;&#039; sans modifier l&#039;état des autres registres nous allons utiliser l&#039;opérateur &#039;&#039;ET&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Si on reprend l&#039;exemple précédent, nous allons plutôt faire cela:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB &amp;amp;= ~B00100000; // pour mettre seulement D13 à LOW&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
L&#039;opérateur &#039;&#039;~&#039;&#039; permet d&#039;obtenir le [https://fr.wikipedia.org/wiki/Compl%C3%A9ment_%C3%A0_un complément A1] d&#039;un nombre binaire: B00100000 devient alors B11011111.&lt;br /&gt;
&lt;br /&gt;
Imaginons que PORTB est la valeur suivante &#039;&#039;B00010101&#039;&#039;, PORTB &amp;amp;= ~B00100000 revient à faire:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||0||1||0||1||0||1&lt;br /&gt;
|-&lt;br /&gt;
||ET||1||1||0||1||1||1||1||1&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;9&amp;quot;|&amp;lt;hr&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||0||1||0||1||0||1&lt;br /&gt;
|}&lt;br /&gt;
On a donc réussi à modifier la valeur de D13 sans modifier l&#039;état des autres broches !&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|width=&amp;quot;12%&amp;quot; valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
Table de vérité de ET&lt;br /&gt;
{|border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
A&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
B&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
A ET B&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|}&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;40%&amp;quot;|&lt;br /&gt;
On peut également effectuer cette manipulation par décalage de bit. Dans l&#039;exemple précédent nous avons utilisé &#039;&#039;~B00100000&#039;&#039; pour faire le OU mais on aurait pu utiliser l&#039;écriture &#039;&#039;~(1&amp;lt;&amp;lt;5)&#039;&#039; ou même la constante prévue à cet effet &#039;&#039;~(1&amp;lt;&amp;lt;PB5)&#039;&#039;..&lt;br /&gt;
Les trois écritures suivantes sont donc équivalentes:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB &amp;amp;= ~B00100000;&lt;br /&gt;
PORTB &amp;amp;= ~(1&amp;lt;&amp;lt;5);&lt;br /&gt;
PORTB &amp;amp;= ~(1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Modification de l&#039;état d&#039;une broche =&lt;br /&gt;
Dans l&#039;exemple suivant nous allons modifier l&#039;état de la broche D13 pour la faire clignoter.&lt;br /&gt;
==Modification de sens ==&lt;br /&gt;
Dans un premier temps il faut la placer en sortie et pour cela nous allons manipuler le registre &#039;&#039;DDRB&#039;&#039;:&lt;br /&gt;
* la valeur &#039;&#039;1&#039;&#039; positionne la broche en sortie (&#039;&#039;OUTPUT&#039;&#039;);&lt;br /&gt;
* la valeur &#039;&#039;0&#039;&#039; positionne la broche en entrée (&#039;&#039;INPUT&#039;&#039;), si l&#039;état est positionné à &#039;&#039;1&#039;&#039; cela active la résistance de &#039;&#039;pullup&#039;&#039; (&#039;&#039;INPUT_PULLUP&#039;&#039;).&lt;br /&gt;
Nous allons donc modifier le registre DDRB comme suit, les trois notations sont équivalentes:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
DDRB |= B00100000; // Positionnement de D13 en OUTPUT&lt;br /&gt;
DDRB |= (1&amp;lt;&amp;lt;5);&lt;br /&gt;
DDRB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Modification d&#039;état ==&lt;br /&gt;
On peut maintenant modifier son état grâce au registre PORTB:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB |= B00100000; // Positionnement de D13 à HIGH&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;5);&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Lecture d&#039;état==&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Il est possible de lire l&#039;état de la broche D13 grâce au registre &#039;&#039;PINB&#039;&#039;. Nous allons utiliser la même technique du masquage que précédemment avec l&#039;opérateur &#039;&#039;ET&#039;&#039;.&lt;br /&gt;
Imaginons que PINB est la valeur suivante &#039;&#039;B00110101&#039;&#039;, PINB &amp;amp;= B00100000 revient à faire:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||1||1||0||1||0||1&lt;br /&gt;
|-&lt;br /&gt;
||ET||0||0||1||0||0||0||0||0&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;9&amp;quot;|&amp;lt;hr&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||1||0||0||0||0||0&lt;br /&gt;
|}&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
On va donc retirer l&#039;état des autres broches sans modifier pour autant celui de la broche D13 !&lt;br /&gt;
Il ne nous reste plus qu&#039;à décaler ce bit de 5 positions vers la droite pour pouvoir le lire. &lt;br /&gt;
&lt;br /&gt;
Les deux écritures suivantes sont équivalentes:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t state;&lt;br /&gt;
state = (PINB &amp;amp; B00100000) &amp;gt;&amp;gt; 5;&lt;br /&gt;
state = (PINB &amp;amp; B00100000) &amp;gt;&amp;gt; PB5;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Rien n’empêche d&#039;utiliser cela pour faire des tests:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Modification de D13 en sortie&lt;br /&gt;
DDRB |= B00100000;&lt;br /&gt;
// Passage de D13 à HIGH&lt;br /&gt;
PORTB |= (1 &amp;lt;&amp;lt; PB5);&lt;br /&gt;
if ((PINB &amp;amp; B00100000) &amp;gt;&amp;gt; PB5) {&lt;br /&gt;
    Serial.println(&amp;quot;D13 is ON&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
// Passage de D13 à LOW&lt;br /&gt;
PORTB &amp;amp;= ~(1 &amp;lt;&amp;lt; PB5);&lt;br /&gt;
if ((PINB &amp;amp; B00100000) &amp;gt;&amp;gt; PB5 == 0) {&lt;br /&gt;
    Serial.println(&amp;quot;D13 is OFF&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Lorsque l&#039;on exécute le code suivant on s&#039;aperçoit que rien ne s&#039;affiche. La raison est simple, la modification de registre est tellement rapide que l&#039;état électrique n&#039;a pas le temps d&#039;être modifié avant la lecture !&lt;br /&gt;
&lt;br /&gt;
Il suffit d&#039;ajouter:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
delay(1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
avant les lignes modifiant les états:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
PORTB |= (1 &amp;lt;&amp;lt; PB5);&lt;br /&gt;
...&lt;br /&gt;
PORTB &amp;amp;= ~(1 &amp;lt;&amp;lt; PB5);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Performances =&lt;br /&gt;
La &#039;&#039;&#039;véritable&#039;&#039;&#039; interrogation réside dans le fait de savoir si le jeu en vaut la chandelle. Est-il intéressant de sacrifier la simplicité d&#039;écriture et la compréhension du code pour la performance ?&lt;br /&gt;
&lt;br /&gt;
Pour faire un test probant, allumons la led présente sur D13 100.000 fois avec:&lt;br /&gt;
* dans un premier temps &#039;&#039;pinMode()&#039;&#039; et &#039;&#039;digitalWrite()&#039;&#039;;&lt;br /&gt;
* dans un second temps &#039;&#039;DDRB&#039;&#039; et &#039;&#039;PORTB&#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
{|width=&amp;quot;100%&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Voila le premier test:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define LIMIT 100000&lt;br /&gt;
void setup() {&lt;br /&gt;
  Serial.begin(9600);&lt;br /&gt;
  int32_t timer = millis();&lt;br /&gt;
  pinMode(13, OUTPUT);&lt;br /&gt;
  for (uint32_t i = 0; i &amp;lt; LIMIT; i++) {&lt;br /&gt;
    digitalWrite(13, HIGH);&lt;br /&gt;
    digitalWrite(13, LOW);&lt;br /&gt;
  }&lt;br /&gt;
  int32_t elapsed = millis()  - timer;&lt;br /&gt;
  Serial.print(&amp;quot;Temps d&#039;exécution: &amp;quot;);&lt;br /&gt;
  Serial.println(elapsed);&lt;br /&gt;
}&lt;br /&gt;
void loop() {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Le croquis utilise &#039;&#039;&#039;2138&#039;&#039;&#039; octets et le résultat est:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Temps d’exécution: 709&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Voila le deuxième test:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define LIMIT 100000&lt;br /&gt;
void setup() {&lt;br /&gt;
  Serial.begin(9600);&lt;br /&gt;
  int32_t timer = millis();&lt;br /&gt;
  DDRB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
  for(uint32_t i = 0; i &amp;lt; LIMIT; i++){&lt;br /&gt;
    PORTB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
    PORTB &amp;amp;= ~(1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
  }&lt;br /&gt;
  int32_t elapsed = millis()  - timer;&lt;br /&gt;
  Serial.print(&amp;quot;Temps d&#039;exécution: &amp;quot;);&lt;br /&gt;
  Serial.println(elapsed);&lt;br /&gt;
}&lt;br /&gt;
void loop() {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Le croquis utilise &#039;&#039;&#039;1844&#039;&#039;&#039; octets et le résultat est:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Temps d’exécution: 62&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
On peut constater que le code utilisant les registres est non seulement plus léger (&#039;&#039;&#039;13,67%&#039;&#039;&#039;) mais également plus rapide (&#039;&#039;&#039;11.42&#039;&#039;&#039; fois) !&lt;/div&gt;</summary>
		<author><name>Magsss</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=Atmega328_registers&amp;diff=3300</id>
		<title>Atmega328 registers</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=Atmega328_registers&amp;diff=3300"/>
		<updated>2018-11-01T18:46:35Z</updated>

		<summary type="html">&lt;p&gt;Magsss : /* Performances */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Lorsque l&#039;on commence à écrire des programmes qui sortent de l&#039;ordinaire ou que l&#039;on veut pousser un microcontrôleur à la limite de ses capacités, il est obligatoire de descendre au niveau des registres.&lt;br /&gt;
Seulement voilà, la manipulation de registres ne s&#039;improvise pas, nécessite de connaître le hardware que l&#039;on utilise et, de par sa nature, est &#039;&#039;&#039;spécifique&#039;&#039;&#039; à un type de microcontrôleur !&lt;br /&gt;
&lt;br /&gt;
Manipuler les registres implique donc de sacrifier la portabilité du code, offerte par l&#039;utilisation des fonctions &#039;&#039;haut niveau&#039;&#039; comme &#039;&#039;digitalWrite()&#039;&#039;, au profit de la vitesse d&#039;exécution et de la compacité du code.&lt;br /&gt;
&lt;br /&gt;
Les fonctions comme &#039;&#039;digitalWrite()&#039;&#039;, &#039;&#039;digitalRead()&#039;&#039;, &#039;&#039;pinMode()&#039;&#039; permettent au développeur d&#039;accomplir une action sans avoir à se soucier du type de microcontrôleur utilisé mais, in fine, elles vont elles-mêmes manipuler les registres pour accomplir ces tâches !&lt;br /&gt;
&lt;br /&gt;
= Le cas de l&#039;ATmega328 =&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
L&#039;AtMega328 est le microcontrôleur utilisé sur les Arduino Uno, pro, nano et bon nombre de circuits électroniques d&#039;autres marques ! Ci à droite, l&#039;association entre le nom des broches et leurs positions dans le registre.&lt;br /&gt;
&lt;br /&gt;
On peut remarquer que :&lt;br /&gt;
* les broches D0, D1, D2, D3, D4, D5 et D6 font partie du groupe &#039;&#039;&#039;D&#039;&#039;&#039; (P&#039;&#039;&#039;D&#039;&#039;&#039;);&lt;br /&gt;
* les broches D8, D9, D10, D11, D12 et D13 font partie du groupe &#039;&#039;&#039;B&#039;&#039;&#039; (P&#039;&#039;&#039;B&#039;&#039;&#039;);&lt;br /&gt;
* les broches analogiques font partie du groupe &#039;&#039;&#039;C&#039;&#039;&#039; (P&#039;&#039;&#039;C&#039;&#039;&#039;).&lt;br /&gt;
A chacun de ces groupes on peut associer trois registres :&lt;br /&gt;
* PORT &amp;amp;rarr; pour positionner l&#039;état du port (HIGH ou LOW);&lt;br /&gt;
* PIN &amp;amp;rarr; pour lire l&#039;état d&#039;un port;&lt;br /&gt;
* DDR (&#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;D&#039;&#039;&#039;irection &#039;&#039;&#039;R&#039;&#039;&#039;egister) &amp;amp;rarr; pour positionner le sens d&#039;un port (INPUT, OUTPUT);&lt;br /&gt;
On se retrouve donc avec 9 registres manipulables:&lt;br /&gt;
* PORTD, PIND et DDRD pour les broches D0, D1, D2, D3, D4, D5 et D6;&lt;br /&gt;
* PORTB, PINB et DDRB pour les broches D8, D9, D10, D11, D12 et D13;&lt;br /&gt;
* PORTC, PINC et DDRC pour les broches analogiques.&lt;br /&gt;
La position du bit dans le registre est déterminé par le numéro attenant à la lettre du groupe. Par exemple, si on prend le cas de la broche D13, c&#039;est le 5ème bit du groupe B (PB&#039;&#039;&#039;5&#039;&#039;&#039;) &lt;br /&gt;
[[Fichier:Mapping D13 atmega328.png]]&lt;br /&gt;
.&lt;br /&gt;
||&lt;br /&gt;
[[Fichier:Atmega328 pin mapping.png|centré]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Comment manipuler un registre ? =&lt;br /&gt;
Maintenant que l&#039;on a identifié le groupe et la position du bit des broches dans les registres, nous pouvons passer à la manipulation !&lt;br /&gt;
&lt;br /&gt;
Reprenons l&#039;exemple de la broche D13. Pour modifier son état, il suffit de modifier la valeur de PORTB:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB = B00100000; // pour mettre D13 à HIGH&lt;br /&gt;
PORTB = B00000000; // pour mettre D13 à LOW&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Seulement voilà, on sait que sa position dans les registres correspond au bit numéro 6 mais on n&#039;a absolument aucune idée de la valeur de PORTB au moment de la modification !&lt;br /&gt;
La manipulation précédente va certainement modifier l&#039;état de D13 mais va aussi modifier l&#039;état des autre broches du groupe &#039;&#039;&#039;B&#039;&#039;&#039;. On ne peut donc pas agir de la sorte, il faut utiliser un opérateur logique pour modifier les registres et on va distinguer deux cas de figures: le passage à &#039;&#039;1&#039;&#039; et celui à &#039;&#039;0&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Passage à &#039;&#039;1&#039;&#039; ==&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;40%&amp;quot;|&lt;br /&gt;
Pour modifier l&#039;état d&#039;un registre à &#039;&#039;1&#039;&#039; sans modifier l&#039;état des autres registres nous allons utiliser l&#039;opérateur &#039;&#039;OU&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Si on reprend l&#039;exemple précédent, nous allons plutôt faire cela:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB |= B00100000; // pour mettre seulement D13 à HIGH&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Imaginons que PORTB est la valeur suivante &#039;&#039;B00010101&#039;&#039;, PORTB |= B00100000 revient à faire:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||0||1||0||1||0||1&lt;br /&gt;
|-&lt;br /&gt;
||ou||0||0||1||0||0||0||0||0&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;9&amp;quot;|&amp;lt;hr&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||1||1||0||1||0||1&lt;br /&gt;
|}&lt;br /&gt;
On a donc réussi à modifier la valeur de D13 sans modifier l&#039;état des autres broches !&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|width=&amp;quot;12%&amp;quot; valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
Table de vérité de OU&lt;br /&gt;
{|border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
A&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
B&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
A OU B&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|}&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;40%&amp;quot;|&lt;br /&gt;
On peut également effectuer cette manipulation par décalage de bit. Dans l&#039;exemple précédent nous avons utilisé &#039;&#039;B00100000&#039;&#039; pour faire le OU mais on aurait pu utiliser l&#039;écriture &#039;&#039;(1&amp;lt;&amp;lt;5)&#039;&#039; ou même la constante prévue à cet effet &#039;&#039;(1&amp;lt;&amp;lt;PB5)&#039;&#039;.&lt;br /&gt;
Le chiffre &#039;&#039;1&#039;&#039; est un entier qui s&#039;écrit en binaire comme cela:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||position||7||6||5||4||3||2||1||0&lt;br /&gt;
|-&lt;br /&gt;
||valeur||0||0||0||0||0||0||0||1&lt;br /&gt;
|}&lt;br /&gt;
Il nous suffit donc de le décaler de 5 positions vers la gauche pour obtenir B00100000:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||position||7||6||5||4||3||2||1||0&lt;br /&gt;
|-&lt;br /&gt;
||valeur||0||0||1||0||0||0||0||0&lt;br /&gt;
|}&lt;br /&gt;
Les trois écritures suivantes sont donc équivalentes:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB |= B00100000;&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;5);&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Passage à &#039;&#039;0&#039;&#039; ==&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;40%&amp;quot;|&lt;br /&gt;
Pour modifier l&#039;état d&#039;un registre à &#039;&#039;1&#039;&#039; sans modifier l&#039;état des autres registres nous allons utiliser l&#039;opérateur &#039;&#039;ET&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Si on reprend l&#039;exemple précédent, nous allons plutôt faire cela:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB &amp;amp;= ~B00100000; // pour mettre seulement D13 à LOW&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
L&#039;opérateur &#039;&#039;~&#039;&#039; permet d&#039;obtenir le [https://fr.wikipedia.org/wiki/Compl%C3%A9ment_%C3%A0_un complément A1] d&#039;un nombre binaire: B00100000 devient alors B11011111.&lt;br /&gt;
&lt;br /&gt;
Imaginons que PORTB est la valeur suivante &#039;&#039;B00010101&#039;&#039;, PORTB &amp;amp;= ~B00100000 revient à faire:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||0||1||0||1||0||1&lt;br /&gt;
|-&lt;br /&gt;
||ET||1||1||0||1||1||1||1||1&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;9&amp;quot;|&amp;lt;hr&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||0||1||0||1||0||1&lt;br /&gt;
|}&lt;br /&gt;
On a donc réussi à modifier la valeur de D13 sans modifier l&#039;état des autres broches !&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|width=&amp;quot;12%&amp;quot; valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
Table de vérité de ET&lt;br /&gt;
{|border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
A&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
B&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
A ET B&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|}&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;40%&amp;quot;|&lt;br /&gt;
On peut également effectuer cette manipulation par décalage de bit. Dans l&#039;exemple précédent nous avons utilisé &#039;&#039;~B00100000&#039;&#039; pour faire le OU mais on aurait pu utiliser l&#039;écriture &#039;&#039;~(1&amp;lt;&amp;lt;5)&#039;&#039; ou même la constante prévue à cet effet &#039;&#039;~(1&amp;lt;&amp;lt;PB5)&#039;&#039;..&lt;br /&gt;
Les trois écritures suivantes sont donc équivalentes:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB &amp;amp;= ~B00100000;&lt;br /&gt;
PORTB &amp;amp;= ~(1&amp;lt;&amp;lt;5);&lt;br /&gt;
PORTB &amp;amp;= ~(1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Modification de l&#039;état d&#039;une broche =&lt;br /&gt;
Dans l&#039;exemple suivant nous allons modifier l&#039;état de la broche D13 pour la faire clignoter.&lt;br /&gt;
==Modification de sens ==&lt;br /&gt;
Dans un premier temps il faut la placer en sortie et pour cela nous allons manipuler le registre &#039;&#039;DDRB&#039;&#039;:&lt;br /&gt;
* la valeur &#039;&#039;1&#039;&#039; positionne la broche en sortie (&#039;&#039;OUTPUT&#039;&#039;);&lt;br /&gt;
* la valeur &#039;&#039;0&#039;&#039; positionne la broche en entrée (&#039;&#039;INPUT&#039;&#039;), si l&#039;état est positionné à &#039;&#039;1&#039;&#039; cela active la résistance de &#039;&#039;pullup&#039;&#039; (&#039;&#039;INPUT_PULLUP&#039;&#039;).&lt;br /&gt;
Nous allons donc modifier le registre DDRB comme suit, les trois notations sont équivalentes:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
DDRB |= B00100000; // Positionnement de D13 en OUTPUT&lt;br /&gt;
DDRB |= (1&amp;lt;&amp;lt;5);&lt;br /&gt;
DDRB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Modification d&#039;état ==&lt;br /&gt;
On peut maintenant modifier son état grâce au registre PORTB:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB |= B00100000; // Positionnement de D13 à HIGH&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;5);&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Lecture d&#039;état==&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Il est possible de lire l&#039;état de la broche D13 grâce au registre &#039;&#039;PINB&#039;&#039;. Nous allons utiliser la même technique du masquage que précédemment avec l&#039;opérateur &#039;&#039;ET&#039;&#039;.&lt;br /&gt;
Imaginons que PINB est la valeur suivante &#039;&#039;B00110101&#039;&#039;, PINB &amp;amp;= B00100000 revient à faire:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||1||1||0||1||0||1&lt;br /&gt;
|-&lt;br /&gt;
||ET||0||0||1||0||0||0||0||0&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;9&amp;quot;|&amp;lt;hr&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||1||0||0||0||0||0&lt;br /&gt;
|}&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
On va donc retirer l&#039;état des autres broches sans modifier pour autant celui de la broche D13 !&lt;br /&gt;
Il ne nous reste plus qu&#039;à décaler ce bit de 5 positions vers la droite pour pouvoir le lire. &lt;br /&gt;
&lt;br /&gt;
Les deux écritures suivantes sont équivalentes:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t state;&lt;br /&gt;
state = (PINB &amp;amp; B00100000) &amp;gt;&amp;gt; 5;&lt;br /&gt;
state = (PINB &amp;amp; B00100000) &amp;gt;&amp;gt; PB5;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Rien n’empêche d&#039;utiliser cela pour faire des tests:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Modification de D13 en sortie&lt;br /&gt;
DDRB |= B00100000;&lt;br /&gt;
// Passage de D13 à HIGH&lt;br /&gt;
PORTB |= (1 &amp;lt;&amp;lt; PB5);&lt;br /&gt;
if ((PINB &amp;amp; B00100000) &amp;gt;&amp;gt; PB5) {&lt;br /&gt;
    Serial.println(&amp;quot;D13 is ON&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
// Passage de D13 à LOW&lt;br /&gt;
PORTB &amp;amp;= ~(1 &amp;lt;&amp;lt; PB5);&lt;br /&gt;
if ((PINB &amp;amp; B00100000) &amp;gt;&amp;gt; PB5 == 0) {&lt;br /&gt;
    Serial.println(&amp;quot;D13 is OFF&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Lorsque l&#039;on exécute le code suivant on s&#039;aperçoit que rien ne s&#039;affiche. La raison est simple, la modification de registre est tellement rapide que l&#039;état électrique n&#039;a pas le temps d&#039;être modifié avant la lecture !&lt;br /&gt;
&lt;br /&gt;
Il suffit d&#039;ajouter:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
delay(1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
avant les lignes modifiant les états:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
PORTB |= (1 &amp;lt;&amp;lt; PB5);&lt;br /&gt;
...&lt;br /&gt;
PORTB &amp;amp;= ~(1 &amp;lt;&amp;lt; PB5);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Performances =&lt;br /&gt;
La &#039;&#039;&#039;véritable&#039;&#039;&#039; interrogation réside dans le fait de savoir si le jeu en vaut la chandelle. Est-il intéressant de sacrifier la simplicité d&#039;écriture et la compréhension du code pour la performance ?&lt;br /&gt;
&lt;br /&gt;
Pour faire un test probant, allumons la led présente sur D13 100.000 fois avec:&lt;br /&gt;
* dans un premier temps &#039;&#039;pinMode()&#039;&#039; et &#039;&#039;digitalWrite()&#039;&#039;;&lt;br /&gt;
* dans un second temps &#039;&#039;DDRB&#039;&#039; et &#039;&#039;PORTB&#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
{|width=&amp;quot;100%&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Voila le premier test:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define LIMIT 100000&lt;br /&gt;
void setup() {&lt;br /&gt;
  Serial.begin(9600);&lt;br /&gt;
  int32_t timer = millis();&lt;br /&gt;
  pinMode(13, OUTPUT);&lt;br /&gt;
  for (uint32_t i = 0; i &amp;lt; LIMIT; i++) {&lt;br /&gt;
    digitalWrite(13, HIGH);&lt;br /&gt;
    digitalWrite(13, LOW);&lt;br /&gt;
  }&lt;br /&gt;
  int32_t elapsed = millis()  - timer;&lt;br /&gt;
  Serial.print(&amp;quot;Temps d&#039;exécution: &amp;quot;);&lt;br /&gt;
  Serial.println(elapsed);&lt;br /&gt;
}&lt;br /&gt;
void loop() {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Le croquis utilise &#039;&#039;&#039;2138&#039;&#039;&#039; octets et le résultat est:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Temps d’exécution: 709&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Voila le deuxième test:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define LIMIT 100000&lt;br /&gt;
void setup() {&lt;br /&gt;
  Serial.begin(9600);&lt;br /&gt;
  int32_t timer = millis();&lt;br /&gt;
  DDRB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
  for(uint32_t i = 0; i &amp;lt; LIMIT; i++){&lt;br /&gt;
    PORTB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
    PORTB &amp;amp;= ~(1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
  }&lt;br /&gt;
  int32_t elapsed = millis()  - timer;&lt;br /&gt;
  Serial.print(&amp;quot;Temps d&#039;exécution: &amp;quot;);&lt;br /&gt;
  Serial.println(elapsed);&lt;br /&gt;
}&lt;br /&gt;
void loop() {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Le croquis utilise &#039;&#039;&#039;1844&#039;&#039;&#039; octets et le résultat est:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Temps d’exécution: 62&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
On peut constater que le code utilisant les registre est non seulement plus léger (&#039;&#039;&#039;13,67%&#039;&#039;&#039;) mais également plus rapide (&#039;&#039;&#039;11.42&#039;&#039;&#039; fois) !&lt;/div&gt;</summary>
		<author><name>Magsss</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=Atmega328_registers&amp;diff=3299</id>
		<title>Atmega328 registers</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=Atmega328_registers&amp;diff=3299"/>
		<updated>2018-11-01T18:45:35Z</updated>

		<summary type="html">&lt;p&gt;Magsss : /* Lecture d&amp;#039;état */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Lorsque l&#039;on commence à écrire des programmes qui sortent de l&#039;ordinaire ou que l&#039;on veut pousser un microcontrôleur à la limite de ses capacités, il est obligatoire de descendre au niveau des registres.&lt;br /&gt;
Seulement voilà, la manipulation de registres ne s&#039;improvise pas, nécessite de connaître le hardware que l&#039;on utilise et, de par sa nature, est &#039;&#039;&#039;spécifique&#039;&#039;&#039; à un type de microcontrôleur !&lt;br /&gt;
&lt;br /&gt;
Manipuler les registres implique donc de sacrifier la portabilité du code, offerte par l&#039;utilisation des fonctions &#039;&#039;haut niveau&#039;&#039; comme &#039;&#039;digitalWrite()&#039;&#039;, au profit de la vitesse d&#039;exécution et de la compacité du code.&lt;br /&gt;
&lt;br /&gt;
Les fonctions comme &#039;&#039;digitalWrite()&#039;&#039;, &#039;&#039;digitalRead()&#039;&#039;, &#039;&#039;pinMode()&#039;&#039; permettent au développeur d&#039;accomplir une action sans avoir à se soucier du type de microcontrôleur utilisé mais, in fine, elles vont elles-mêmes manipuler les registres pour accomplir ces tâches !&lt;br /&gt;
&lt;br /&gt;
= Le cas de l&#039;ATmega328 =&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
L&#039;AtMega328 est le microcontrôleur utilisé sur les Arduino Uno, pro, nano et bon nombre de circuits électroniques d&#039;autres marques ! Ci à droite, l&#039;association entre le nom des broches et leurs positions dans le registre.&lt;br /&gt;
&lt;br /&gt;
On peut remarquer que :&lt;br /&gt;
* les broches D0, D1, D2, D3, D4, D5 et D6 font partie du groupe &#039;&#039;&#039;D&#039;&#039;&#039; (P&#039;&#039;&#039;D&#039;&#039;&#039;);&lt;br /&gt;
* les broches D8, D9, D10, D11, D12 et D13 font partie du groupe &#039;&#039;&#039;B&#039;&#039;&#039; (P&#039;&#039;&#039;B&#039;&#039;&#039;);&lt;br /&gt;
* les broches analogiques font partie du groupe &#039;&#039;&#039;C&#039;&#039;&#039; (P&#039;&#039;&#039;C&#039;&#039;&#039;).&lt;br /&gt;
A chacun de ces groupes on peut associer trois registres :&lt;br /&gt;
* PORT &amp;amp;rarr; pour positionner l&#039;état du port (HIGH ou LOW);&lt;br /&gt;
* PIN &amp;amp;rarr; pour lire l&#039;état d&#039;un port;&lt;br /&gt;
* DDR (&#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;D&#039;&#039;&#039;irection &#039;&#039;&#039;R&#039;&#039;&#039;egister) &amp;amp;rarr; pour positionner le sens d&#039;un port (INPUT, OUTPUT);&lt;br /&gt;
On se retrouve donc avec 9 registres manipulables:&lt;br /&gt;
* PORTD, PIND et DDRD pour les broches D0, D1, D2, D3, D4, D5 et D6;&lt;br /&gt;
* PORTB, PINB et DDRB pour les broches D8, D9, D10, D11, D12 et D13;&lt;br /&gt;
* PORTC, PINC et DDRC pour les broches analogiques.&lt;br /&gt;
La position du bit dans le registre est déterminé par le numéro attenant à la lettre du groupe. Par exemple, si on prend le cas de la broche D13, c&#039;est le 5ème bit du groupe B (PB&#039;&#039;&#039;5&#039;&#039;&#039;) &lt;br /&gt;
[[Fichier:Mapping D13 atmega328.png]]&lt;br /&gt;
.&lt;br /&gt;
||&lt;br /&gt;
[[Fichier:Atmega328 pin mapping.png|centré]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Comment manipuler un registre ? =&lt;br /&gt;
Maintenant que l&#039;on a identifié le groupe et la position du bit des broches dans les registres, nous pouvons passer à la manipulation !&lt;br /&gt;
&lt;br /&gt;
Reprenons l&#039;exemple de la broche D13. Pour modifier son état, il suffit de modifier la valeur de PORTB:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB = B00100000; // pour mettre D13 à HIGH&lt;br /&gt;
PORTB = B00000000; // pour mettre D13 à LOW&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Seulement voilà, on sait que sa position dans les registres correspond au bit numéro 6 mais on n&#039;a absolument aucune idée de la valeur de PORTB au moment de la modification !&lt;br /&gt;
La manipulation précédente va certainement modifier l&#039;état de D13 mais va aussi modifier l&#039;état des autre broches du groupe &#039;&#039;&#039;B&#039;&#039;&#039;. On ne peut donc pas agir de la sorte, il faut utiliser un opérateur logique pour modifier les registres et on va distinguer deux cas de figures: le passage à &#039;&#039;1&#039;&#039; et celui à &#039;&#039;0&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Passage à &#039;&#039;1&#039;&#039; ==&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;40%&amp;quot;|&lt;br /&gt;
Pour modifier l&#039;état d&#039;un registre à &#039;&#039;1&#039;&#039; sans modifier l&#039;état des autres registres nous allons utiliser l&#039;opérateur &#039;&#039;OU&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Si on reprend l&#039;exemple précédent, nous allons plutôt faire cela:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB |= B00100000; // pour mettre seulement D13 à HIGH&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Imaginons que PORTB est la valeur suivante &#039;&#039;B00010101&#039;&#039;, PORTB |= B00100000 revient à faire:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||0||1||0||1||0||1&lt;br /&gt;
|-&lt;br /&gt;
||ou||0||0||1||0||0||0||0||0&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;9&amp;quot;|&amp;lt;hr&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||1||1||0||1||0||1&lt;br /&gt;
|}&lt;br /&gt;
On a donc réussi à modifier la valeur de D13 sans modifier l&#039;état des autres broches !&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|width=&amp;quot;12%&amp;quot; valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
Table de vérité de OU&lt;br /&gt;
{|border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
A&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
B&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
A OU B&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|}&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;40%&amp;quot;|&lt;br /&gt;
On peut également effectuer cette manipulation par décalage de bit. Dans l&#039;exemple précédent nous avons utilisé &#039;&#039;B00100000&#039;&#039; pour faire le OU mais on aurait pu utiliser l&#039;écriture &#039;&#039;(1&amp;lt;&amp;lt;5)&#039;&#039; ou même la constante prévue à cet effet &#039;&#039;(1&amp;lt;&amp;lt;PB5)&#039;&#039;.&lt;br /&gt;
Le chiffre &#039;&#039;1&#039;&#039; est un entier qui s&#039;écrit en binaire comme cela:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||position||7||6||5||4||3||2||1||0&lt;br /&gt;
|-&lt;br /&gt;
||valeur||0||0||0||0||0||0||0||1&lt;br /&gt;
|}&lt;br /&gt;
Il nous suffit donc de le décaler de 5 positions vers la gauche pour obtenir B00100000:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||position||7||6||5||4||3||2||1||0&lt;br /&gt;
|-&lt;br /&gt;
||valeur||0||0||1||0||0||0||0||0&lt;br /&gt;
|}&lt;br /&gt;
Les trois écritures suivantes sont donc équivalentes:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB |= B00100000;&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;5);&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Passage à &#039;&#039;0&#039;&#039; ==&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;40%&amp;quot;|&lt;br /&gt;
Pour modifier l&#039;état d&#039;un registre à &#039;&#039;1&#039;&#039; sans modifier l&#039;état des autres registres nous allons utiliser l&#039;opérateur &#039;&#039;ET&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Si on reprend l&#039;exemple précédent, nous allons plutôt faire cela:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB &amp;amp;= ~B00100000; // pour mettre seulement D13 à LOW&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
L&#039;opérateur &#039;&#039;~&#039;&#039; permet d&#039;obtenir le [https://fr.wikipedia.org/wiki/Compl%C3%A9ment_%C3%A0_un complément A1] d&#039;un nombre binaire: B00100000 devient alors B11011111.&lt;br /&gt;
&lt;br /&gt;
Imaginons que PORTB est la valeur suivante &#039;&#039;B00010101&#039;&#039;, PORTB &amp;amp;= ~B00100000 revient à faire:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||0||1||0||1||0||1&lt;br /&gt;
|-&lt;br /&gt;
||ET||1||1||0||1||1||1||1||1&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;9&amp;quot;|&amp;lt;hr&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||0||1||0||1||0||1&lt;br /&gt;
|}&lt;br /&gt;
On a donc réussi à modifier la valeur de D13 sans modifier l&#039;état des autres broches !&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|width=&amp;quot;12%&amp;quot; valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
Table de vérité de ET&lt;br /&gt;
{|border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
A&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
B&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
A ET B&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|}&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;40%&amp;quot;|&lt;br /&gt;
On peut également effectuer cette manipulation par décalage de bit. Dans l&#039;exemple précédent nous avons utilisé &#039;&#039;~B00100000&#039;&#039; pour faire le OU mais on aurait pu utiliser l&#039;écriture &#039;&#039;~(1&amp;lt;&amp;lt;5)&#039;&#039; ou même la constante prévue à cet effet &#039;&#039;~(1&amp;lt;&amp;lt;PB5)&#039;&#039;..&lt;br /&gt;
Les trois écritures suivantes sont donc équivalentes:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB &amp;amp;= ~B00100000;&lt;br /&gt;
PORTB &amp;amp;= ~(1&amp;lt;&amp;lt;5);&lt;br /&gt;
PORTB &amp;amp;= ~(1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Modification de l&#039;état d&#039;une broche =&lt;br /&gt;
Dans l&#039;exemple suivant nous allons modifier l&#039;état de la broche D13 pour la faire clignoter.&lt;br /&gt;
==Modification de sens ==&lt;br /&gt;
Dans un premier temps il faut la placer en sortie et pour cela nous allons manipuler le registre &#039;&#039;DDRB&#039;&#039;:&lt;br /&gt;
* la valeur &#039;&#039;1&#039;&#039; positionne la broche en sortie (&#039;&#039;OUTPUT&#039;&#039;);&lt;br /&gt;
* la valeur &#039;&#039;0&#039;&#039; positionne la broche en entrée (&#039;&#039;INPUT&#039;&#039;), si l&#039;état est positionné à &#039;&#039;1&#039;&#039; cela active la résistance de &#039;&#039;pullup&#039;&#039; (&#039;&#039;INPUT_PULLUP&#039;&#039;).&lt;br /&gt;
Nous allons donc modifier le registre DDRB comme suit, les trois notations sont équivalentes:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
DDRB |= B00100000; // Positionnement de D13 en OUTPUT&lt;br /&gt;
DDRB |= (1&amp;lt;&amp;lt;5);&lt;br /&gt;
DDRB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Modification d&#039;état ==&lt;br /&gt;
On peut maintenant modifier son état grâce au registre PORTB:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB |= B00100000; // Positionnement de D13 à HIGH&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;5);&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Lecture d&#039;état==&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Il est possible de lire l&#039;état de la broche D13 grâce au registre &#039;&#039;PINB&#039;&#039;. Nous allons utiliser la même technique du masquage que précédemment avec l&#039;opérateur &#039;&#039;ET&#039;&#039;.&lt;br /&gt;
Imaginons que PINB est la valeur suivante &#039;&#039;B00110101&#039;&#039;, PINB &amp;amp;= B00100000 revient à faire:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||1||1||0||1||0||1&lt;br /&gt;
|-&lt;br /&gt;
||ET||0||0||1||0||0||0||0||0&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;9&amp;quot;|&amp;lt;hr&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||1||0||0||0||0||0&lt;br /&gt;
|}&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
On va donc retirer l&#039;état des autres broches sans modifier pour autant celui de la broche D13 !&lt;br /&gt;
Il ne nous reste plus qu&#039;à décaler ce bit de 5 positions vers la droite pour pouvoir le lire. &lt;br /&gt;
&lt;br /&gt;
Les deux écritures suivantes sont équivalentes:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t state;&lt;br /&gt;
state = (PINB &amp;amp; B00100000) &amp;gt;&amp;gt; 5;&lt;br /&gt;
state = (PINB &amp;amp; B00100000) &amp;gt;&amp;gt; PB5;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Rien n’empêche d&#039;utiliser cela pour faire des tests:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Modification de D13 en sortie&lt;br /&gt;
DDRB |= B00100000;&lt;br /&gt;
// Passage de D13 à HIGH&lt;br /&gt;
PORTB |= (1 &amp;lt;&amp;lt; PB5);&lt;br /&gt;
if ((PINB &amp;amp; B00100000) &amp;gt;&amp;gt; PB5) {&lt;br /&gt;
    Serial.println(&amp;quot;D13 is ON&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
// Passage de D13 à LOW&lt;br /&gt;
PORTB &amp;amp;= ~(1 &amp;lt;&amp;lt; PB5);&lt;br /&gt;
if ((PINB &amp;amp; B00100000) &amp;gt;&amp;gt; PB5 == 0) {&lt;br /&gt;
    Serial.println(&amp;quot;D13 is OFF&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Lorsque l&#039;on exécute le code suivant on s&#039;aperçoit que rien ne s&#039;affiche. La raison est simple, la modification de registre est tellement rapide que l&#039;état électrique n&#039;a pas le temps d&#039;être modifié avant la lecture !&lt;br /&gt;
&lt;br /&gt;
Il suffit d&#039;ajouter:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
delay(1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
avant les lignes modifiant les états:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
PORTB |= (1 &amp;lt;&amp;lt; PB5);&lt;br /&gt;
...&lt;br /&gt;
PORTB &amp;amp;= ~(1 &amp;lt;&amp;lt; PB5);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Performances =&lt;br /&gt;
La &#039;&#039;&#039;véritable&#039;&#039;&#039; interrogation réside dans le faite de savoir si le jeu en vaut la chandelle ? Est-il intéressant de sacrifier la simplicité d&#039;écriture et la compréhension du code pour la performance ?&lt;br /&gt;
&lt;br /&gt;
Pour faire un test probant, allumons la led présente sur D13 100.000 fois avec:&lt;br /&gt;
* dans un premier temps &#039;&#039;pinMode()&#039;&#039; et &#039;&#039;digitalWrite()&#039;&#039;;&lt;br /&gt;
* dans un second temps &#039;&#039;DDRB&#039;&#039; et &#039;&#039;PORTB&#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
{|width=&amp;quot;100%&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Voila le premier test:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define LIMIT 100000&lt;br /&gt;
void setup() {&lt;br /&gt;
  Serial.begin(9600);&lt;br /&gt;
  int32_t timer = millis();&lt;br /&gt;
  pinMode(13, OUTPUT);&lt;br /&gt;
  for (uint32_t i = 0; i &amp;lt; LIMIT; i++) {&lt;br /&gt;
    digitalWrite(13, HIGH);&lt;br /&gt;
    digitalWrite(13, LOW);&lt;br /&gt;
  }&lt;br /&gt;
  int32_t elapsed = millis()  - timer;&lt;br /&gt;
  Serial.print(&amp;quot;Temps d&#039;exécution: &amp;quot;);&lt;br /&gt;
  Serial.println(elapsed);&lt;br /&gt;
}&lt;br /&gt;
void loop() {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Le croquis utilise &#039;&#039;&#039;2138&#039;&#039;&#039; octets et le résultat est:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Temps d’exécution: 709&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Voila le deuxième test:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define LIMIT 100000&lt;br /&gt;
void setup() {&lt;br /&gt;
  Serial.begin(9600);&lt;br /&gt;
  int32_t timer = millis();&lt;br /&gt;
  DDRB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
  for(uint32_t i = 0; i &amp;lt; LIMIT; i++){&lt;br /&gt;
    PORTB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
    PORTB &amp;amp;= ~(1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
  }&lt;br /&gt;
  int32_t elapsed = millis()  - timer;&lt;br /&gt;
  Serial.print(&amp;quot;Temps d&#039;exécution: &amp;quot;);&lt;br /&gt;
  Serial.println(elapsed);&lt;br /&gt;
}&lt;br /&gt;
void loop() {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Le croquis utilise &#039;&#039;&#039;1844&#039;&#039;&#039; octets et le résultat est:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Temps d’exécution: 62&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
On peut constater que le code utilisant les registre est non seulement plus léger (&#039;&#039;&#039;13,67%&#039;&#039;&#039;) mais également plus rapide (&#039;&#039;&#039;11.42&#039;&#039;&#039; fois) !&lt;/div&gt;</summary>
		<author><name>Magsss</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=Atmega328_registers&amp;diff=3298</id>
		<title>Atmega328 registers</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=Atmega328_registers&amp;diff=3298"/>
		<updated>2018-11-01T18:44:13Z</updated>

		<summary type="html">&lt;p&gt;Magsss : /* Modification de sens */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Lorsque l&#039;on commence à écrire des programmes qui sortent de l&#039;ordinaire ou que l&#039;on veut pousser un microcontrôleur à la limite de ses capacités, il est obligatoire de descendre au niveau des registres.&lt;br /&gt;
Seulement voilà, la manipulation de registres ne s&#039;improvise pas, nécessite de connaître le hardware que l&#039;on utilise et, de par sa nature, est &#039;&#039;&#039;spécifique&#039;&#039;&#039; à un type de microcontrôleur !&lt;br /&gt;
&lt;br /&gt;
Manipuler les registres implique donc de sacrifier la portabilité du code, offerte par l&#039;utilisation des fonctions &#039;&#039;haut niveau&#039;&#039; comme &#039;&#039;digitalWrite()&#039;&#039;, au profit de la vitesse d&#039;exécution et de la compacité du code.&lt;br /&gt;
&lt;br /&gt;
Les fonctions comme &#039;&#039;digitalWrite()&#039;&#039;, &#039;&#039;digitalRead()&#039;&#039;, &#039;&#039;pinMode()&#039;&#039; permettent au développeur d&#039;accomplir une action sans avoir à se soucier du type de microcontrôleur utilisé mais, in fine, elles vont elles-mêmes manipuler les registres pour accomplir ces tâches !&lt;br /&gt;
&lt;br /&gt;
= Le cas de l&#039;ATmega328 =&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
L&#039;AtMega328 est le microcontrôleur utilisé sur les Arduino Uno, pro, nano et bon nombre de circuits électroniques d&#039;autres marques ! Ci à droite, l&#039;association entre le nom des broches et leurs positions dans le registre.&lt;br /&gt;
&lt;br /&gt;
On peut remarquer que :&lt;br /&gt;
* les broches D0, D1, D2, D3, D4, D5 et D6 font partie du groupe &#039;&#039;&#039;D&#039;&#039;&#039; (P&#039;&#039;&#039;D&#039;&#039;&#039;);&lt;br /&gt;
* les broches D8, D9, D10, D11, D12 et D13 font partie du groupe &#039;&#039;&#039;B&#039;&#039;&#039; (P&#039;&#039;&#039;B&#039;&#039;&#039;);&lt;br /&gt;
* les broches analogiques font partie du groupe &#039;&#039;&#039;C&#039;&#039;&#039; (P&#039;&#039;&#039;C&#039;&#039;&#039;).&lt;br /&gt;
A chacun de ces groupes on peut associer trois registres :&lt;br /&gt;
* PORT &amp;amp;rarr; pour positionner l&#039;état du port (HIGH ou LOW);&lt;br /&gt;
* PIN &amp;amp;rarr; pour lire l&#039;état d&#039;un port;&lt;br /&gt;
* DDR (&#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;D&#039;&#039;&#039;irection &#039;&#039;&#039;R&#039;&#039;&#039;egister) &amp;amp;rarr; pour positionner le sens d&#039;un port (INPUT, OUTPUT);&lt;br /&gt;
On se retrouve donc avec 9 registres manipulables:&lt;br /&gt;
* PORTD, PIND et DDRD pour les broches D0, D1, D2, D3, D4, D5 et D6;&lt;br /&gt;
* PORTB, PINB et DDRB pour les broches D8, D9, D10, D11, D12 et D13;&lt;br /&gt;
* PORTC, PINC et DDRC pour les broches analogiques.&lt;br /&gt;
La position du bit dans le registre est déterminé par le numéro attenant à la lettre du groupe. Par exemple, si on prend le cas de la broche D13, c&#039;est le 5ème bit du groupe B (PB&#039;&#039;&#039;5&#039;&#039;&#039;) &lt;br /&gt;
[[Fichier:Mapping D13 atmega328.png]]&lt;br /&gt;
.&lt;br /&gt;
||&lt;br /&gt;
[[Fichier:Atmega328 pin mapping.png|centré]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Comment manipuler un registre ? =&lt;br /&gt;
Maintenant que l&#039;on a identifié le groupe et la position du bit des broches dans les registres, nous pouvons passer à la manipulation !&lt;br /&gt;
&lt;br /&gt;
Reprenons l&#039;exemple de la broche D13. Pour modifier son état, il suffit de modifier la valeur de PORTB:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB = B00100000; // pour mettre D13 à HIGH&lt;br /&gt;
PORTB = B00000000; // pour mettre D13 à LOW&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Seulement voilà, on sait que sa position dans les registres correspond au bit numéro 6 mais on n&#039;a absolument aucune idée de la valeur de PORTB au moment de la modification !&lt;br /&gt;
La manipulation précédente va certainement modifier l&#039;état de D13 mais va aussi modifier l&#039;état des autre broches du groupe &#039;&#039;&#039;B&#039;&#039;&#039;. On ne peut donc pas agir de la sorte, il faut utiliser un opérateur logique pour modifier les registres et on va distinguer deux cas de figures: le passage à &#039;&#039;1&#039;&#039; et celui à &#039;&#039;0&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Passage à &#039;&#039;1&#039;&#039; ==&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;40%&amp;quot;|&lt;br /&gt;
Pour modifier l&#039;état d&#039;un registre à &#039;&#039;1&#039;&#039; sans modifier l&#039;état des autres registres nous allons utiliser l&#039;opérateur &#039;&#039;OU&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Si on reprend l&#039;exemple précédent, nous allons plutôt faire cela:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB |= B00100000; // pour mettre seulement D13 à HIGH&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Imaginons que PORTB est la valeur suivante &#039;&#039;B00010101&#039;&#039;, PORTB |= B00100000 revient à faire:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||0||1||0||1||0||1&lt;br /&gt;
|-&lt;br /&gt;
||ou||0||0||1||0||0||0||0||0&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;9&amp;quot;|&amp;lt;hr&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||1||1||0||1||0||1&lt;br /&gt;
|}&lt;br /&gt;
On a donc réussi à modifier la valeur de D13 sans modifier l&#039;état des autres broches !&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|width=&amp;quot;12%&amp;quot; valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
Table de vérité de OU&lt;br /&gt;
{|border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
A&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
B&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
A OU B&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|}&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;40%&amp;quot;|&lt;br /&gt;
On peut également effectuer cette manipulation par décalage de bit. Dans l&#039;exemple précédent nous avons utilisé &#039;&#039;B00100000&#039;&#039; pour faire le OU mais on aurait pu utiliser l&#039;écriture &#039;&#039;(1&amp;lt;&amp;lt;5)&#039;&#039; ou même la constante prévue à cet effet &#039;&#039;(1&amp;lt;&amp;lt;PB5)&#039;&#039;.&lt;br /&gt;
Le chiffre &#039;&#039;1&#039;&#039; est un entier qui s&#039;écrit en binaire comme cela:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||position||7||6||5||4||3||2||1||0&lt;br /&gt;
|-&lt;br /&gt;
||valeur||0||0||0||0||0||0||0||1&lt;br /&gt;
|}&lt;br /&gt;
Il nous suffit donc de le décaler de 5 positions vers la gauche pour obtenir B00100000:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||position||7||6||5||4||3||2||1||0&lt;br /&gt;
|-&lt;br /&gt;
||valeur||0||0||1||0||0||0||0||0&lt;br /&gt;
|}&lt;br /&gt;
Les trois écritures suivantes sont donc équivalentes:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB |= B00100000;&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;5);&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Passage à &#039;&#039;0&#039;&#039; ==&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;40%&amp;quot;|&lt;br /&gt;
Pour modifier l&#039;état d&#039;un registre à &#039;&#039;1&#039;&#039; sans modifier l&#039;état des autres registres nous allons utiliser l&#039;opérateur &#039;&#039;ET&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Si on reprend l&#039;exemple précédent, nous allons plutôt faire cela:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB &amp;amp;= ~B00100000; // pour mettre seulement D13 à LOW&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
L&#039;opérateur &#039;&#039;~&#039;&#039; permet d&#039;obtenir le [https://fr.wikipedia.org/wiki/Compl%C3%A9ment_%C3%A0_un complément A1] d&#039;un nombre binaire: B00100000 devient alors B11011111.&lt;br /&gt;
&lt;br /&gt;
Imaginons que PORTB est la valeur suivante &#039;&#039;B00010101&#039;&#039;, PORTB &amp;amp;= ~B00100000 revient à faire:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||0||1||0||1||0||1&lt;br /&gt;
|-&lt;br /&gt;
||ET||1||1||0||1||1||1||1||1&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;9&amp;quot;|&amp;lt;hr&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||0||1||0||1||0||1&lt;br /&gt;
|}&lt;br /&gt;
On a donc réussi à modifier la valeur de D13 sans modifier l&#039;état des autres broches !&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|width=&amp;quot;12%&amp;quot; valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
Table de vérité de ET&lt;br /&gt;
{|border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
A&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
B&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
A ET B&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|}&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;40%&amp;quot;|&lt;br /&gt;
On peut également effectuer cette manipulation par décalage de bit. Dans l&#039;exemple précédent nous avons utilisé &#039;&#039;~B00100000&#039;&#039; pour faire le OU mais on aurait pu utiliser l&#039;écriture &#039;&#039;~(1&amp;lt;&amp;lt;5)&#039;&#039; ou même la constante prévue à cet effet &#039;&#039;~(1&amp;lt;&amp;lt;PB5)&#039;&#039;..&lt;br /&gt;
Les trois écritures suivantes sont donc équivalentes:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB &amp;amp;= ~B00100000;&lt;br /&gt;
PORTB &amp;amp;= ~(1&amp;lt;&amp;lt;5);&lt;br /&gt;
PORTB &amp;amp;= ~(1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Modification de l&#039;état d&#039;une broche =&lt;br /&gt;
Dans l&#039;exemple suivant nous allons modifier l&#039;état de la broche D13 pour la faire clignoter.&lt;br /&gt;
==Modification de sens ==&lt;br /&gt;
Dans un premier temps il faut la placer en sortie et pour cela nous allons manipuler le registre &#039;&#039;DDRB&#039;&#039;:&lt;br /&gt;
* la valeur &#039;&#039;1&#039;&#039; positionne la broche en sortie (&#039;&#039;OUTPUT&#039;&#039;);&lt;br /&gt;
* la valeur &#039;&#039;0&#039;&#039; positionne la broche en entrée (&#039;&#039;INPUT&#039;&#039;), si l&#039;état est positionné à &#039;&#039;1&#039;&#039; cela active la résistance de &#039;&#039;pullup&#039;&#039; (&#039;&#039;INPUT_PULLUP&#039;&#039;).&lt;br /&gt;
Nous allons donc modifier le registre DDRB comme suit, les trois notations sont équivalentes:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
DDRB |= B00100000; // Positionnement de D13 en OUTPUT&lt;br /&gt;
DDRB |= (1&amp;lt;&amp;lt;5);&lt;br /&gt;
DDRB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Modification d&#039;état ==&lt;br /&gt;
On peut maintenant modifier son état grâce au registre PORTB:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB |= B00100000; // Positionnement de D13 à HIGH&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;5);&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Lecture d&#039;état==&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Il est possible de lire l&#039;état de la broche D13 grâce au registre &#039;&#039;PINB&#039;&#039;. Nous allons utiliser la même technique du masquage que précédemment avec l&#039;opérateur &#039;&#039;ET&#039;&#039;.&lt;br /&gt;
Imaginons que PINB est la valeur suivante &#039;&#039;B00110101&#039;&#039;, PINB &amp;amp;= B00100000 revient à faire:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||1||1||0||1||0||1&lt;br /&gt;
|-&lt;br /&gt;
||ET||0||0||1||0||0||0||0||0&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;9&amp;quot;|&amp;lt;hr&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||1||0||0||0||0||0&lt;br /&gt;
|}&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
On va donc retirer l&#039;état des autres broches sans modifier pour autant celui de la broche D13 !&lt;br /&gt;
Il ne nous reste plus qu&#039;à décaler ce bit de 5 positions vers la droite pour pouvoir le lire. &lt;br /&gt;
&lt;br /&gt;
Les deux écritures suivantes sont équivalentes:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t state;&lt;br /&gt;
state = (PINB &amp;amp; B00100000) &amp;gt;&amp;gt; 5;&lt;br /&gt;
state = (PINB &amp;amp; B00100000) &amp;gt;&amp;gt; PB5;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Rien n’empêche d&#039;utiliser cela pour faire des tests:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Modification de D13 en sortie&lt;br /&gt;
DDRB |= B00100000;&lt;br /&gt;
// Passage de D13 à HIGH&lt;br /&gt;
PORTB |= (1 &amp;lt;&amp;lt; PB5);&lt;br /&gt;
if ((PINB &amp;amp; B00100000) &amp;gt;&amp;gt; PB5) {&lt;br /&gt;
    Serial.println(&amp;quot;D13 is ON&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
// Passage de D13 à LOW&lt;br /&gt;
PORTB &amp;amp;= ~(1 &amp;lt;&amp;lt; PB5);&lt;br /&gt;
if ((PINB &amp;amp; B00100000) &amp;gt;&amp;gt; PB5 == 0) {&lt;br /&gt;
    Serial.println(&amp;quot;D13 is OFF&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Lorsque l&#039;on exécute le code suivant on s&#039;aperçoit que rien ne s&#039;affiche. La raison est simple, la modification de registre est tellement rapide que l&#039;état électrique n&#039;a pas le temps d&#039;être modifier avant la lecture !&lt;br /&gt;
&lt;br /&gt;
Il suffit d&#039;ajouter:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
delay(1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Avant les lignes modifiant les états:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
PORTB |= (1 &amp;lt;&amp;lt; PB5);&lt;br /&gt;
...&lt;br /&gt;
PORTB &amp;amp;= ~(1 &amp;lt;&amp;lt; PB5);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Performances =&lt;br /&gt;
La &#039;&#039;&#039;véritable&#039;&#039;&#039; interrogation réside dans le faite de savoir si le jeu en vaut la chandelle ? Est-il intéressant de sacrifier la simplicité d&#039;écriture et la compréhension du code pour la performance ?&lt;br /&gt;
&lt;br /&gt;
Pour faire un test probant, allumons la led présente sur D13 100.000 fois avec:&lt;br /&gt;
* dans un premier temps &#039;&#039;pinMode()&#039;&#039; et &#039;&#039;digitalWrite()&#039;&#039;;&lt;br /&gt;
* dans un second temps &#039;&#039;DDRB&#039;&#039; et &#039;&#039;PORTB&#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
{|width=&amp;quot;100%&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Voila le premier test:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define LIMIT 100000&lt;br /&gt;
void setup() {&lt;br /&gt;
  Serial.begin(9600);&lt;br /&gt;
  int32_t timer = millis();&lt;br /&gt;
  pinMode(13, OUTPUT);&lt;br /&gt;
  for (uint32_t i = 0; i &amp;lt; LIMIT; i++) {&lt;br /&gt;
    digitalWrite(13, HIGH);&lt;br /&gt;
    digitalWrite(13, LOW);&lt;br /&gt;
  }&lt;br /&gt;
  int32_t elapsed = millis()  - timer;&lt;br /&gt;
  Serial.print(&amp;quot;Temps d&#039;exécution: &amp;quot;);&lt;br /&gt;
  Serial.println(elapsed);&lt;br /&gt;
}&lt;br /&gt;
void loop() {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Le croquis utilise &#039;&#039;&#039;2138&#039;&#039;&#039; octets et le résultat est:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Temps d’exécution: 709&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Voila le deuxième test:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define LIMIT 100000&lt;br /&gt;
void setup() {&lt;br /&gt;
  Serial.begin(9600);&lt;br /&gt;
  int32_t timer = millis();&lt;br /&gt;
  DDRB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
  for(uint32_t i = 0; i &amp;lt; LIMIT; i++){&lt;br /&gt;
    PORTB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
    PORTB &amp;amp;= ~(1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
  }&lt;br /&gt;
  int32_t elapsed = millis()  - timer;&lt;br /&gt;
  Serial.print(&amp;quot;Temps d&#039;exécution: &amp;quot;);&lt;br /&gt;
  Serial.println(elapsed);&lt;br /&gt;
}&lt;br /&gt;
void loop() {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Le croquis utilise &#039;&#039;&#039;1844&#039;&#039;&#039; octets et le résultat est:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Temps d’exécution: 62&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
On peut constater que le code utilisant les registre est non seulement plus léger (&#039;&#039;&#039;13,67%&#039;&#039;&#039;) mais également plus rapide (&#039;&#039;&#039;11.42&#039;&#039;&#039; fois) !&lt;/div&gt;</summary>
		<author><name>Magsss</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=Atmega328_registers&amp;diff=3297</id>
		<title>Atmega328 registers</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=Atmega328_registers&amp;diff=3297"/>
		<updated>2018-11-01T18:41:53Z</updated>

		<summary type="html">&lt;p&gt;Magsss : /* Passage à 1 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Lorsque l&#039;on commence à écrire des programmes qui sortent de l&#039;ordinaire ou que l&#039;on veut pousser un microcontrôleur à la limite de ses capacités, il est obligatoire de descendre au niveau des registres.&lt;br /&gt;
Seulement voilà, la manipulation de registres ne s&#039;improvise pas, nécessite de connaître le hardware que l&#039;on utilise et, de par sa nature, est &#039;&#039;&#039;spécifique&#039;&#039;&#039; à un type de microcontrôleur !&lt;br /&gt;
&lt;br /&gt;
Manipuler les registres implique donc de sacrifier la portabilité du code, offerte par l&#039;utilisation des fonctions &#039;&#039;haut niveau&#039;&#039; comme &#039;&#039;digitalWrite()&#039;&#039;, au profit de la vitesse d&#039;exécution et de la compacité du code.&lt;br /&gt;
&lt;br /&gt;
Les fonctions comme &#039;&#039;digitalWrite()&#039;&#039;, &#039;&#039;digitalRead()&#039;&#039;, &#039;&#039;pinMode()&#039;&#039; permettent au développeur d&#039;accomplir une action sans avoir à se soucier du type de microcontrôleur utilisé mais, in fine, elles vont elles-mêmes manipuler les registres pour accomplir ces tâches !&lt;br /&gt;
&lt;br /&gt;
= Le cas de l&#039;ATmega328 =&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
L&#039;AtMega328 est le microcontrôleur utilisé sur les Arduino Uno, pro, nano et bon nombre de circuits électroniques d&#039;autres marques ! Ci à droite, l&#039;association entre le nom des broches et leurs positions dans le registre.&lt;br /&gt;
&lt;br /&gt;
On peut remarquer que :&lt;br /&gt;
* les broches D0, D1, D2, D3, D4, D5 et D6 font partie du groupe &#039;&#039;&#039;D&#039;&#039;&#039; (P&#039;&#039;&#039;D&#039;&#039;&#039;);&lt;br /&gt;
* les broches D8, D9, D10, D11, D12 et D13 font partie du groupe &#039;&#039;&#039;B&#039;&#039;&#039; (P&#039;&#039;&#039;B&#039;&#039;&#039;);&lt;br /&gt;
* les broches analogiques font partie du groupe &#039;&#039;&#039;C&#039;&#039;&#039; (P&#039;&#039;&#039;C&#039;&#039;&#039;).&lt;br /&gt;
A chacun de ces groupes on peut associer trois registres :&lt;br /&gt;
* PORT &amp;amp;rarr; pour positionner l&#039;état du port (HIGH ou LOW);&lt;br /&gt;
* PIN &amp;amp;rarr; pour lire l&#039;état d&#039;un port;&lt;br /&gt;
* DDR (&#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;D&#039;&#039;&#039;irection &#039;&#039;&#039;R&#039;&#039;&#039;egister) &amp;amp;rarr; pour positionner le sens d&#039;un port (INPUT, OUTPUT);&lt;br /&gt;
On se retrouve donc avec 9 registres manipulables:&lt;br /&gt;
* PORTD, PIND et DDRD pour les broches D0, D1, D2, D3, D4, D5 et D6;&lt;br /&gt;
* PORTB, PINB et DDRB pour les broches D8, D9, D10, D11, D12 et D13;&lt;br /&gt;
* PORTC, PINC et DDRC pour les broches analogiques.&lt;br /&gt;
La position du bit dans le registre est déterminé par le numéro attenant à la lettre du groupe. Par exemple, si on prend le cas de la broche D13, c&#039;est le 5ème bit du groupe B (PB&#039;&#039;&#039;5&#039;&#039;&#039;) &lt;br /&gt;
[[Fichier:Mapping D13 atmega328.png]]&lt;br /&gt;
.&lt;br /&gt;
||&lt;br /&gt;
[[Fichier:Atmega328 pin mapping.png|centré]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Comment manipuler un registre ? =&lt;br /&gt;
Maintenant que l&#039;on a identifié le groupe et la position du bit des broches dans les registres, nous pouvons passer à la manipulation !&lt;br /&gt;
&lt;br /&gt;
Reprenons l&#039;exemple de la broche D13. Pour modifier son état, il suffit de modifier la valeur de PORTB:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB = B00100000; // pour mettre D13 à HIGH&lt;br /&gt;
PORTB = B00000000; // pour mettre D13 à LOW&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Seulement voilà, on sait que sa position dans les registres correspond au bit numéro 6 mais on n&#039;a absolument aucune idée de la valeur de PORTB au moment de la modification !&lt;br /&gt;
La manipulation précédente va certainement modifier l&#039;état de D13 mais va aussi modifier l&#039;état des autre broches du groupe &#039;&#039;&#039;B&#039;&#039;&#039;. On ne peut donc pas agir de la sorte, il faut utiliser un opérateur logique pour modifier les registres et on va distinguer deux cas de figures: le passage à &#039;&#039;1&#039;&#039; et celui à &#039;&#039;0&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Passage à &#039;&#039;1&#039;&#039; ==&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;40%&amp;quot;|&lt;br /&gt;
Pour modifier l&#039;état d&#039;un registre à &#039;&#039;1&#039;&#039; sans modifier l&#039;état des autres registres nous allons utiliser l&#039;opérateur &#039;&#039;OU&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Si on reprend l&#039;exemple précédent, nous allons plutôt faire cela:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB |= B00100000; // pour mettre seulement D13 à HIGH&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Imaginons que PORTB est la valeur suivante &#039;&#039;B00010101&#039;&#039;, PORTB |= B00100000 revient à faire:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||0||1||0||1||0||1&lt;br /&gt;
|-&lt;br /&gt;
||ou||0||0||1||0||0||0||0||0&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;9&amp;quot;|&amp;lt;hr&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||1||1||0||1||0||1&lt;br /&gt;
|}&lt;br /&gt;
On a donc réussi à modifier la valeur de D13 sans modifier l&#039;état des autres broches !&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|width=&amp;quot;12%&amp;quot; valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
Table de vérité de OU&lt;br /&gt;
{|border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
A&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
B&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
A OU B&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|}&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;40%&amp;quot;|&lt;br /&gt;
On peut également effectuer cette manipulation par décalage de bit. Dans l&#039;exemple précédent nous avons utilisé &#039;&#039;B00100000&#039;&#039; pour faire le OU mais on aurait pu utiliser l&#039;écriture &#039;&#039;(1&amp;lt;&amp;lt;5)&#039;&#039; ou même la constante prévue à cet effet &#039;&#039;(1&amp;lt;&amp;lt;PB5)&#039;&#039;.&lt;br /&gt;
Le chiffre &#039;&#039;1&#039;&#039; est un entier qui s&#039;écrit en binaire comme cela:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||position||7||6||5||4||3||2||1||0&lt;br /&gt;
|-&lt;br /&gt;
||valeur||0||0||0||0||0||0||0||1&lt;br /&gt;
|}&lt;br /&gt;
Il nous suffit donc de le décaler de 5 positions vers la gauche pour obtenir B00100000:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||position||7||6||5||4||3||2||1||0&lt;br /&gt;
|-&lt;br /&gt;
||valeur||0||0||1||0||0||0||0||0&lt;br /&gt;
|}&lt;br /&gt;
Les trois écritures suivantes sont donc équivalentes:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB |= B00100000;&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;5);&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Passage à &#039;&#039;0&#039;&#039; ==&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;40%&amp;quot;|&lt;br /&gt;
Pour modifier l&#039;état d&#039;un registre à &#039;&#039;1&#039;&#039; sans modifier l&#039;état des autres registres nous allons utiliser l&#039;opérateur &#039;&#039;ET&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Si on reprend l&#039;exemple précédent, nous allons plutôt faire cela:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB &amp;amp;= ~B00100000; // pour mettre seulement D13 à LOW&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
L&#039;opérateur &#039;&#039;~&#039;&#039; permet d&#039;obtenir le [https://fr.wikipedia.org/wiki/Compl%C3%A9ment_%C3%A0_un complément A1] d&#039;un nombre binaire: B00100000 devient alors B11011111.&lt;br /&gt;
&lt;br /&gt;
Imaginons que PORTB est la valeur suivante &#039;&#039;B00010101&#039;&#039;, PORTB &amp;amp;= ~B00100000 revient à faire:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||0||1||0||1||0||1&lt;br /&gt;
|-&lt;br /&gt;
||ET||1||1||0||1||1||1||1||1&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;9&amp;quot;|&amp;lt;hr&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||0||1||0||1||0||1&lt;br /&gt;
|}&lt;br /&gt;
On a donc réussi à modifier la valeur de D13 sans modifier l&#039;état des autres broches !&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|width=&amp;quot;12%&amp;quot; valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
Table de vérité de ET&lt;br /&gt;
{|border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
A&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
B&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
A ET B&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|}&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;40%&amp;quot;|&lt;br /&gt;
On peut également effectuer cette manipulation par décalage de bit. Dans l&#039;exemple précédent nous avons utilisé &#039;&#039;~B00100000&#039;&#039; pour faire le OU mais on aurait pu utiliser l&#039;écriture &#039;&#039;~(1&amp;lt;&amp;lt;5)&#039;&#039; ou même la constante prévue à cet effet &#039;&#039;~(1&amp;lt;&amp;lt;PB5)&#039;&#039;..&lt;br /&gt;
Les trois écritures suivantes sont donc équivalentes:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB &amp;amp;= ~B00100000;&lt;br /&gt;
PORTB &amp;amp;= ~(1&amp;lt;&amp;lt;5);&lt;br /&gt;
PORTB &amp;amp;= ~(1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Modification de l&#039;état d&#039;une broche =&lt;br /&gt;
Dans l&#039;exemple suivant nous allons modifier l&#039;état de la broche D13 pour la faire clignoter.&lt;br /&gt;
==Modification de sens ==&lt;br /&gt;
Dans un premier temps il faut la placer en sortie et pour cela nous allons manipuler le registre &#039;&#039;DDRB&#039;&#039;:&lt;br /&gt;
* la valeur &#039;&#039;1&#039;&#039; positionne la broche en sortie (&#039;&#039;OUTPUT&#039;&#039;);&lt;br /&gt;
* la valeur &#039;&#039;0&#039;&#039; positionne la broche en entrée (&#039;&#039;INPUT&#039;&#039;), si la l&#039;état est positionné à &#039;&#039;1&#039;&#039; cela active la résistance de &#039;&#039;pullup&#039;&#039; (&#039;&#039;INPUT_PULLUP&#039;&#039;).&lt;br /&gt;
Nous allons donc modifier le registre DDRB comme suit, les trois notations sont équivalentes:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
DDRB |= B00100000; // Positionnement de D13 en OUTPUT&lt;br /&gt;
DDRB |= (1&amp;lt;&amp;lt;5);&lt;br /&gt;
DDRB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Modification d&#039;état ==&lt;br /&gt;
On peut maintenant modifier son état grâce au registre PORTB:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB |= B00100000; // Positionnement de D13 à HIGH&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;5);&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Lecture d&#039;état==&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Il est possible de lire l&#039;état de la broche D13 grâce au registre &#039;&#039;PINB&#039;&#039;. Nous allons utiliser la même technique du masquage que précédemment avec l&#039;opérateur &#039;&#039;ET&#039;&#039;.&lt;br /&gt;
Imaginons que PINB est la valeur suivante &#039;&#039;B00110101&#039;&#039;, PINB &amp;amp;= B00100000 revient à faire:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||1||1||0||1||0||1&lt;br /&gt;
|-&lt;br /&gt;
||ET||0||0||1||0||0||0||0||0&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;9&amp;quot;|&amp;lt;hr&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||1||0||0||0||0||0&lt;br /&gt;
|}&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
On va donc retirer l&#039;état des autres broches sans modifier pour autant celui de la broche D13 !&lt;br /&gt;
Il ne nous reste plus qu&#039;à décaler ce bit de 5 positions vers la droite pour pouvoir le lire. &lt;br /&gt;
&lt;br /&gt;
Les deux écritures suivantes sont équivalentes:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t state;&lt;br /&gt;
state = (PINB &amp;amp; B00100000) &amp;gt;&amp;gt; 5;&lt;br /&gt;
state = (PINB &amp;amp; B00100000) &amp;gt;&amp;gt; PB5;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Rien n’empêche d&#039;utiliser cela pour faire des tests:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Modification de D13 en sortie&lt;br /&gt;
DDRB |= B00100000;&lt;br /&gt;
// Passage de D13 à HIGH&lt;br /&gt;
PORTB |= (1 &amp;lt;&amp;lt; PB5);&lt;br /&gt;
if ((PINB &amp;amp; B00100000) &amp;gt;&amp;gt; PB5) {&lt;br /&gt;
    Serial.println(&amp;quot;D13 is ON&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
// Passage de D13 à LOW&lt;br /&gt;
PORTB &amp;amp;= ~(1 &amp;lt;&amp;lt; PB5);&lt;br /&gt;
if ((PINB &amp;amp; B00100000) &amp;gt;&amp;gt; PB5 == 0) {&lt;br /&gt;
    Serial.println(&amp;quot;D13 is OFF&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Lorsque l&#039;on exécute le code suivant on s&#039;aperçoit que rien ne s&#039;affiche. La raison est simple, la modification de registre est tellement rapide que l&#039;état électrique n&#039;a pas le temps d&#039;être modifier avant la lecture !&lt;br /&gt;
&lt;br /&gt;
Il suffit d&#039;ajouter:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
delay(1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Avant les lignes modifiant les états:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
PORTB |= (1 &amp;lt;&amp;lt; PB5);&lt;br /&gt;
...&lt;br /&gt;
PORTB &amp;amp;= ~(1 &amp;lt;&amp;lt; PB5);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Performances =&lt;br /&gt;
La &#039;&#039;&#039;véritable&#039;&#039;&#039; interrogation réside dans le faite de savoir si le jeu en vaut la chandelle ? Est-il intéressant de sacrifier la simplicité d&#039;écriture et la compréhension du code pour la performance ?&lt;br /&gt;
&lt;br /&gt;
Pour faire un test probant, allumons la led présente sur D13 100.000 fois avec:&lt;br /&gt;
* dans un premier temps &#039;&#039;pinMode()&#039;&#039; et &#039;&#039;digitalWrite()&#039;&#039;;&lt;br /&gt;
* dans un second temps &#039;&#039;DDRB&#039;&#039; et &#039;&#039;PORTB&#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
{|width=&amp;quot;100%&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Voila le premier test:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define LIMIT 100000&lt;br /&gt;
void setup() {&lt;br /&gt;
  Serial.begin(9600);&lt;br /&gt;
  int32_t timer = millis();&lt;br /&gt;
  pinMode(13, OUTPUT);&lt;br /&gt;
  for (uint32_t i = 0; i &amp;lt; LIMIT; i++) {&lt;br /&gt;
    digitalWrite(13, HIGH);&lt;br /&gt;
    digitalWrite(13, LOW);&lt;br /&gt;
  }&lt;br /&gt;
  int32_t elapsed = millis()  - timer;&lt;br /&gt;
  Serial.print(&amp;quot;Temps d&#039;exécution: &amp;quot;);&lt;br /&gt;
  Serial.println(elapsed);&lt;br /&gt;
}&lt;br /&gt;
void loop() {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Le croquis utilise &#039;&#039;&#039;2138&#039;&#039;&#039; octets et le résultat est:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Temps d’exécution: 709&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Voila le deuxième test:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define LIMIT 100000&lt;br /&gt;
void setup() {&lt;br /&gt;
  Serial.begin(9600);&lt;br /&gt;
  int32_t timer = millis();&lt;br /&gt;
  DDRB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
  for(uint32_t i = 0; i &amp;lt; LIMIT; i++){&lt;br /&gt;
    PORTB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
    PORTB &amp;amp;= ~(1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
  }&lt;br /&gt;
  int32_t elapsed = millis()  - timer;&lt;br /&gt;
  Serial.print(&amp;quot;Temps d&#039;exécution: &amp;quot;);&lt;br /&gt;
  Serial.println(elapsed);&lt;br /&gt;
}&lt;br /&gt;
void loop() {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Le croquis utilise &#039;&#039;&#039;1844&#039;&#039;&#039; octets et le résultat est:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Temps d’exécution: 62&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
On peut constater que le code utilisant les registre est non seulement plus léger (&#039;&#039;&#039;13,67%&#039;&#039;&#039;) mais également plus rapide (&#039;&#039;&#039;11.42&#039;&#039;&#039; fois) !&lt;/div&gt;</summary>
		<author><name>Magsss</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=Atmega328_registers&amp;diff=3296</id>
		<title>Atmega328 registers</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=Atmega328_registers&amp;diff=3296"/>
		<updated>2018-11-01T18:40:12Z</updated>

		<summary type="html">&lt;p&gt;Magsss : /* Comment manipuler un registre ? */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Lorsque l&#039;on commence à écrire des programmes qui sortent de l&#039;ordinaire ou que l&#039;on veut pousser un microcontrôleur à la limite de ses capacités, il est obligatoire de descendre au niveau des registres.&lt;br /&gt;
Seulement voilà, la manipulation de registres ne s&#039;improvise pas, nécessite de connaître le hardware que l&#039;on utilise et, de par sa nature, est &#039;&#039;&#039;spécifique&#039;&#039;&#039; à un type de microcontrôleur !&lt;br /&gt;
&lt;br /&gt;
Manipuler les registres implique donc de sacrifier la portabilité du code, offerte par l&#039;utilisation des fonctions &#039;&#039;haut niveau&#039;&#039; comme &#039;&#039;digitalWrite()&#039;&#039;, au profit de la vitesse d&#039;exécution et de la compacité du code.&lt;br /&gt;
&lt;br /&gt;
Les fonctions comme &#039;&#039;digitalWrite()&#039;&#039;, &#039;&#039;digitalRead()&#039;&#039;, &#039;&#039;pinMode()&#039;&#039; permettent au développeur d&#039;accomplir une action sans avoir à se soucier du type de microcontrôleur utilisé mais, in fine, elles vont elles-mêmes manipuler les registres pour accomplir ces tâches !&lt;br /&gt;
&lt;br /&gt;
= Le cas de l&#039;ATmega328 =&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
L&#039;AtMega328 est le microcontrôleur utilisé sur les Arduino Uno, pro, nano et bon nombre de circuits électroniques d&#039;autres marques ! Ci à droite, l&#039;association entre le nom des broches et leurs positions dans le registre.&lt;br /&gt;
&lt;br /&gt;
On peut remarquer que :&lt;br /&gt;
* les broches D0, D1, D2, D3, D4, D5 et D6 font partie du groupe &#039;&#039;&#039;D&#039;&#039;&#039; (P&#039;&#039;&#039;D&#039;&#039;&#039;);&lt;br /&gt;
* les broches D8, D9, D10, D11, D12 et D13 font partie du groupe &#039;&#039;&#039;B&#039;&#039;&#039; (P&#039;&#039;&#039;B&#039;&#039;&#039;);&lt;br /&gt;
* les broches analogiques font partie du groupe &#039;&#039;&#039;C&#039;&#039;&#039; (P&#039;&#039;&#039;C&#039;&#039;&#039;).&lt;br /&gt;
A chacun de ces groupes on peut associer trois registres :&lt;br /&gt;
* PORT &amp;amp;rarr; pour positionner l&#039;état du port (HIGH ou LOW);&lt;br /&gt;
* PIN &amp;amp;rarr; pour lire l&#039;état d&#039;un port;&lt;br /&gt;
* DDR (&#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;D&#039;&#039;&#039;irection &#039;&#039;&#039;R&#039;&#039;&#039;egister) &amp;amp;rarr; pour positionner le sens d&#039;un port (INPUT, OUTPUT);&lt;br /&gt;
On se retrouve donc avec 9 registres manipulables:&lt;br /&gt;
* PORTD, PIND et DDRD pour les broches D0, D1, D2, D3, D4, D5 et D6;&lt;br /&gt;
* PORTB, PINB et DDRB pour les broches D8, D9, D10, D11, D12 et D13;&lt;br /&gt;
* PORTC, PINC et DDRC pour les broches analogiques.&lt;br /&gt;
La position du bit dans le registre est déterminé par le numéro attenant à la lettre du groupe. Par exemple, si on prend le cas de la broche D13, c&#039;est le 5ème bit du groupe B (PB&#039;&#039;&#039;5&#039;&#039;&#039;) &lt;br /&gt;
[[Fichier:Mapping D13 atmega328.png]]&lt;br /&gt;
.&lt;br /&gt;
||&lt;br /&gt;
[[Fichier:Atmega328 pin mapping.png|centré]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Comment manipuler un registre ? =&lt;br /&gt;
Maintenant que l&#039;on a identifié le groupe et la position du bit des broches dans les registres, nous pouvons passer à la manipulation !&lt;br /&gt;
&lt;br /&gt;
Reprenons l&#039;exemple de la broche D13. Pour modifier son état, il suffit de modifier la valeur de PORTB:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB = B00100000; // pour mettre D13 à HIGH&lt;br /&gt;
PORTB = B00000000; // pour mettre D13 à LOW&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Seulement voilà, on sait que sa position dans les registres correspond au bit numéro 6 mais on n&#039;a absolument aucune idée de la valeur de PORTB au moment de la modification !&lt;br /&gt;
La manipulation précédente va certainement modifier l&#039;état de D13 mais va aussi modifier l&#039;état des autre broches du groupe &#039;&#039;&#039;B&#039;&#039;&#039;. On ne peut donc pas agir de la sorte, il faut utiliser un opérateur logique pour modifier les registres et on va distinguer deux cas de figures: le passage à &#039;&#039;1&#039;&#039; et celui à &#039;&#039;0&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Passage à &#039;&#039;1&#039;&#039; ==&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;40%&amp;quot;|&lt;br /&gt;
Pour modifier l&#039;état d&#039;un registre à &#039;&#039;1&#039;&#039; sans modifier l&#039;état des autres registres nous allons utiliser l&#039;opérateur &#039;&#039;OU&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Si on reprend l&#039;exemple précédent, nous allons plutôt faire cela:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB |= B00100000; // pour mettre seulement D13 à HIGH&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Imaginons que PORTB est la valeur suivante &#039;&#039;B00010101&#039;&#039;, PORTB |= B00100000 revient à faire:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||0||1||0||1||0||1&lt;br /&gt;
|-&lt;br /&gt;
||ou||0||0||1||0||0||0||0||0&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;9&amp;quot;|&amp;lt;hr&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||1||1||0||1||0||1&lt;br /&gt;
|}&lt;br /&gt;
On a donc réussi à modifier la valeur de D13 sans modifier l&#039;état des autres broches !&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|width=&amp;quot;12%&amp;quot; valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
Table de vérité de OU&lt;br /&gt;
{|border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
A&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
B&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
A OU B&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|}&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;40%&amp;quot;|&lt;br /&gt;
On peut également effectuer cette manipulation par décalage de bit. Dans l&#039;exemple précédent nous avons utilisé &#039;&#039;B00100000&#039;&#039; pour faire le OU mais on aurait pu utiliser l&#039;écriture &#039;&#039;(1&amp;lt;&amp;lt;5)&#039;&#039; ou même la constante prévue à cet effet &#039;&#039;(1&amp;lt;&amp;lt;PB5)&#039;&#039;.&lt;br /&gt;
Le chiffre &#039;&#039;1&#039;&#039; est un entier qui s&#039;écrit en binaire comme cela:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||position||7||6||5||4||3||2||1||0&lt;br /&gt;
|-&lt;br /&gt;
||valeur||0||0||0||0||0||0||0||1&lt;br /&gt;
|}&lt;br /&gt;
Il nous suffit donc de le décaler de 5 position vers la gauche pour obtenir B00100000:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||position||7||6||5||4||3||2||1||0&lt;br /&gt;
|-&lt;br /&gt;
||valeur||0||0||1||0||0||0||0||0&lt;br /&gt;
|}&lt;br /&gt;
Les trois écritures suivantes sont donc équivalentes:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB |= B00100000;&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;5);&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Passage à &#039;&#039;0&#039;&#039; ==&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;40%&amp;quot;|&lt;br /&gt;
Pour modifier l&#039;état d&#039;un registre à &#039;&#039;1&#039;&#039; sans modifier l&#039;état des autres registres nous allons utiliser l&#039;opérateur &#039;&#039;ET&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Si on reprend l&#039;exemple précédent, nous allons plutôt faire cela:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB &amp;amp;= ~B00100000; // pour mettre seulement D13 à LOW&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
L&#039;opérateur &#039;&#039;~&#039;&#039; permet d&#039;obtenir le [https://fr.wikipedia.org/wiki/Compl%C3%A9ment_%C3%A0_un complément A1] d&#039;un nombre binaire: B00100000 devient alors B11011111.&lt;br /&gt;
&lt;br /&gt;
Imaginons que PORTB est la valeur suivante &#039;&#039;B00010101&#039;&#039;, PORTB &amp;amp;= ~B00100000 revient à faire:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||0||1||0||1||0||1&lt;br /&gt;
|-&lt;br /&gt;
||ET||1||1||0||1||1||1||1||1&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;9&amp;quot;|&amp;lt;hr&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||0||1||0||1||0||1&lt;br /&gt;
|}&lt;br /&gt;
On a donc réussi à modifier la valeur de D13 sans modifier l&#039;état des autres broches !&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|width=&amp;quot;12%&amp;quot; valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
Table de vérité de ET&lt;br /&gt;
{|border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
A&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
B&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
A ET B&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|}&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;40%&amp;quot;|&lt;br /&gt;
On peut également effectuer cette manipulation par décalage de bit. Dans l&#039;exemple précédent nous avons utilisé &#039;&#039;~B00100000&#039;&#039; pour faire le OU mais on aurait pu utiliser l&#039;écriture &#039;&#039;~(1&amp;lt;&amp;lt;5)&#039;&#039; ou même la constante prévue à cet effet &#039;&#039;~(1&amp;lt;&amp;lt;PB5)&#039;&#039;..&lt;br /&gt;
Les trois écritures suivantes sont donc équivalentes:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB &amp;amp;= ~B00100000;&lt;br /&gt;
PORTB &amp;amp;= ~(1&amp;lt;&amp;lt;5);&lt;br /&gt;
PORTB &amp;amp;= ~(1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Modification de l&#039;état d&#039;une broche =&lt;br /&gt;
Dans l&#039;exemple suivant nous allons modifier l&#039;état de la broche D13 pour la faire clignoter.&lt;br /&gt;
==Modification de sens ==&lt;br /&gt;
Dans un premier temps il faut la placer en sortie et pour cela nous allons manipuler le registre &#039;&#039;DDRB&#039;&#039;:&lt;br /&gt;
* la valeur &#039;&#039;1&#039;&#039; positionne la broche en sortie (&#039;&#039;OUTPUT&#039;&#039;);&lt;br /&gt;
* la valeur &#039;&#039;0&#039;&#039; positionne la broche en entrée (&#039;&#039;INPUT&#039;&#039;), si la l&#039;état est positionné à &#039;&#039;1&#039;&#039; cela active la résistance de &#039;&#039;pullup&#039;&#039; (&#039;&#039;INPUT_PULLUP&#039;&#039;).&lt;br /&gt;
Nous allons donc modifier le registre DDRB comme suit, les trois notations sont équivalentes:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
DDRB |= B00100000; // Positionnement de D13 en OUTPUT&lt;br /&gt;
DDRB |= (1&amp;lt;&amp;lt;5);&lt;br /&gt;
DDRB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Modification d&#039;état ==&lt;br /&gt;
On peut maintenant modifier son état grâce au registre PORTB:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB |= B00100000; // Positionnement de D13 à HIGH&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;5);&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Lecture d&#039;état==&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Il est possible de lire l&#039;état de la broche D13 grâce au registre &#039;&#039;PINB&#039;&#039;. Nous allons utiliser la même technique du masquage que précédemment avec l&#039;opérateur &#039;&#039;ET&#039;&#039;.&lt;br /&gt;
Imaginons que PINB est la valeur suivante &#039;&#039;B00110101&#039;&#039;, PINB &amp;amp;= B00100000 revient à faire:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||1||1||0||1||0||1&lt;br /&gt;
|-&lt;br /&gt;
||ET||0||0||1||0||0||0||0||0&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;9&amp;quot;|&amp;lt;hr&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||1||0||0||0||0||0&lt;br /&gt;
|}&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
On va donc retirer l&#039;état des autres broches sans modifier pour autant celui de la broche D13 !&lt;br /&gt;
Il ne nous reste plus qu&#039;à décaler ce bit de 5 positions vers la droite pour pouvoir le lire. &lt;br /&gt;
&lt;br /&gt;
Les deux écritures suivantes sont équivalentes:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t state;&lt;br /&gt;
state = (PINB &amp;amp; B00100000) &amp;gt;&amp;gt; 5;&lt;br /&gt;
state = (PINB &amp;amp; B00100000) &amp;gt;&amp;gt; PB5;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Rien n’empêche d&#039;utiliser cela pour faire des tests:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Modification de D13 en sortie&lt;br /&gt;
DDRB |= B00100000;&lt;br /&gt;
// Passage de D13 à HIGH&lt;br /&gt;
PORTB |= (1 &amp;lt;&amp;lt; PB5);&lt;br /&gt;
if ((PINB &amp;amp; B00100000) &amp;gt;&amp;gt; PB5) {&lt;br /&gt;
    Serial.println(&amp;quot;D13 is ON&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
// Passage de D13 à LOW&lt;br /&gt;
PORTB &amp;amp;= ~(1 &amp;lt;&amp;lt; PB5);&lt;br /&gt;
if ((PINB &amp;amp; B00100000) &amp;gt;&amp;gt; PB5 == 0) {&lt;br /&gt;
    Serial.println(&amp;quot;D13 is OFF&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Lorsque l&#039;on exécute le code suivant on s&#039;aperçoit que rien ne s&#039;affiche. La raison est simple, la modification de registre est tellement rapide que l&#039;état électrique n&#039;a pas le temps d&#039;être modifier avant la lecture !&lt;br /&gt;
&lt;br /&gt;
Il suffit d&#039;ajouter:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
delay(1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Avant les lignes modifiant les états:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
PORTB |= (1 &amp;lt;&amp;lt; PB5);&lt;br /&gt;
...&lt;br /&gt;
PORTB &amp;amp;= ~(1 &amp;lt;&amp;lt; PB5);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Performances =&lt;br /&gt;
La &#039;&#039;&#039;véritable&#039;&#039;&#039; interrogation réside dans le faite de savoir si le jeu en vaut la chandelle ? Est-il intéressant de sacrifier la simplicité d&#039;écriture et la compréhension du code pour la performance ?&lt;br /&gt;
&lt;br /&gt;
Pour faire un test probant, allumons la led présente sur D13 100.000 fois avec:&lt;br /&gt;
* dans un premier temps &#039;&#039;pinMode()&#039;&#039; et &#039;&#039;digitalWrite()&#039;&#039;;&lt;br /&gt;
* dans un second temps &#039;&#039;DDRB&#039;&#039; et &#039;&#039;PORTB&#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
{|width=&amp;quot;100%&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Voila le premier test:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define LIMIT 100000&lt;br /&gt;
void setup() {&lt;br /&gt;
  Serial.begin(9600);&lt;br /&gt;
  int32_t timer = millis();&lt;br /&gt;
  pinMode(13, OUTPUT);&lt;br /&gt;
  for (uint32_t i = 0; i &amp;lt; LIMIT; i++) {&lt;br /&gt;
    digitalWrite(13, HIGH);&lt;br /&gt;
    digitalWrite(13, LOW);&lt;br /&gt;
  }&lt;br /&gt;
  int32_t elapsed = millis()  - timer;&lt;br /&gt;
  Serial.print(&amp;quot;Temps d&#039;exécution: &amp;quot;);&lt;br /&gt;
  Serial.println(elapsed);&lt;br /&gt;
}&lt;br /&gt;
void loop() {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Le croquis utilise &#039;&#039;&#039;2138&#039;&#039;&#039; octets et le résultat est:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Temps d’exécution: 709&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Voila le deuxième test:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define LIMIT 100000&lt;br /&gt;
void setup() {&lt;br /&gt;
  Serial.begin(9600);&lt;br /&gt;
  int32_t timer = millis();&lt;br /&gt;
  DDRB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
  for(uint32_t i = 0; i &amp;lt; LIMIT; i++){&lt;br /&gt;
    PORTB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
    PORTB &amp;amp;= ~(1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
  }&lt;br /&gt;
  int32_t elapsed = millis()  - timer;&lt;br /&gt;
  Serial.print(&amp;quot;Temps d&#039;exécution: &amp;quot;);&lt;br /&gt;
  Serial.println(elapsed);&lt;br /&gt;
}&lt;br /&gt;
void loop() {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Le croquis utilise &#039;&#039;&#039;1844&#039;&#039;&#039; octets et le résultat est:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Temps d’exécution: 62&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
On peut constater que le code utilisant les registre est non seulement plus léger (&#039;&#039;&#039;13,67%&#039;&#039;&#039;) mais également plus rapide (&#039;&#039;&#039;11.42&#039;&#039;&#039; fois) !&lt;/div&gt;</summary>
		<author><name>Magsss</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=Atmega328_registers&amp;diff=3295</id>
		<title>Atmega328 registers</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=Atmega328_registers&amp;diff=3295"/>
		<updated>2018-11-01T18:38:24Z</updated>

		<summary type="html">&lt;p&gt;Magsss : /* Comment manipuler un registre ? */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Lorsque l&#039;on commence à écrire des programmes qui sortent de l&#039;ordinaire ou que l&#039;on veut pousser un microcontrôleur à la limite de ses capacités, il est obligatoire de descendre au niveau des registres.&lt;br /&gt;
Seulement voilà, la manipulation de registres ne s&#039;improvise pas, nécessite de connaître le hardware que l&#039;on utilise et, de par sa nature, est &#039;&#039;&#039;spécifique&#039;&#039;&#039; à un type de microcontrôleur !&lt;br /&gt;
&lt;br /&gt;
Manipuler les registres implique donc de sacrifier la portabilité du code, offerte par l&#039;utilisation des fonctions &#039;&#039;haut niveau&#039;&#039; comme &#039;&#039;digitalWrite()&#039;&#039;, au profit de la vitesse d&#039;exécution et de la compacité du code.&lt;br /&gt;
&lt;br /&gt;
Les fonctions comme &#039;&#039;digitalWrite()&#039;&#039;, &#039;&#039;digitalRead()&#039;&#039;, &#039;&#039;pinMode()&#039;&#039; permettent au développeur d&#039;accomplir une action sans avoir à se soucier du type de microcontrôleur utilisé mais, in fine, elles vont elles-mêmes manipuler les registres pour accomplir ces tâches !&lt;br /&gt;
&lt;br /&gt;
= Le cas de l&#039;ATmega328 =&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
L&#039;AtMega328 est le microcontrôleur utilisé sur les Arduino Uno, pro, nano et bon nombre de circuits électroniques d&#039;autres marques ! Ci à droite, l&#039;association entre le nom des broches et leurs positions dans le registre.&lt;br /&gt;
&lt;br /&gt;
On peut remarquer que :&lt;br /&gt;
* les broches D0, D1, D2, D3, D4, D5 et D6 font partie du groupe &#039;&#039;&#039;D&#039;&#039;&#039; (P&#039;&#039;&#039;D&#039;&#039;&#039;);&lt;br /&gt;
* les broches D8, D9, D10, D11, D12 et D13 font partie du groupe &#039;&#039;&#039;B&#039;&#039;&#039; (P&#039;&#039;&#039;B&#039;&#039;&#039;);&lt;br /&gt;
* les broches analogiques font partie du groupe &#039;&#039;&#039;C&#039;&#039;&#039; (P&#039;&#039;&#039;C&#039;&#039;&#039;).&lt;br /&gt;
A chacun de ces groupes on peut associer trois registres :&lt;br /&gt;
* PORT &amp;amp;rarr; pour positionner l&#039;état du port (HIGH ou LOW);&lt;br /&gt;
* PIN &amp;amp;rarr; pour lire l&#039;état d&#039;un port;&lt;br /&gt;
* DDR (&#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;D&#039;&#039;&#039;irection &#039;&#039;&#039;R&#039;&#039;&#039;egister) &amp;amp;rarr; pour positionner le sens d&#039;un port (INPUT, OUTPUT);&lt;br /&gt;
On se retrouve donc avec 9 registres manipulables:&lt;br /&gt;
* PORTD, PIND et DDRD pour les broches D0, D1, D2, D3, D4, D5 et D6;&lt;br /&gt;
* PORTB, PINB et DDRB pour les broches D8, D9, D10, D11, D12 et D13;&lt;br /&gt;
* PORTC, PINC et DDRC pour les broches analogiques.&lt;br /&gt;
La position du bit dans le registre est déterminé par le numéro attenant à la lettre du groupe. Par exemple, si on prend le cas de la broche D13, c&#039;est le 5ème bit du groupe B (PB&#039;&#039;&#039;5&#039;&#039;&#039;) &lt;br /&gt;
[[Fichier:Mapping D13 atmega328.png]]&lt;br /&gt;
.&lt;br /&gt;
||&lt;br /&gt;
[[Fichier:Atmega328 pin mapping.png|centré]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Comment manipuler un registre ? =&lt;br /&gt;
Maintenant que l&#039;on a identifié le groupe et la position du bit des broches dans les registres, nous pouvons passer à la manipulation !&lt;br /&gt;
&lt;br /&gt;
Reprenons l&#039;exemple de la broche D13. Pour modifier son état, il suffit de modifier la valeur de PORTB:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB = B00100000; // pour mettre D13 à HIGH&lt;br /&gt;
PORTB = B00000000; // pour mettre D13 à LOW&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Seulement voilà, on sait que sa position dans les registres correspond au bit numéro 6 mais on a absolument aucune idée de la valeur de PORTB au moment de la modification !&lt;br /&gt;
La manipulation précédente va certainement modifier l&#039;état de D13 mais va aussi modifier l&#039;état des autre broches du groupe &#039;&#039;&#039;B&#039;&#039;&#039;. On ne peut donc pas agir de la sorte, il faut utiliser un opérateur logique pour modifier les registres et on va distinguer deux cas de figures: le passage à &#039;&#039;1&#039;&#039; et celui à &#039;&#039;0&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Passage à &#039;&#039;1&#039;&#039; ==&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;40%&amp;quot;|&lt;br /&gt;
Pour modifier l&#039;état d&#039;un registre à &#039;&#039;1&#039;&#039; sans modifier l&#039;état des autres registres nous allons utiliser l&#039;opérateur &#039;&#039;OU&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Si on reprend l&#039;exemple précédent, nous allons plutôt faire cela:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB |= B00100000; // pour mettre seulement D13 à HIGH&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Imaginons que PORTB est la valeur suivante &#039;&#039;B00010101&#039;&#039;, PORTB |= B00100000 revient à faire:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||0||1||0||1||0||1&lt;br /&gt;
|-&lt;br /&gt;
||ou||0||0||1||0||0||0||0||0&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;9&amp;quot;|&amp;lt;hr&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||1||1||0||1||0||1&lt;br /&gt;
|}&lt;br /&gt;
On a donc réussi à modifier la valeur de D13 sans modifier l&#039;état des autres broches !&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|width=&amp;quot;12%&amp;quot; valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
Table de vérité de OU&lt;br /&gt;
{|border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
A&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
B&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
A OU B&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|}&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;40%&amp;quot;|&lt;br /&gt;
On peut également effectuer cette manipulation par décalage de bit. Dans l&#039;exemple précédent nous avons utilisé &#039;&#039;B00100000&#039;&#039; pour faire le OU mais on aurait pu utiliser l&#039;écriture &#039;&#039;(1&amp;lt;&amp;lt;5)&#039;&#039; ou même la constante prévue à cet effet &#039;&#039;(1&amp;lt;&amp;lt;PB5)&#039;&#039;.&lt;br /&gt;
Le chiffre &#039;&#039;1&#039;&#039; est un entier qui s&#039;écrit en binaire comme cela:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||position||7||6||5||4||3||2||1||0&lt;br /&gt;
|-&lt;br /&gt;
||valeur||0||0||0||0||0||0||0||1&lt;br /&gt;
|}&lt;br /&gt;
Il nous suffit donc de le décaler de 5 position vers la gauche pour obtenir B00100000:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||position||7||6||5||4||3||2||1||0&lt;br /&gt;
|-&lt;br /&gt;
||valeur||0||0||1||0||0||0||0||0&lt;br /&gt;
|}&lt;br /&gt;
Les trois écritures suivantes sont donc équivalentes:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB |= B00100000;&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;5);&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Passage à &#039;&#039;0&#039;&#039; ==&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;40%&amp;quot;|&lt;br /&gt;
Pour modifier l&#039;état d&#039;un registre à &#039;&#039;1&#039;&#039; sans modifier l&#039;état des autres registres nous allons utiliser l&#039;opérateur &#039;&#039;ET&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Si on reprend l&#039;exemple précédent, nous allons plutôt faire cela:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB &amp;amp;= ~B00100000; // pour mettre seulement D13 à LOW&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
L&#039;opérateur &#039;&#039;~&#039;&#039; permet d&#039;obtenir le [https://fr.wikipedia.org/wiki/Compl%C3%A9ment_%C3%A0_un complément A1] d&#039;un nombre binaire: B00100000 devient alors B11011111.&lt;br /&gt;
&lt;br /&gt;
Imaginons que PORTB est la valeur suivante &#039;&#039;B00010101&#039;&#039;, PORTB &amp;amp;= ~B00100000 revient à faire:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||0||1||0||1||0||1&lt;br /&gt;
|-&lt;br /&gt;
||ET||1||1||0||1||1||1||1||1&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;9&amp;quot;|&amp;lt;hr&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||0||1||0||1||0||1&lt;br /&gt;
|}&lt;br /&gt;
On a donc réussi à modifier la valeur de D13 sans modifier l&#039;état des autres broches !&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|width=&amp;quot;12%&amp;quot; valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
Table de vérité de ET&lt;br /&gt;
{|border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
A&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
B&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
A ET B&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|}&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;40%&amp;quot;|&lt;br /&gt;
On peut également effectuer cette manipulation par décalage de bit. Dans l&#039;exemple précédent nous avons utilisé &#039;&#039;~B00100000&#039;&#039; pour faire le OU mais on aurait pu utiliser l&#039;écriture &#039;&#039;~(1&amp;lt;&amp;lt;5)&#039;&#039; ou même la constante prévue à cet effet &#039;&#039;~(1&amp;lt;&amp;lt;PB5)&#039;&#039;..&lt;br /&gt;
Les trois écritures suivantes sont donc équivalentes:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB &amp;amp;= ~B00100000;&lt;br /&gt;
PORTB &amp;amp;= ~(1&amp;lt;&amp;lt;5);&lt;br /&gt;
PORTB &amp;amp;= ~(1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Modification de l&#039;état d&#039;une broche =&lt;br /&gt;
Dans l&#039;exemple suivant nous allons modifier l&#039;état de la broche D13 pour la faire clignoter.&lt;br /&gt;
==Modification de sens ==&lt;br /&gt;
Dans un premier temps il faut la placer en sortie et pour cela nous allons manipuler le registre &#039;&#039;DDRB&#039;&#039;:&lt;br /&gt;
* la valeur &#039;&#039;1&#039;&#039; positionne la broche en sortie (&#039;&#039;OUTPUT&#039;&#039;);&lt;br /&gt;
* la valeur &#039;&#039;0&#039;&#039; positionne la broche en entrée (&#039;&#039;INPUT&#039;&#039;), si la l&#039;état est positionné à &#039;&#039;1&#039;&#039; cela active la résistance de &#039;&#039;pullup&#039;&#039; (&#039;&#039;INPUT_PULLUP&#039;&#039;).&lt;br /&gt;
Nous allons donc modifier le registre DDRB comme suit, les trois notations sont équivalentes:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
DDRB |= B00100000; // Positionnement de D13 en OUTPUT&lt;br /&gt;
DDRB |= (1&amp;lt;&amp;lt;5);&lt;br /&gt;
DDRB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Modification d&#039;état ==&lt;br /&gt;
On peut maintenant modifier son état grâce au registre PORTB:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB |= B00100000; // Positionnement de D13 à HIGH&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;5);&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Lecture d&#039;état==&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Il est possible de lire l&#039;état de la broche D13 grâce au registre &#039;&#039;PINB&#039;&#039;. Nous allons utiliser la même technique du masquage que précédemment avec l&#039;opérateur &#039;&#039;ET&#039;&#039;.&lt;br /&gt;
Imaginons que PINB est la valeur suivante &#039;&#039;B00110101&#039;&#039;, PINB &amp;amp;= B00100000 revient à faire:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||1||1||0||1||0||1&lt;br /&gt;
|-&lt;br /&gt;
||ET||0||0||1||0||0||0||0||0&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;9&amp;quot;|&amp;lt;hr&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||1||0||0||0||0||0&lt;br /&gt;
|}&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
On va donc retirer l&#039;état des autres broches sans modifier pour autant celui de la broche D13 !&lt;br /&gt;
Il ne nous reste plus qu&#039;à décaler ce bit de 5 positions vers la droite pour pouvoir le lire. &lt;br /&gt;
&lt;br /&gt;
Les deux écritures suivantes sont équivalentes:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t state;&lt;br /&gt;
state = (PINB &amp;amp; B00100000) &amp;gt;&amp;gt; 5;&lt;br /&gt;
state = (PINB &amp;amp; B00100000) &amp;gt;&amp;gt; PB5;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Rien n’empêche d&#039;utiliser cela pour faire des tests:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Modification de D13 en sortie&lt;br /&gt;
DDRB |= B00100000;&lt;br /&gt;
// Passage de D13 à HIGH&lt;br /&gt;
PORTB |= (1 &amp;lt;&amp;lt; PB5);&lt;br /&gt;
if ((PINB &amp;amp; B00100000) &amp;gt;&amp;gt; PB5) {&lt;br /&gt;
    Serial.println(&amp;quot;D13 is ON&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
// Passage de D13 à LOW&lt;br /&gt;
PORTB &amp;amp;= ~(1 &amp;lt;&amp;lt; PB5);&lt;br /&gt;
if ((PINB &amp;amp; B00100000) &amp;gt;&amp;gt; PB5 == 0) {&lt;br /&gt;
    Serial.println(&amp;quot;D13 is OFF&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Lorsque l&#039;on exécute le code suivant on s&#039;aperçoit que rien ne s&#039;affiche. La raison est simple, la modification de registre est tellement rapide que l&#039;état électrique n&#039;a pas le temps d&#039;être modifier avant la lecture !&lt;br /&gt;
&lt;br /&gt;
Il suffit d&#039;ajouter:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
delay(1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Avant les lignes modifiant les états:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
PORTB |= (1 &amp;lt;&amp;lt; PB5);&lt;br /&gt;
...&lt;br /&gt;
PORTB &amp;amp;= ~(1 &amp;lt;&amp;lt; PB5);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Performances =&lt;br /&gt;
La &#039;&#039;&#039;véritable&#039;&#039;&#039; interrogation réside dans le faite de savoir si le jeu en vaut la chandelle ? Est-il intéressant de sacrifier la simplicité d&#039;écriture et la compréhension du code pour la performance ?&lt;br /&gt;
&lt;br /&gt;
Pour faire un test probant, allumons la led présente sur D13 100.000 fois avec:&lt;br /&gt;
* dans un premier temps &#039;&#039;pinMode()&#039;&#039; et &#039;&#039;digitalWrite()&#039;&#039;;&lt;br /&gt;
* dans un second temps &#039;&#039;DDRB&#039;&#039; et &#039;&#039;PORTB&#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
{|width=&amp;quot;100%&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Voila le premier test:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define LIMIT 100000&lt;br /&gt;
void setup() {&lt;br /&gt;
  Serial.begin(9600);&lt;br /&gt;
  int32_t timer = millis();&lt;br /&gt;
  pinMode(13, OUTPUT);&lt;br /&gt;
  for (uint32_t i = 0; i &amp;lt; LIMIT; i++) {&lt;br /&gt;
    digitalWrite(13, HIGH);&lt;br /&gt;
    digitalWrite(13, LOW);&lt;br /&gt;
  }&lt;br /&gt;
  int32_t elapsed = millis()  - timer;&lt;br /&gt;
  Serial.print(&amp;quot;Temps d&#039;exécution: &amp;quot;);&lt;br /&gt;
  Serial.println(elapsed);&lt;br /&gt;
}&lt;br /&gt;
void loop() {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Le croquis utilise &#039;&#039;&#039;2138&#039;&#039;&#039; octets et le résultat est:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Temps d’exécution: 709&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Voila le deuxième test:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define LIMIT 100000&lt;br /&gt;
void setup() {&lt;br /&gt;
  Serial.begin(9600);&lt;br /&gt;
  int32_t timer = millis();&lt;br /&gt;
  DDRB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
  for(uint32_t i = 0; i &amp;lt; LIMIT; i++){&lt;br /&gt;
    PORTB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
    PORTB &amp;amp;= ~(1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
  }&lt;br /&gt;
  int32_t elapsed = millis()  - timer;&lt;br /&gt;
  Serial.print(&amp;quot;Temps d&#039;exécution: &amp;quot;);&lt;br /&gt;
  Serial.println(elapsed);&lt;br /&gt;
}&lt;br /&gt;
void loop() {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Le croquis utilise &#039;&#039;&#039;1844&#039;&#039;&#039; octets et le résultat est:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Temps d’exécution: 62&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
On peut constater que le code utilisant les registre est non seulement plus léger (&#039;&#039;&#039;13,67%&#039;&#039;&#039;) mais également plus rapide (&#039;&#039;&#039;11.42&#039;&#039;&#039; fois) !&lt;/div&gt;</summary>
		<author><name>Magsss</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=Atmega328_registers&amp;diff=3294</id>
		<title>Atmega328 registers</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=Atmega328_registers&amp;diff=3294"/>
		<updated>2018-11-01T18:38:00Z</updated>

		<summary type="html">&lt;p&gt;Magsss : /* Comment manipuler un registre ? */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Lorsque l&#039;on commence à écrire des programmes qui sortent de l&#039;ordinaire ou que l&#039;on veut pousser un microcontrôleur à la limite de ses capacités, il est obligatoire de descendre au niveau des registres.&lt;br /&gt;
Seulement voilà, la manipulation de registres ne s&#039;improvise pas, nécessite de connaître le hardware que l&#039;on utilise et, de par sa nature, est &#039;&#039;&#039;spécifique&#039;&#039;&#039; à un type de microcontrôleur !&lt;br /&gt;
&lt;br /&gt;
Manipuler les registres implique donc de sacrifier la portabilité du code, offerte par l&#039;utilisation des fonctions &#039;&#039;haut niveau&#039;&#039; comme &#039;&#039;digitalWrite()&#039;&#039;, au profit de la vitesse d&#039;exécution et de la compacité du code.&lt;br /&gt;
&lt;br /&gt;
Les fonctions comme &#039;&#039;digitalWrite()&#039;&#039;, &#039;&#039;digitalRead()&#039;&#039;, &#039;&#039;pinMode()&#039;&#039; permettent au développeur d&#039;accomplir une action sans avoir à se soucier du type de microcontrôleur utilisé mais, in fine, elles vont elles-mêmes manipuler les registres pour accomplir ces tâches !&lt;br /&gt;
&lt;br /&gt;
= Le cas de l&#039;ATmega328 =&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
L&#039;AtMega328 est le microcontrôleur utilisé sur les Arduino Uno, pro, nano et bon nombre de circuits électroniques d&#039;autres marques ! Ci à droite, l&#039;association entre le nom des broches et leurs positions dans le registre.&lt;br /&gt;
&lt;br /&gt;
On peut remarquer que :&lt;br /&gt;
* les broches D0, D1, D2, D3, D4, D5 et D6 font partie du groupe &#039;&#039;&#039;D&#039;&#039;&#039; (P&#039;&#039;&#039;D&#039;&#039;&#039;);&lt;br /&gt;
* les broches D8, D9, D10, D11, D12 et D13 font partie du groupe &#039;&#039;&#039;B&#039;&#039;&#039; (P&#039;&#039;&#039;B&#039;&#039;&#039;);&lt;br /&gt;
* les broches analogiques font partie du groupe &#039;&#039;&#039;C&#039;&#039;&#039; (P&#039;&#039;&#039;C&#039;&#039;&#039;).&lt;br /&gt;
A chacun de ces groupes on peut associer trois registres :&lt;br /&gt;
* PORT &amp;amp;rarr; pour positionner l&#039;état du port (HIGH ou LOW);&lt;br /&gt;
* PIN &amp;amp;rarr; pour lire l&#039;état d&#039;un port;&lt;br /&gt;
* DDR (&#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;D&#039;&#039;&#039;irection &#039;&#039;&#039;R&#039;&#039;&#039;egister) &amp;amp;rarr; pour positionner le sens d&#039;un port (INPUT, OUTPUT);&lt;br /&gt;
On se retrouve donc avec 9 registres manipulables:&lt;br /&gt;
* PORTD, PIND et DDRD pour les broches D0, D1, D2, D3, D4, D5 et D6;&lt;br /&gt;
* PORTB, PINB et DDRB pour les broches D8, D9, D10, D11, D12 et D13;&lt;br /&gt;
* PORTC, PINC et DDRC pour les broches analogiques.&lt;br /&gt;
La position du bit dans le registre est déterminé par le numéro attenant à la lettre du groupe. Par exemple, si on prend le cas de la broche D13, c&#039;est le 5ème bit du groupe B (PB&#039;&#039;&#039;5&#039;&#039;&#039;) &lt;br /&gt;
[[Fichier:Mapping D13 atmega328.png]]&lt;br /&gt;
.&lt;br /&gt;
||&lt;br /&gt;
[[Fichier:Atmega328 pin mapping.png|centré]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Comment manipuler un registre ? =&lt;br /&gt;
Maintenant que l&#039;on a identifié le groupe et la position du bit des broches dans les registres, nous pouvons passer à la manipulation !&lt;br /&gt;
&lt;br /&gt;
Reprenons l&#039;exemple de la broche D13. Pour modifier son état, il suffit de modifier la valeur de PORTB:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB = B00100000; // pour mettre D13 à HIGH&lt;br /&gt;
PORTB = B00000000; // pour mettre D13 à LOW&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Seulement voila, on sait que sa position dans les registres correspond au bit numéro 6 mais on a absolument aucune idée de la valeur de PORTB au moment de la modification !&lt;br /&gt;
La manipulation précédente va certainement modifier l&#039;état de D13 mais va aussi modifier l&#039;état des autre broches du groupe &#039;&#039;&#039;B&#039;&#039;&#039;. On ne peut donc pas agir de la sorte, il faut utiliser un opérateur logique pour modifier les registres et on va distinguer deux cas de figures: le passage à &#039;&#039;1&#039;&#039; et celui à &#039;&#039;0&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Passage à &#039;&#039;1&#039;&#039; ==&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;40%&amp;quot;|&lt;br /&gt;
Pour modifier l&#039;état d&#039;un registre à &#039;&#039;1&#039;&#039; sans modifier l&#039;état des autres registres nous allons utiliser l&#039;opérateur &#039;&#039;OU&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Si on reprend l&#039;exemple précédent, nous allons plutôt faire cela:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB |= B00100000; // pour mettre seulement D13 à HIGH&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Imaginons que PORTB est la valeur suivante &#039;&#039;B00010101&#039;&#039;, PORTB |= B00100000 revient à faire:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||0||1||0||1||0||1&lt;br /&gt;
|-&lt;br /&gt;
||ou||0||0||1||0||0||0||0||0&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;9&amp;quot;|&amp;lt;hr&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||1||1||0||1||0||1&lt;br /&gt;
|}&lt;br /&gt;
On a donc réussi à modifier la valeur de D13 sans modifier l&#039;état des autres broches !&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|width=&amp;quot;12%&amp;quot; valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
Table de vérité de OU&lt;br /&gt;
{|border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
A&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
B&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
A OU B&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|}&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;40%&amp;quot;|&lt;br /&gt;
On peut également effectuer cette manipulation par décalage de bit. Dans l&#039;exemple précédent nous avons utilisé &#039;&#039;B00100000&#039;&#039; pour faire le OU mais on aurait pu utiliser l&#039;écriture &#039;&#039;(1&amp;lt;&amp;lt;5)&#039;&#039; ou même la constante prévue à cet effet &#039;&#039;(1&amp;lt;&amp;lt;PB5)&#039;&#039;.&lt;br /&gt;
Le chiffre &#039;&#039;1&#039;&#039; est un entier qui s&#039;écrit en binaire comme cela:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||position||7||6||5||4||3||2||1||0&lt;br /&gt;
|-&lt;br /&gt;
||valeur||0||0||0||0||0||0||0||1&lt;br /&gt;
|}&lt;br /&gt;
Il nous suffit donc de le décaler de 5 position vers la gauche pour obtenir B00100000:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||position||7||6||5||4||3||2||1||0&lt;br /&gt;
|-&lt;br /&gt;
||valeur||0||0||1||0||0||0||0||0&lt;br /&gt;
|}&lt;br /&gt;
Les trois écritures suivantes sont donc équivalentes:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB |= B00100000;&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;5);&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Passage à &#039;&#039;0&#039;&#039; ==&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;40%&amp;quot;|&lt;br /&gt;
Pour modifier l&#039;état d&#039;un registre à &#039;&#039;1&#039;&#039; sans modifier l&#039;état des autres registres nous allons utiliser l&#039;opérateur &#039;&#039;ET&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Si on reprend l&#039;exemple précédent, nous allons plutôt faire cela:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB &amp;amp;= ~B00100000; // pour mettre seulement D13 à LOW&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
L&#039;opérateur &#039;&#039;~&#039;&#039; permet d&#039;obtenir le [https://fr.wikipedia.org/wiki/Compl%C3%A9ment_%C3%A0_un complément A1] d&#039;un nombre binaire: B00100000 devient alors B11011111.&lt;br /&gt;
&lt;br /&gt;
Imaginons que PORTB est la valeur suivante &#039;&#039;B00010101&#039;&#039;, PORTB &amp;amp;= ~B00100000 revient à faire:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||0||1||0||1||0||1&lt;br /&gt;
|-&lt;br /&gt;
||ET||1||1||0||1||1||1||1||1&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;9&amp;quot;|&amp;lt;hr&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||0||1||0||1||0||1&lt;br /&gt;
|}&lt;br /&gt;
On a donc réussi à modifier la valeur de D13 sans modifier l&#039;état des autres broches !&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|width=&amp;quot;12%&amp;quot; valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
Table de vérité de ET&lt;br /&gt;
{|border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
A&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
B&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
A ET B&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|}&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;40%&amp;quot;|&lt;br /&gt;
On peut également effectuer cette manipulation par décalage de bit. Dans l&#039;exemple précédent nous avons utilisé &#039;&#039;~B00100000&#039;&#039; pour faire le OU mais on aurait pu utiliser l&#039;écriture &#039;&#039;~(1&amp;lt;&amp;lt;5)&#039;&#039; ou même la constante prévue à cet effet &#039;&#039;~(1&amp;lt;&amp;lt;PB5)&#039;&#039;..&lt;br /&gt;
Les trois écritures suivantes sont donc équivalentes:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB &amp;amp;= ~B00100000;&lt;br /&gt;
PORTB &amp;amp;= ~(1&amp;lt;&amp;lt;5);&lt;br /&gt;
PORTB &amp;amp;= ~(1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Modification de l&#039;état d&#039;une broche =&lt;br /&gt;
Dans l&#039;exemple suivant nous allons modifier l&#039;état de la broche D13 pour la faire clignoter.&lt;br /&gt;
==Modification de sens ==&lt;br /&gt;
Dans un premier temps il faut la placer en sortie et pour cela nous allons manipuler le registre &#039;&#039;DDRB&#039;&#039;:&lt;br /&gt;
* la valeur &#039;&#039;1&#039;&#039; positionne la broche en sortie (&#039;&#039;OUTPUT&#039;&#039;);&lt;br /&gt;
* la valeur &#039;&#039;0&#039;&#039; positionne la broche en entrée (&#039;&#039;INPUT&#039;&#039;), si la l&#039;état est positionné à &#039;&#039;1&#039;&#039; cela active la résistance de &#039;&#039;pullup&#039;&#039; (&#039;&#039;INPUT_PULLUP&#039;&#039;).&lt;br /&gt;
Nous allons donc modifier le registre DDRB comme suit, les trois notations sont équivalentes:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
DDRB |= B00100000; // Positionnement de D13 en OUTPUT&lt;br /&gt;
DDRB |= (1&amp;lt;&amp;lt;5);&lt;br /&gt;
DDRB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Modification d&#039;état ==&lt;br /&gt;
On peut maintenant modifier son état grâce au registre PORTB:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB |= B00100000; // Positionnement de D13 à HIGH&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;5);&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Lecture d&#039;état==&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Il est possible de lire l&#039;état de la broche D13 grâce au registre &#039;&#039;PINB&#039;&#039;. Nous allons utiliser la même technique du masquage que précédemment avec l&#039;opérateur &#039;&#039;ET&#039;&#039;.&lt;br /&gt;
Imaginons que PINB est la valeur suivante &#039;&#039;B00110101&#039;&#039;, PINB &amp;amp;= B00100000 revient à faire:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||1||1||0||1||0||1&lt;br /&gt;
|-&lt;br /&gt;
||ET||0||0||1||0||0||0||0||0&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;9&amp;quot;|&amp;lt;hr&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||1||0||0||0||0||0&lt;br /&gt;
|}&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
On va donc retirer l&#039;état des autres broches sans modifier pour autant celui de la broche D13 !&lt;br /&gt;
Il ne nous reste plus qu&#039;à décaler ce bit de 5 positions vers la droite pour pouvoir le lire. &lt;br /&gt;
&lt;br /&gt;
Les deux écritures suivantes sont équivalentes:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t state;&lt;br /&gt;
state = (PINB &amp;amp; B00100000) &amp;gt;&amp;gt; 5;&lt;br /&gt;
state = (PINB &amp;amp; B00100000) &amp;gt;&amp;gt; PB5;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Rien n’empêche d&#039;utiliser cela pour faire des tests:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Modification de D13 en sortie&lt;br /&gt;
DDRB |= B00100000;&lt;br /&gt;
// Passage de D13 à HIGH&lt;br /&gt;
PORTB |= (1 &amp;lt;&amp;lt; PB5);&lt;br /&gt;
if ((PINB &amp;amp; B00100000) &amp;gt;&amp;gt; PB5) {&lt;br /&gt;
    Serial.println(&amp;quot;D13 is ON&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
// Passage de D13 à LOW&lt;br /&gt;
PORTB &amp;amp;= ~(1 &amp;lt;&amp;lt; PB5);&lt;br /&gt;
if ((PINB &amp;amp; B00100000) &amp;gt;&amp;gt; PB5 == 0) {&lt;br /&gt;
    Serial.println(&amp;quot;D13 is OFF&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Lorsque l&#039;on exécute le code suivant on s&#039;aperçoit que rien ne s&#039;affiche. La raison est simple, la modification de registre est tellement rapide que l&#039;état électrique n&#039;a pas le temps d&#039;être modifier avant la lecture !&lt;br /&gt;
&lt;br /&gt;
Il suffit d&#039;ajouter:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
delay(1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Avant les lignes modifiant les états:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
PORTB |= (1 &amp;lt;&amp;lt; PB5);&lt;br /&gt;
...&lt;br /&gt;
PORTB &amp;amp;= ~(1 &amp;lt;&amp;lt; PB5);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Performances =&lt;br /&gt;
La &#039;&#039;&#039;véritable&#039;&#039;&#039; interrogation réside dans le faite de savoir si le jeu en vaut la chandelle ? Est-il intéressant de sacrifier la simplicité d&#039;écriture et la compréhension du code pour la performance ?&lt;br /&gt;
&lt;br /&gt;
Pour faire un test probant, allumons la led présente sur D13 100.000 fois avec:&lt;br /&gt;
* dans un premier temps &#039;&#039;pinMode()&#039;&#039; et &#039;&#039;digitalWrite()&#039;&#039;;&lt;br /&gt;
* dans un second temps &#039;&#039;DDRB&#039;&#039; et &#039;&#039;PORTB&#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
{|width=&amp;quot;100%&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Voila le premier test:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define LIMIT 100000&lt;br /&gt;
void setup() {&lt;br /&gt;
  Serial.begin(9600);&lt;br /&gt;
  int32_t timer = millis();&lt;br /&gt;
  pinMode(13, OUTPUT);&lt;br /&gt;
  for (uint32_t i = 0; i &amp;lt; LIMIT; i++) {&lt;br /&gt;
    digitalWrite(13, HIGH);&lt;br /&gt;
    digitalWrite(13, LOW);&lt;br /&gt;
  }&lt;br /&gt;
  int32_t elapsed = millis()  - timer;&lt;br /&gt;
  Serial.print(&amp;quot;Temps d&#039;exécution: &amp;quot;);&lt;br /&gt;
  Serial.println(elapsed);&lt;br /&gt;
}&lt;br /&gt;
void loop() {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Le croquis utilise &#039;&#039;&#039;2138&#039;&#039;&#039; octets et le résultat est:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Temps d’exécution: 709&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Voila le deuxième test:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define LIMIT 100000&lt;br /&gt;
void setup() {&lt;br /&gt;
  Serial.begin(9600);&lt;br /&gt;
  int32_t timer = millis();&lt;br /&gt;
  DDRB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
  for(uint32_t i = 0; i &amp;lt; LIMIT; i++){&lt;br /&gt;
    PORTB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
    PORTB &amp;amp;= ~(1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
  }&lt;br /&gt;
  int32_t elapsed = millis()  - timer;&lt;br /&gt;
  Serial.print(&amp;quot;Temps d&#039;exécution: &amp;quot;);&lt;br /&gt;
  Serial.println(elapsed);&lt;br /&gt;
}&lt;br /&gt;
void loop() {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Le croquis utilise &#039;&#039;&#039;1844&#039;&#039;&#039; octets et le résultat est:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Temps d’exécution: 62&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
On peut constater que le code utilisant les registre est non seulement plus léger (&#039;&#039;&#039;13,67%&#039;&#039;&#039;) mais également plus rapide (&#039;&#039;&#039;11.42&#039;&#039;&#039; fois) !&lt;/div&gt;</summary>
		<author><name>Magsss</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=Atmega328_registers&amp;diff=3293</id>
		<title>Atmega328 registers</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=Atmega328_registers&amp;diff=3293"/>
		<updated>2018-11-01T18:37:21Z</updated>

		<summary type="html">&lt;p&gt;Magsss : /* Comment manipuler un registre ? */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Lorsque l&#039;on commence à écrire des programmes qui sortent de l&#039;ordinaire ou que l&#039;on veut pousser un microcontrôleur à la limite de ses capacités, il est obligatoire de descendre au niveau des registres.&lt;br /&gt;
Seulement voilà, la manipulation de registres ne s&#039;improvise pas, nécessite de connaître le hardware que l&#039;on utilise et, de par sa nature, est &#039;&#039;&#039;spécifique&#039;&#039;&#039; à un type de microcontrôleur !&lt;br /&gt;
&lt;br /&gt;
Manipuler les registres implique donc de sacrifier la portabilité du code, offerte par l&#039;utilisation des fonctions &#039;&#039;haut niveau&#039;&#039; comme &#039;&#039;digitalWrite()&#039;&#039;, au profit de la vitesse d&#039;exécution et de la compacité du code.&lt;br /&gt;
&lt;br /&gt;
Les fonctions comme &#039;&#039;digitalWrite()&#039;&#039;, &#039;&#039;digitalRead()&#039;&#039;, &#039;&#039;pinMode()&#039;&#039; permettent au développeur d&#039;accomplir une action sans avoir à se soucier du type de microcontrôleur utilisé mais, in fine, elles vont elles-mêmes manipuler les registres pour accomplir ces tâches !&lt;br /&gt;
&lt;br /&gt;
= Le cas de l&#039;ATmega328 =&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
L&#039;AtMega328 est le microcontrôleur utilisé sur les Arduino Uno, pro, nano et bon nombre de circuits électroniques d&#039;autres marques ! Ci à droite, l&#039;association entre le nom des broches et leurs positions dans le registre.&lt;br /&gt;
&lt;br /&gt;
On peut remarquer que :&lt;br /&gt;
* les broches D0, D1, D2, D3, D4, D5 et D6 font partie du groupe &#039;&#039;&#039;D&#039;&#039;&#039; (P&#039;&#039;&#039;D&#039;&#039;&#039;);&lt;br /&gt;
* les broches D8, D9, D10, D11, D12 et D13 font partie du groupe &#039;&#039;&#039;B&#039;&#039;&#039; (P&#039;&#039;&#039;B&#039;&#039;&#039;);&lt;br /&gt;
* les broches analogiques font partie du groupe &#039;&#039;&#039;C&#039;&#039;&#039; (P&#039;&#039;&#039;C&#039;&#039;&#039;).&lt;br /&gt;
A chacun de ces groupes on peut associer trois registres :&lt;br /&gt;
* PORT &amp;amp;rarr; pour positionner l&#039;état du port (HIGH ou LOW);&lt;br /&gt;
* PIN &amp;amp;rarr; pour lire l&#039;état d&#039;un port;&lt;br /&gt;
* DDR (&#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;D&#039;&#039;&#039;irection &#039;&#039;&#039;R&#039;&#039;&#039;egister) &amp;amp;rarr; pour positionner le sens d&#039;un port (INPUT, OUTPUT);&lt;br /&gt;
On se retrouve donc avec 9 registres manipulables:&lt;br /&gt;
* PORTD, PIND et DDRD pour les broches D0, D1, D2, D3, D4, D5 et D6;&lt;br /&gt;
* PORTB, PINB et DDRB pour les broches D8, D9, D10, D11, D12 et D13;&lt;br /&gt;
* PORTC, PINC et DDRC pour les broches analogiques.&lt;br /&gt;
La position du bit dans le registre est déterminé par le numéro attenant à la lettre du groupe. Par exemple, si on prend le cas de la broche D13, c&#039;est le 5ème bit du groupe B (PB&#039;&#039;&#039;5&#039;&#039;&#039;) &lt;br /&gt;
[[Fichier:Mapping D13 atmega328.png]]&lt;br /&gt;
.&lt;br /&gt;
||&lt;br /&gt;
[[Fichier:Atmega328 pin mapping.png|centré]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Comment manipuler un registre ? =&lt;br /&gt;
Maintenant que l&#039;on a identifié le groupe et la position du bit des broches dans les registres, nous pouvons passer à la manipulation !&lt;br /&gt;
&lt;br /&gt;
Reprenons l&#039;exemple de la broche D13. Pour modifier sont état, il suffit de modifier la valeur de PORTB:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB = B00100000; // pour mettre D13 à HIGH&lt;br /&gt;
PORTB = B00000000; // pour mettre D13 à LOW&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Seulement voila, on sait que sa position dans les registres correspond au bit numéro 6 mais on a absolument aucune idée de la valeur de PORTB au moment de la modification !&lt;br /&gt;
La manipulation précédente va certainement modifier l&#039;état de D13 mais va aussi modifier l&#039;état des autre broches du groupe &#039;&#039;&#039;B&#039;&#039;&#039;. On ne peut donc pas agir de la sorte, il faut utiliser un opérateur logique pour modifier les registres et on va distinguer deux cas de figures: le passage à &#039;&#039;1&#039;&#039; et celui à &#039;&#039;0&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Passage à &#039;&#039;1&#039;&#039; ==&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;40%&amp;quot;|&lt;br /&gt;
Pour modifier l&#039;état d&#039;un registre à &#039;&#039;1&#039;&#039; sans modifier l&#039;état des autres registres nous allons utiliser l&#039;opérateur &#039;&#039;OU&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Si on reprend l&#039;exemple précédent, nous allons plutôt faire cela:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB |= B00100000; // pour mettre seulement D13 à HIGH&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Imaginons que PORTB est la valeur suivante &#039;&#039;B00010101&#039;&#039;, PORTB |= B00100000 revient à faire:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||0||1||0||1||0||1&lt;br /&gt;
|-&lt;br /&gt;
||ou||0||0||1||0||0||0||0||0&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;9&amp;quot;|&amp;lt;hr&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||1||1||0||1||0||1&lt;br /&gt;
|}&lt;br /&gt;
On a donc réussi à modifier la valeur de D13 sans modifier l&#039;état des autres broches !&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|width=&amp;quot;12%&amp;quot; valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
Table de vérité de OU&lt;br /&gt;
{|border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
A&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
B&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
A OU B&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|}&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;40%&amp;quot;|&lt;br /&gt;
On peut également effectuer cette manipulation par décalage de bit. Dans l&#039;exemple précédent nous avons utilisé &#039;&#039;B00100000&#039;&#039; pour faire le OU mais on aurait pu utiliser l&#039;écriture &#039;&#039;(1&amp;lt;&amp;lt;5)&#039;&#039; ou même la constante prévue à cet effet &#039;&#039;(1&amp;lt;&amp;lt;PB5)&#039;&#039;.&lt;br /&gt;
Le chiffre &#039;&#039;1&#039;&#039; est un entier qui s&#039;écrit en binaire comme cela:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||position||7||6||5||4||3||2||1||0&lt;br /&gt;
|-&lt;br /&gt;
||valeur||0||0||0||0||0||0||0||1&lt;br /&gt;
|}&lt;br /&gt;
Il nous suffit donc de le décaler de 5 position vers la gauche pour obtenir B00100000:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||position||7||6||5||4||3||2||1||0&lt;br /&gt;
|-&lt;br /&gt;
||valeur||0||0||1||0||0||0||0||0&lt;br /&gt;
|}&lt;br /&gt;
Les trois écritures suivantes sont donc équivalentes:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB |= B00100000;&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;5);&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Passage à &#039;&#039;0&#039;&#039; ==&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;40%&amp;quot;|&lt;br /&gt;
Pour modifier l&#039;état d&#039;un registre à &#039;&#039;1&#039;&#039; sans modifier l&#039;état des autres registres nous allons utiliser l&#039;opérateur &#039;&#039;ET&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Si on reprend l&#039;exemple précédent, nous allons plutôt faire cela:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB &amp;amp;= ~B00100000; // pour mettre seulement D13 à LOW&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
L&#039;opérateur &#039;&#039;~&#039;&#039; permet d&#039;obtenir le [https://fr.wikipedia.org/wiki/Compl%C3%A9ment_%C3%A0_un complément A1] d&#039;un nombre binaire: B00100000 devient alors B11011111.&lt;br /&gt;
&lt;br /&gt;
Imaginons que PORTB est la valeur suivante &#039;&#039;B00010101&#039;&#039;, PORTB &amp;amp;= ~B00100000 revient à faire:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||0||1||0||1||0||1&lt;br /&gt;
|-&lt;br /&gt;
||ET||1||1||0||1||1||1||1||1&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;9&amp;quot;|&amp;lt;hr&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||0||1||0||1||0||1&lt;br /&gt;
|}&lt;br /&gt;
On a donc réussi à modifier la valeur de D13 sans modifier l&#039;état des autres broches !&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|width=&amp;quot;12%&amp;quot; valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
Table de vérité de ET&lt;br /&gt;
{|border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
A&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
B&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
A ET B&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|}&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;40%&amp;quot;|&lt;br /&gt;
On peut également effectuer cette manipulation par décalage de bit. Dans l&#039;exemple précédent nous avons utilisé &#039;&#039;~B00100000&#039;&#039; pour faire le OU mais on aurait pu utiliser l&#039;écriture &#039;&#039;~(1&amp;lt;&amp;lt;5)&#039;&#039; ou même la constante prévue à cet effet &#039;&#039;~(1&amp;lt;&amp;lt;PB5)&#039;&#039;..&lt;br /&gt;
Les trois écritures suivantes sont donc équivalentes:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB &amp;amp;= ~B00100000;&lt;br /&gt;
PORTB &amp;amp;= ~(1&amp;lt;&amp;lt;5);&lt;br /&gt;
PORTB &amp;amp;= ~(1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Modification de l&#039;état d&#039;une broche =&lt;br /&gt;
Dans l&#039;exemple suivant nous allons modifier l&#039;état de la broche D13 pour la faire clignoter.&lt;br /&gt;
==Modification de sens ==&lt;br /&gt;
Dans un premier temps il faut la placer en sortie et pour cela nous allons manipuler le registre &#039;&#039;DDRB&#039;&#039;:&lt;br /&gt;
* la valeur &#039;&#039;1&#039;&#039; positionne la broche en sortie (&#039;&#039;OUTPUT&#039;&#039;);&lt;br /&gt;
* la valeur &#039;&#039;0&#039;&#039; positionne la broche en entrée (&#039;&#039;INPUT&#039;&#039;), si la l&#039;état est positionné à &#039;&#039;1&#039;&#039; cela active la résistance de &#039;&#039;pullup&#039;&#039; (&#039;&#039;INPUT_PULLUP&#039;&#039;).&lt;br /&gt;
Nous allons donc modifier le registre DDRB comme suit, les trois notations sont équivalentes:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
DDRB |= B00100000; // Positionnement de D13 en OUTPUT&lt;br /&gt;
DDRB |= (1&amp;lt;&amp;lt;5);&lt;br /&gt;
DDRB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Modification d&#039;état ==&lt;br /&gt;
On peut maintenant modifier son état grâce au registre PORTB:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB |= B00100000; // Positionnement de D13 à HIGH&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;5);&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Lecture d&#039;état==&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Il est possible de lire l&#039;état de la broche D13 grâce au registre &#039;&#039;PINB&#039;&#039;. Nous allons utiliser la même technique du masquage que précédemment avec l&#039;opérateur &#039;&#039;ET&#039;&#039;.&lt;br /&gt;
Imaginons que PINB est la valeur suivante &#039;&#039;B00110101&#039;&#039;, PINB &amp;amp;= B00100000 revient à faire:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||1||1||0||1||0||1&lt;br /&gt;
|-&lt;br /&gt;
||ET||0||0||1||0||0||0||0||0&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;9&amp;quot;|&amp;lt;hr&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||1||0||0||0||0||0&lt;br /&gt;
|}&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
On va donc retirer l&#039;état des autres broches sans modifier pour autant celui de la broche D13 !&lt;br /&gt;
Il ne nous reste plus qu&#039;à décaler ce bit de 5 positions vers la droite pour pouvoir le lire. &lt;br /&gt;
&lt;br /&gt;
Les deux écritures suivantes sont équivalentes:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t state;&lt;br /&gt;
state = (PINB &amp;amp; B00100000) &amp;gt;&amp;gt; 5;&lt;br /&gt;
state = (PINB &amp;amp; B00100000) &amp;gt;&amp;gt; PB5;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Rien n’empêche d&#039;utiliser cela pour faire des tests:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Modification de D13 en sortie&lt;br /&gt;
DDRB |= B00100000;&lt;br /&gt;
// Passage de D13 à HIGH&lt;br /&gt;
PORTB |= (1 &amp;lt;&amp;lt; PB5);&lt;br /&gt;
if ((PINB &amp;amp; B00100000) &amp;gt;&amp;gt; PB5) {&lt;br /&gt;
    Serial.println(&amp;quot;D13 is ON&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
// Passage de D13 à LOW&lt;br /&gt;
PORTB &amp;amp;= ~(1 &amp;lt;&amp;lt; PB5);&lt;br /&gt;
if ((PINB &amp;amp; B00100000) &amp;gt;&amp;gt; PB5 == 0) {&lt;br /&gt;
    Serial.println(&amp;quot;D13 is OFF&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Lorsque l&#039;on exécute le code suivant on s&#039;aperçoit que rien ne s&#039;affiche. La raison est simple, la modification de registre est tellement rapide que l&#039;état électrique n&#039;a pas le temps d&#039;être modifier avant la lecture !&lt;br /&gt;
&lt;br /&gt;
Il suffit d&#039;ajouter:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
delay(1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Avant les lignes modifiant les états:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
PORTB |= (1 &amp;lt;&amp;lt; PB5);&lt;br /&gt;
...&lt;br /&gt;
PORTB &amp;amp;= ~(1 &amp;lt;&amp;lt; PB5);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Performances =&lt;br /&gt;
La &#039;&#039;&#039;véritable&#039;&#039;&#039; interrogation réside dans le faite de savoir si le jeu en vaut la chandelle ? Est-il intéressant de sacrifier la simplicité d&#039;écriture et la compréhension du code pour la performance ?&lt;br /&gt;
&lt;br /&gt;
Pour faire un test probant, allumons la led présente sur D13 100.000 fois avec:&lt;br /&gt;
* dans un premier temps &#039;&#039;pinMode()&#039;&#039; et &#039;&#039;digitalWrite()&#039;&#039;;&lt;br /&gt;
* dans un second temps &#039;&#039;DDRB&#039;&#039; et &#039;&#039;PORTB&#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
{|width=&amp;quot;100%&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Voila le premier test:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define LIMIT 100000&lt;br /&gt;
void setup() {&lt;br /&gt;
  Serial.begin(9600);&lt;br /&gt;
  int32_t timer = millis();&lt;br /&gt;
  pinMode(13, OUTPUT);&lt;br /&gt;
  for (uint32_t i = 0; i &amp;lt; LIMIT; i++) {&lt;br /&gt;
    digitalWrite(13, HIGH);&lt;br /&gt;
    digitalWrite(13, LOW);&lt;br /&gt;
  }&lt;br /&gt;
  int32_t elapsed = millis()  - timer;&lt;br /&gt;
  Serial.print(&amp;quot;Temps d&#039;exécution: &amp;quot;);&lt;br /&gt;
  Serial.println(elapsed);&lt;br /&gt;
}&lt;br /&gt;
void loop() {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Le croquis utilise &#039;&#039;&#039;2138&#039;&#039;&#039; octets et le résultat est:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Temps d’exécution: 709&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Voila le deuxième test:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define LIMIT 100000&lt;br /&gt;
void setup() {&lt;br /&gt;
  Serial.begin(9600);&lt;br /&gt;
  int32_t timer = millis();&lt;br /&gt;
  DDRB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
  for(uint32_t i = 0; i &amp;lt; LIMIT; i++){&lt;br /&gt;
    PORTB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
    PORTB &amp;amp;= ~(1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
  }&lt;br /&gt;
  int32_t elapsed = millis()  - timer;&lt;br /&gt;
  Serial.print(&amp;quot;Temps d&#039;exécution: &amp;quot;);&lt;br /&gt;
  Serial.println(elapsed);&lt;br /&gt;
}&lt;br /&gt;
void loop() {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Le croquis utilise &#039;&#039;&#039;1844&#039;&#039;&#039; octets et le résultat est:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Temps d’exécution: 62&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
On peut constater que le code utilisant les registre est non seulement plus léger (&#039;&#039;&#039;13,67%&#039;&#039;&#039;) mais également plus rapide (&#039;&#039;&#039;11.42&#039;&#039;&#039; fois) !&lt;/div&gt;</summary>
		<author><name>Magsss</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=Atmega328_registers&amp;diff=3292</id>
		<title>Atmega328 registers</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=Atmega328_registers&amp;diff=3292"/>
		<updated>2018-11-01T18:36:48Z</updated>

		<summary type="html">&lt;p&gt;Magsss : /* Le cas de l&amp;#039;ATmega328 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Lorsque l&#039;on commence à écrire des programmes qui sortent de l&#039;ordinaire ou que l&#039;on veut pousser un microcontrôleur à la limite de ses capacités, il est obligatoire de descendre au niveau des registres.&lt;br /&gt;
Seulement voilà, la manipulation de registres ne s&#039;improvise pas, nécessite de connaître le hardware que l&#039;on utilise et, de par sa nature, est &#039;&#039;&#039;spécifique&#039;&#039;&#039; à un type de microcontrôleur !&lt;br /&gt;
&lt;br /&gt;
Manipuler les registres implique donc de sacrifier la portabilité du code, offerte par l&#039;utilisation des fonctions &#039;&#039;haut niveau&#039;&#039; comme &#039;&#039;digitalWrite()&#039;&#039;, au profit de la vitesse d&#039;exécution et de la compacité du code.&lt;br /&gt;
&lt;br /&gt;
Les fonctions comme &#039;&#039;digitalWrite()&#039;&#039;, &#039;&#039;digitalRead()&#039;&#039;, &#039;&#039;pinMode()&#039;&#039; permettent au développeur d&#039;accomplir une action sans avoir à se soucier du type de microcontrôleur utilisé mais, in fine, elles vont elles-mêmes manipuler les registres pour accomplir ces tâches !&lt;br /&gt;
&lt;br /&gt;
= Le cas de l&#039;ATmega328 =&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
L&#039;AtMega328 est le microcontrôleur utilisé sur les Arduino Uno, pro, nano et bon nombre de circuits électroniques d&#039;autres marques ! Ci à droite, l&#039;association entre le nom des broches et leurs positions dans le registre.&lt;br /&gt;
&lt;br /&gt;
On peut remarquer que :&lt;br /&gt;
* les broches D0, D1, D2, D3, D4, D5 et D6 font partie du groupe &#039;&#039;&#039;D&#039;&#039;&#039; (P&#039;&#039;&#039;D&#039;&#039;&#039;);&lt;br /&gt;
* les broches D8, D9, D10, D11, D12 et D13 font partie du groupe &#039;&#039;&#039;B&#039;&#039;&#039; (P&#039;&#039;&#039;B&#039;&#039;&#039;);&lt;br /&gt;
* les broches analogiques font partie du groupe &#039;&#039;&#039;C&#039;&#039;&#039; (P&#039;&#039;&#039;C&#039;&#039;&#039;).&lt;br /&gt;
A chacun de ces groupes on peut associer trois registres :&lt;br /&gt;
* PORT &amp;amp;rarr; pour positionner l&#039;état du port (HIGH ou LOW);&lt;br /&gt;
* PIN &amp;amp;rarr; pour lire l&#039;état d&#039;un port;&lt;br /&gt;
* DDR (&#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;D&#039;&#039;&#039;irection &#039;&#039;&#039;R&#039;&#039;&#039;egister) &amp;amp;rarr; pour positionner le sens d&#039;un port (INPUT, OUTPUT);&lt;br /&gt;
On se retrouve donc avec 9 registres manipulables:&lt;br /&gt;
* PORTD, PIND et DDRD pour les broches D0, D1, D2, D3, D4, D5 et D6;&lt;br /&gt;
* PORTB, PINB et DDRB pour les broches D8, D9, D10, D11, D12 et D13;&lt;br /&gt;
* PORTC, PINC et DDRC pour les broches analogiques.&lt;br /&gt;
La position du bit dans le registre est déterminé par le numéro attenant à la lettre du groupe. Par exemple, si on prend le cas de la broche D13, c&#039;est le 5ème bit du groupe B (PB&#039;&#039;&#039;5&#039;&#039;&#039;) &lt;br /&gt;
[[Fichier:Mapping D13 atmega328.png]]&lt;br /&gt;
.&lt;br /&gt;
||&lt;br /&gt;
[[Fichier:Atmega328 pin mapping.png|centré]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Comment manipuler un registre ? =&lt;br /&gt;
Maintenant que l&#039;on à identifié le groupe et la position du bit des broches dans les registres, nous pouvons passer à la manipulation !&lt;br /&gt;
&lt;br /&gt;
Reprenons l&#039;exemple de la broche D13. Pour modifier sont état, il suffit de modifier la valeur de PORTB:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB = B00100000; // pour mettre D13 à HIGH&lt;br /&gt;
PORTB = B00000000; // pour mettre D13 à LOW&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Seulement voila, on sait que sa position dans les registres correspond au bit numéro 6 mais on a absolument aucune idée de la valeur de PORTB au moment de la modification !&lt;br /&gt;
La manipulation précédente va certainement modifier l&#039;état de D13 mais va aussi modifier l&#039;état des autre broches du groupe &#039;&#039;&#039;B&#039;&#039;&#039;. On ne peut donc pas agir de la sorte, il faut utiliser un opérateur logique pour modifier les registres et on va distinguer deux cas de figures: le passage à &#039;&#039;1&#039;&#039; et celui à &#039;&#039;0&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Passage à &#039;&#039;1&#039;&#039; ==&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;40%&amp;quot;|&lt;br /&gt;
Pour modifier l&#039;état d&#039;un registre à &#039;&#039;1&#039;&#039; sans modifier l&#039;état des autres registres nous allons utiliser l&#039;opérateur &#039;&#039;OU&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Si on reprend l&#039;exemple précédent, nous allons plutôt faire cela:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB |= B00100000; // pour mettre seulement D13 à HIGH&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Imaginons que PORTB est la valeur suivante &#039;&#039;B00010101&#039;&#039;, PORTB |= B00100000 revient à faire:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||0||1||0||1||0||1&lt;br /&gt;
|-&lt;br /&gt;
||ou||0||0||1||0||0||0||0||0&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;9&amp;quot;|&amp;lt;hr&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||1||1||0||1||0||1&lt;br /&gt;
|}&lt;br /&gt;
On a donc réussi à modifier la valeur de D13 sans modifier l&#039;état des autres broches !&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|width=&amp;quot;12%&amp;quot; valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
Table de vérité de OU&lt;br /&gt;
{|border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
A&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
B&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
A OU B&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|}&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;40%&amp;quot;|&lt;br /&gt;
On peut également effectuer cette manipulation par décalage de bit. Dans l&#039;exemple précédent nous avons utilisé &#039;&#039;B00100000&#039;&#039; pour faire le OU mais on aurait pu utiliser l&#039;écriture &#039;&#039;(1&amp;lt;&amp;lt;5)&#039;&#039; ou même la constante prévue à cet effet &#039;&#039;(1&amp;lt;&amp;lt;PB5)&#039;&#039;.&lt;br /&gt;
Le chiffre &#039;&#039;1&#039;&#039; est un entier qui s&#039;écrit en binaire comme cela:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||position||7||6||5||4||3||2||1||0&lt;br /&gt;
|-&lt;br /&gt;
||valeur||0||0||0||0||0||0||0||1&lt;br /&gt;
|}&lt;br /&gt;
Il nous suffit donc de le décaler de 5 position vers la gauche pour obtenir B00100000:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||position||7||6||5||4||3||2||1||0&lt;br /&gt;
|-&lt;br /&gt;
||valeur||0||0||1||0||0||0||0||0&lt;br /&gt;
|}&lt;br /&gt;
Les trois écritures suivantes sont donc équivalentes:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB |= B00100000;&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;5);&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Passage à &#039;&#039;0&#039;&#039; ==&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;40%&amp;quot;|&lt;br /&gt;
Pour modifier l&#039;état d&#039;un registre à &#039;&#039;1&#039;&#039; sans modifier l&#039;état des autres registres nous allons utiliser l&#039;opérateur &#039;&#039;ET&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Si on reprend l&#039;exemple précédent, nous allons plutôt faire cela:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB &amp;amp;= ~B00100000; // pour mettre seulement D13 à LOW&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
L&#039;opérateur &#039;&#039;~&#039;&#039; permet d&#039;obtenir le [https://fr.wikipedia.org/wiki/Compl%C3%A9ment_%C3%A0_un complément A1] d&#039;un nombre binaire: B00100000 devient alors B11011111.&lt;br /&gt;
&lt;br /&gt;
Imaginons que PORTB est la valeur suivante &#039;&#039;B00010101&#039;&#039;, PORTB &amp;amp;= ~B00100000 revient à faire:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||0||1||0||1||0||1&lt;br /&gt;
|-&lt;br /&gt;
||ET||1||1||0||1||1||1||1||1&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;9&amp;quot;|&amp;lt;hr&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||0||1||0||1||0||1&lt;br /&gt;
|}&lt;br /&gt;
On a donc réussi à modifier la valeur de D13 sans modifier l&#039;état des autres broches !&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|width=&amp;quot;12%&amp;quot; valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
Table de vérité de ET&lt;br /&gt;
{|border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
A&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
B&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
A ET B&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|}&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;40%&amp;quot;|&lt;br /&gt;
On peut également effectuer cette manipulation par décalage de bit. Dans l&#039;exemple précédent nous avons utilisé &#039;&#039;~B00100000&#039;&#039; pour faire le OU mais on aurait pu utiliser l&#039;écriture &#039;&#039;~(1&amp;lt;&amp;lt;5)&#039;&#039; ou même la constante prévue à cet effet &#039;&#039;~(1&amp;lt;&amp;lt;PB5)&#039;&#039;..&lt;br /&gt;
Les trois écritures suivantes sont donc équivalentes:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB &amp;amp;= ~B00100000;&lt;br /&gt;
PORTB &amp;amp;= ~(1&amp;lt;&amp;lt;5);&lt;br /&gt;
PORTB &amp;amp;= ~(1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Modification de l&#039;état d&#039;une broche =&lt;br /&gt;
Dans l&#039;exemple suivant nous allons modifier l&#039;état de la broche D13 pour la faire clignoter.&lt;br /&gt;
==Modification de sens ==&lt;br /&gt;
Dans un premier temps il faut la placer en sortie et pour cela nous allons manipuler le registre &#039;&#039;DDRB&#039;&#039;:&lt;br /&gt;
* la valeur &#039;&#039;1&#039;&#039; positionne la broche en sortie (&#039;&#039;OUTPUT&#039;&#039;);&lt;br /&gt;
* la valeur &#039;&#039;0&#039;&#039; positionne la broche en entrée (&#039;&#039;INPUT&#039;&#039;), si la l&#039;état est positionné à &#039;&#039;1&#039;&#039; cela active la résistance de &#039;&#039;pullup&#039;&#039; (&#039;&#039;INPUT_PULLUP&#039;&#039;).&lt;br /&gt;
Nous allons donc modifier le registre DDRB comme suit, les trois notations sont équivalentes:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
DDRB |= B00100000; // Positionnement de D13 en OUTPUT&lt;br /&gt;
DDRB |= (1&amp;lt;&amp;lt;5);&lt;br /&gt;
DDRB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Modification d&#039;état ==&lt;br /&gt;
On peut maintenant modifier son état grâce au registre PORTB:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB |= B00100000; // Positionnement de D13 à HIGH&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;5);&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Lecture d&#039;état==&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Il est possible de lire l&#039;état de la broche D13 grâce au registre &#039;&#039;PINB&#039;&#039;. Nous allons utiliser la même technique du masquage que précédemment avec l&#039;opérateur &#039;&#039;ET&#039;&#039;.&lt;br /&gt;
Imaginons que PINB est la valeur suivante &#039;&#039;B00110101&#039;&#039;, PINB &amp;amp;= B00100000 revient à faire:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||1||1||0||1||0||1&lt;br /&gt;
|-&lt;br /&gt;
||ET||0||0||1||0||0||0||0||0&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;9&amp;quot;|&amp;lt;hr&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||1||0||0||0||0||0&lt;br /&gt;
|}&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
On va donc retirer l&#039;état des autres broches sans modifier pour autant celui de la broche D13 !&lt;br /&gt;
Il ne nous reste plus qu&#039;à décaler ce bit de 5 positions vers la droite pour pouvoir le lire. &lt;br /&gt;
&lt;br /&gt;
Les deux écritures suivantes sont équivalentes:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t state;&lt;br /&gt;
state = (PINB &amp;amp; B00100000) &amp;gt;&amp;gt; 5;&lt;br /&gt;
state = (PINB &amp;amp; B00100000) &amp;gt;&amp;gt; PB5;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Rien n’empêche d&#039;utiliser cela pour faire des tests:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Modification de D13 en sortie&lt;br /&gt;
DDRB |= B00100000;&lt;br /&gt;
// Passage de D13 à HIGH&lt;br /&gt;
PORTB |= (1 &amp;lt;&amp;lt; PB5);&lt;br /&gt;
if ((PINB &amp;amp; B00100000) &amp;gt;&amp;gt; PB5) {&lt;br /&gt;
    Serial.println(&amp;quot;D13 is ON&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
// Passage de D13 à LOW&lt;br /&gt;
PORTB &amp;amp;= ~(1 &amp;lt;&amp;lt; PB5);&lt;br /&gt;
if ((PINB &amp;amp; B00100000) &amp;gt;&amp;gt; PB5 == 0) {&lt;br /&gt;
    Serial.println(&amp;quot;D13 is OFF&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Lorsque l&#039;on exécute le code suivant on s&#039;aperçoit que rien ne s&#039;affiche. La raison est simple, la modification de registre est tellement rapide que l&#039;état électrique n&#039;a pas le temps d&#039;être modifier avant la lecture !&lt;br /&gt;
&lt;br /&gt;
Il suffit d&#039;ajouter:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
delay(1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Avant les lignes modifiant les états:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
PORTB |= (1 &amp;lt;&amp;lt; PB5);&lt;br /&gt;
...&lt;br /&gt;
PORTB &amp;amp;= ~(1 &amp;lt;&amp;lt; PB5);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Performances =&lt;br /&gt;
La &#039;&#039;&#039;véritable&#039;&#039;&#039; interrogation réside dans le faite de savoir si le jeu en vaut la chandelle ? Est-il intéressant de sacrifier la simplicité d&#039;écriture et la compréhension du code pour la performance ?&lt;br /&gt;
&lt;br /&gt;
Pour faire un test probant, allumons la led présente sur D13 100.000 fois avec:&lt;br /&gt;
* dans un premier temps &#039;&#039;pinMode()&#039;&#039; et &#039;&#039;digitalWrite()&#039;&#039;;&lt;br /&gt;
* dans un second temps &#039;&#039;DDRB&#039;&#039; et &#039;&#039;PORTB&#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
{|width=&amp;quot;100%&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Voila le premier test:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define LIMIT 100000&lt;br /&gt;
void setup() {&lt;br /&gt;
  Serial.begin(9600);&lt;br /&gt;
  int32_t timer = millis();&lt;br /&gt;
  pinMode(13, OUTPUT);&lt;br /&gt;
  for (uint32_t i = 0; i &amp;lt; LIMIT; i++) {&lt;br /&gt;
    digitalWrite(13, HIGH);&lt;br /&gt;
    digitalWrite(13, LOW);&lt;br /&gt;
  }&lt;br /&gt;
  int32_t elapsed = millis()  - timer;&lt;br /&gt;
  Serial.print(&amp;quot;Temps d&#039;exécution: &amp;quot;);&lt;br /&gt;
  Serial.println(elapsed);&lt;br /&gt;
}&lt;br /&gt;
void loop() {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Le croquis utilise &#039;&#039;&#039;2138&#039;&#039;&#039; octets et le résultat est:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Temps d’exécution: 709&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Voila le deuxième test:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define LIMIT 100000&lt;br /&gt;
void setup() {&lt;br /&gt;
  Serial.begin(9600);&lt;br /&gt;
  int32_t timer = millis();&lt;br /&gt;
  DDRB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
  for(uint32_t i = 0; i &amp;lt; LIMIT; i++){&lt;br /&gt;
    PORTB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
    PORTB &amp;amp;= ~(1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
  }&lt;br /&gt;
  int32_t elapsed = millis()  - timer;&lt;br /&gt;
  Serial.print(&amp;quot;Temps d&#039;exécution: &amp;quot;);&lt;br /&gt;
  Serial.println(elapsed);&lt;br /&gt;
}&lt;br /&gt;
void loop() {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Le croquis utilise &#039;&#039;&#039;1844&#039;&#039;&#039; octets et le résultat est:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Temps d’exécution: 62&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
On peut constater que le code utilisant les registre est non seulement plus léger (&#039;&#039;&#039;13,67%&#039;&#039;&#039;) mais également plus rapide (&#039;&#039;&#039;11.42&#039;&#039;&#039; fois) !&lt;/div&gt;</summary>
		<author><name>Magsss</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=Atmega328_registers&amp;diff=3291</id>
		<title>Atmega328 registers</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=Atmega328_registers&amp;diff=3291"/>
		<updated>2018-11-01T18:36:14Z</updated>

		<summary type="html">&lt;p&gt;Magsss : /* Le cas de l&amp;#039;ATmega328 */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Lorsque l&#039;on commence à écrire des programmes qui sortent de l&#039;ordinaire ou que l&#039;on veut pousser un microcontrôleur à la limite de ses capacités, il est obligatoire de descendre au niveau des registres.&lt;br /&gt;
Seulement voilà, la manipulation de registres ne s&#039;improvise pas, nécessite de connaître le hardware que l&#039;on utilise et, de par sa nature, est &#039;&#039;&#039;spécifique&#039;&#039;&#039; à un type de microcontrôleur !&lt;br /&gt;
&lt;br /&gt;
Manipuler les registres implique donc de sacrifier la portabilité du code, offerte par l&#039;utilisation des fonctions &#039;&#039;haut niveau&#039;&#039; comme &#039;&#039;digitalWrite()&#039;&#039;, au profit de la vitesse d&#039;exécution et de la compacité du code.&lt;br /&gt;
&lt;br /&gt;
Les fonctions comme &#039;&#039;digitalWrite()&#039;&#039;, &#039;&#039;digitalRead()&#039;&#039;, &#039;&#039;pinMode()&#039;&#039; permettent au développeur d&#039;accomplir une action sans avoir à se soucier du type de microcontrôleur utilisé mais, in fine, elles vont elles-mêmes manipuler les registres pour accomplir ces tâches !&lt;br /&gt;
&lt;br /&gt;
= Le cas de l&#039;ATmega328 =&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
L&#039;AtMega328 est le microcontrôleur utilisé sur les Arduino Uno, pro, nano et bon nombre de circuits électroniques d&#039;autres marques ! Ci à droite, l&#039;association entre le nom des broches et leurs positions dans le registre.&lt;br /&gt;
&lt;br /&gt;
On peut remarquer que :&lt;br /&gt;
* les broches D0, D1, D2, D3, D4, D5 et D6 font partie du groupe &#039;&#039;&#039;D&#039;&#039;&#039; (P&#039;&#039;&#039;D&#039;&#039;&#039;);&lt;br /&gt;
* les broches D8, D9, D10, D11, D12 et D13 font partie du groupe &#039;&#039;&#039;B&#039;&#039;&#039; (P&#039;&#039;&#039;B&#039;&#039;&#039;);&lt;br /&gt;
* les broches analogiques font partie du groupe &#039;&#039;&#039;C&#039;&#039;&#039; (P&#039;&#039;&#039;C&#039;&#039;&#039;).&lt;br /&gt;
A chacun de ces groupes on peut associer trois registres :&lt;br /&gt;
* PORT &amp;amp;rarr; pour positionner l&#039;état du port (HIGH ou LOW);&lt;br /&gt;
* PIN &amp;amp;rarr; pour lire l&#039;état d&#039;un port;&lt;br /&gt;
* DDR (&#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;D&#039;&#039;&#039;irection &#039;&#039;&#039;R&#039;&#039;&#039;egister) &amp;amp;rarr; pour positionner le sens d&#039;un port (INPUT, OUTPUT);&lt;br /&gt;
On se retrouve donc avec 9 registres manipulables:&lt;br /&gt;
* PORTD, PIND et DDRD pour les broches D0, D1, D2, D3, D4, D5 et D6;&lt;br /&gt;
* PORTB, PINB et DDRB pour les broches D8, D9, D10, D11, D12 et D13;&lt;br /&gt;
* PORTC, PINC et DDRC pour les broches analogiques.&lt;br /&gt;
La position du bit dans le registre est déterminé par le numéro attenant à la lettre du groupe. Par exemple, si on prend le cas de la broche D13, c&#039;est le 5émé bit du groupe B (PB&#039;&#039;&#039;5&#039;&#039;&#039;) &lt;br /&gt;
[[Fichier:Mapping D13 atmega328.png]]&lt;br /&gt;
.&lt;br /&gt;
||&lt;br /&gt;
[[Fichier:Atmega328 pin mapping.png|centré]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Comment manipuler un registre ? =&lt;br /&gt;
Maintenant que l&#039;on à identifié le groupe et la position du bit des broches dans les registres, nous pouvons passer à la manipulation !&lt;br /&gt;
&lt;br /&gt;
Reprenons l&#039;exemple de la broche D13. Pour modifier sont état, il suffit de modifier la valeur de PORTB:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB = B00100000; // pour mettre D13 à HIGH&lt;br /&gt;
PORTB = B00000000; // pour mettre D13 à LOW&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Seulement voila, on sait que sa position dans les registres correspond au bit numéro 6 mais on a absolument aucune idée de la valeur de PORTB au moment de la modification !&lt;br /&gt;
La manipulation précédente va certainement modifier l&#039;état de D13 mais va aussi modifier l&#039;état des autre broches du groupe &#039;&#039;&#039;B&#039;&#039;&#039;. On ne peut donc pas agir de la sorte, il faut utiliser un opérateur logique pour modifier les registres et on va distinguer deux cas de figures: le passage à &#039;&#039;1&#039;&#039; et celui à &#039;&#039;0&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Passage à &#039;&#039;1&#039;&#039; ==&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;40%&amp;quot;|&lt;br /&gt;
Pour modifier l&#039;état d&#039;un registre à &#039;&#039;1&#039;&#039; sans modifier l&#039;état des autres registres nous allons utiliser l&#039;opérateur &#039;&#039;OU&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Si on reprend l&#039;exemple précédent, nous allons plutôt faire cela:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB |= B00100000; // pour mettre seulement D13 à HIGH&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Imaginons que PORTB est la valeur suivante &#039;&#039;B00010101&#039;&#039;, PORTB |= B00100000 revient à faire:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||0||1||0||1||0||1&lt;br /&gt;
|-&lt;br /&gt;
||ou||0||0||1||0||0||0||0||0&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;9&amp;quot;|&amp;lt;hr&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||1||1||0||1||0||1&lt;br /&gt;
|}&lt;br /&gt;
On a donc réussi à modifier la valeur de D13 sans modifier l&#039;état des autres broches !&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|width=&amp;quot;12%&amp;quot; valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
Table de vérité de OU&lt;br /&gt;
{|border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
A&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
B&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
A OU B&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|}&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;40%&amp;quot;|&lt;br /&gt;
On peut également effectuer cette manipulation par décalage de bit. Dans l&#039;exemple précédent nous avons utilisé &#039;&#039;B00100000&#039;&#039; pour faire le OU mais on aurait pu utiliser l&#039;écriture &#039;&#039;(1&amp;lt;&amp;lt;5)&#039;&#039; ou même la constante prévue à cet effet &#039;&#039;(1&amp;lt;&amp;lt;PB5)&#039;&#039;.&lt;br /&gt;
Le chiffre &#039;&#039;1&#039;&#039; est un entier qui s&#039;écrit en binaire comme cela:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||position||7||6||5||4||3||2||1||0&lt;br /&gt;
|-&lt;br /&gt;
||valeur||0||0||0||0||0||0||0||1&lt;br /&gt;
|}&lt;br /&gt;
Il nous suffit donc de le décaler de 5 position vers la gauche pour obtenir B00100000:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||position||7||6||5||4||3||2||1||0&lt;br /&gt;
|-&lt;br /&gt;
||valeur||0||0||1||0||0||0||0||0&lt;br /&gt;
|}&lt;br /&gt;
Les trois écritures suivantes sont donc équivalentes:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB |= B00100000;&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;5);&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Passage à &#039;&#039;0&#039;&#039; ==&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;40%&amp;quot;|&lt;br /&gt;
Pour modifier l&#039;état d&#039;un registre à &#039;&#039;1&#039;&#039; sans modifier l&#039;état des autres registres nous allons utiliser l&#039;opérateur &#039;&#039;ET&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Si on reprend l&#039;exemple précédent, nous allons plutôt faire cela:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB &amp;amp;= ~B00100000; // pour mettre seulement D13 à LOW&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
L&#039;opérateur &#039;&#039;~&#039;&#039; permet d&#039;obtenir le [https://fr.wikipedia.org/wiki/Compl%C3%A9ment_%C3%A0_un complément A1] d&#039;un nombre binaire: B00100000 devient alors B11011111.&lt;br /&gt;
&lt;br /&gt;
Imaginons que PORTB est la valeur suivante &#039;&#039;B00010101&#039;&#039;, PORTB &amp;amp;= ~B00100000 revient à faire:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||0||1||0||1||0||1&lt;br /&gt;
|-&lt;br /&gt;
||ET||1||1||0||1||1||1||1||1&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;9&amp;quot;|&amp;lt;hr&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||0||1||0||1||0||1&lt;br /&gt;
|}&lt;br /&gt;
On a donc réussi à modifier la valeur de D13 sans modifier l&#039;état des autres broches !&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|width=&amp;quot;12%&amp;quot; valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
Table de vérité de ET&lt;br /&gt;
{|border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
A&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
B&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
A ET B&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|}&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;40%&amp;quot;|&lt;br /&gt;
On peut également effectuer cette manipulation par décalage de bit. Dans l&#039;exemple précédent nous avons utilisé &#039;&#039;~B00100000&#039;&#039; pour faire le OU mais on aurait pu utiliser l&#039;écriture &#039;&#039;~(1&amp;lt;&amp;lt;5)&#039;&#039; ou même la constante prévue à cet effet &#039;&#039;~(1&amp;lt;&amp;lt;PB5)&#039;&#039;..&lt;br /&gt;
Les trois écritures suivantes sont donc équivalentes:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB &amp;amp;= ~B00100000;&lt;br /&gt;
PORTB &amp;amp;= ~(1&amp;lt;&amp;lt;5);&lt;br /&gt;
PORTB &amp;amp;= ~(1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Modification de l&#039;état d&#039;une broche =&lt;br /&gt;
Dans l&#039;exemple suivant nous allons modifier l&#039;état de la broche D13 pour la faire clignoter.&lt;br /&gt;
==Modification de sens ==&lt;br /&gt;
Dans un premier temps il faut la placer en sortie et pour cela nous allons manipuler le registre &#039;&#039;DDRB&#039;&#039;:&lt;br /&gt;
* la valeur &#039;&#039;1&#039;&#039; positionne la broche en sortie (&#039;&#039;OUTPUT&#039;&#039;);&lt;br /&gt;
* la valeur &#039;&#039;0&#039;&#039; positionne la broche en entrée (&#039;&#039;INPUT&#039;&#039;), si la l&#039;état est positionné à &#039;&#039;1&#039;&#039; cela active la résistance de &#039;&#039;pullup&#039;&#039; (&#039;&#039;INPUT_PULLUP&#039;&#039;).&lt;br /&gt;
Nous allons donc modifier le registre DDRB comme suit, les trois notations sont équivalentes:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
DDRB |= B00100000; // Positionnement de D13 en OUTPUT&lt;br /&gt;
DDRB |= (1&amp;lt;&amp;lt;5);&lt;br /&gt;
DDRB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Modification d&#039;état ==&lt;br /&gt;
On peut maintenant modifier son état grâce au registre PORTB:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB |= B00100000; // Positionnement de D13 à HIGH&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;5);&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Lecture d&#039;état==&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Il est possible de lire l&#039;état de la broche D13 grâce au registre &#039;&#039;PINB&#039;&#039;. Nous allons utiliser la même technique du masquage que précédemment avec l&#039;opérateur &#039;&#039;ET&#039;&#039;.&lt;br /&gt;
Imaginons que PINB est la valeur suivante &#039;&#039;B00110101&#039;&#039;, PINB &amp;amp;= B00100000 revient à faire:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||1||1||0||1||0||1&lt;br /&gt;
|-&lt;br /&gt;
||ET||0||0||1||0||0||0||0||0&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;9&amp;quot;|&amp;lt;hr&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||1||0||0||0||0||0&lt;br /&gt;
|}&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
On va donc retirer l&#039;état des autres broches sans modifier pour autant celui de la broche D13 !&lt;br /&gt;
Il ne nous reste plus qu&#039;à décaler ce bit de 5 positions vers la droite pour pouvoir le lire. &lt;br /&gt;
&lt;br /&gt;
Les deux écritures suivantes sont équivalentes:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t state;&lt;br /&gt;
state = (PINB &amp;amp; B00100000) &amp;gt;&amp;gt; 5;&lt;br /&gt;
state = (PINB &amp;amp; B00100000) &amp;gt;&amp;gt; PB5;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Rien n’empêche d&#039;utiliser cela pour faire des tests:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Modification de D13 en sortie&lt;br /&gt;
DDRB |= B00100000;&lt;br /&gt;
// Passage de D13 à HIGH&lt;br /&gt;
PORTB |= (1 &amp;lt;&amp;lt; PB5);&lt;br /&gt;
if ((PINB &amp;amp; B00100000) &amp;gt;&amp;gt; PB5) {&lt;br /&gt;
    Serial.println(&amp;quot;D13 is ON&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
// Passage de D13 à LOW&lt;br /&gt;
PORTB &amp;amp;= ~(1 &amp;lt;&amp;lt; PB5);&lt;br /&gt;
if ((PINB &amp;amp; B00100000) &amp;gt;&amp;gt; PB5 == 0) {&lt;br /&gt;
    Serial.println(&amp;quot;D13 is OFF&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Lorsque l&#039;on exécute le code suivant on s&#039;aperçoit que rien ne s&#039;affiche. La raison est simple, la modification de registre est tellement rapide que l&#039;état électrique n&#039;a pas le temps d&#039;être modifier avant la lecture !&lt;br /&gt;
&lt;br /&gt;
Il suffit d&#039;ajouter:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
delay(1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Avant les lignes modifiant les états:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
PORTB |= (1 &amp;lt;&amp;lt; PB5);&lt;br /&gt;
...&lt;br /&gt;
PORTB &amp;amp;= ~(1 &amp;lt;&amp;lt; PB5);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Performances =&lt;br /&gt;
La &#039;&#039;&#039;véritable&#039;&#039;&#039; interrogation réside dans le faite de savoir si le jeu en vaut la chandelle ? Est-il intéressant de sacrifier la simplicité d&#039;écriture et la compréhension du code pour la performance ?&lt;br /&gt;
&lt;br /&gt;
Pour faire un test probant, allumons la led présente sur D13 100.000 fois avec:&lt;br /&gt;
* dans un premier temps &#039;&#039;pinMode()&#039;&#039; et &#039;&#039;digitalWrite()&#039;&#039;;&lt;br /&gt;
* dans un second temps &#039;&#039;DDRB&#039;&#039; et &#039;&#039;PORTB&#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
{|width=&amp;quot;100%&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Voila le premier test:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define LIMIT 100000&lt;br /&gt;
void setup() {&lt;br /&gt;
  Serial.begin(9600);&lt;br /&gt;
  int32_t timer = millis();&lt;br /&gt;
  pinMode(13, OUTPUT);&lt;br /&gt;
  for (uint32_t i = 0; i &amp;lt; LIMIT; i++) {&lt;br /&gt;
    digitalWrite(13, HIGH);&lt;br /&gt;
    digitalWrite(13, LOW);&lt;br /&gt;
  }&lt;br /&gt;
  int32_t elapsed = millis()  - timer;&lt;br /&gt;
  Serial.print(&amp;quot;Temps d&#039;exécution: &amp;quot;);&lt;br /&gt;
  Serial.println(elapsed);&lt;br /&gt;
}&lt;br /&gt;
void loop() {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Le croquis utilise &#039;&#039;&#039;2138&#039;&#039;&#039; octets et le résultat est:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Temps d’exécution: 709&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Voila le deuxième test:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define LIMIT 100000&lt;br /&gt;
void setup() {&lt;br /&gt;
  Serial.begin(9600);&lt;br /&gt;
  int32_t timer = millis();&lt;br /&gt;
  DDRB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
  for(uint32_t i = 0; i &amp;lt; LIMIT; i++){&lt;br /&gt;
    PORTB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
    PORTB &amp;amp;= ~(1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
  }&lt;br /&gt;
  int32_t elapsed = millis()  - timer;&lt;br /&gt;
  Serial.print(&amp;quot;Temps d&#039;exécution: &amp;quot;);&lt;br /&gt;
  Serial.println(elapsed);&lt;br /&gt;
}&lt;br /&gt;
void loop() {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Le croquis utilise &#039;&#039;&#039;1844&#039;&#039;&#039; octets et le résultat est:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Temps d’exécution: 62&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
On peut constater que le code utilisant les registre est non seulement plus léger (&#039;&#039;&#039;13,67%&#039;&#039;&#039;) mais également plus rapide (&#039;&#039;&#039;11.42&#039;&#039;&#039; fois) !&lt;/div&gt;</summary>
		<author><name>Magsss</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=Atmega328_registers&amp;diff=3290</id>
		<title>Atmega328 registers</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=Atmega328_registers&amp;diff=3290"/>
		<updated>2018-11-01T18:35:13Z</updated>

		<summary type="html">&lt;p&gt;Magsss : /* Introduction */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Lorsque l&#039;on commence à écrire des programmes qui sortent de l&#039;ordinaire ou que l&#039;on veut pousser un microcontrôleur à la limite de ses capacités, il est obligatoire de descendre au niveau des registres.&lt;br /&gt;
Seulement voilà, la manipulation de registres ne s&#039;improvise pas, nécessite de connaître le hardware que l&#039;on utilise et, de par sa nature, est &#039;&#039;&#039;spécifique&#039;&#039;&#039; à un type de microcontrôleur !&lt;br /&gt;
&lt;br /&gt;
Manipuler les registres implique donc de sacrifier la portabilité du code, offerte par l&#039;utilisation des fonctions &#039;&#039;haut niveau&#039;&#039; comme &#039;&#039;digitalWrite()&#039;&#039;, au profit de la vitesse d&#039;exécution et de la compacité du code.&lt;br /&gt;
&lt;br /&gt;
Les fonctions comme &#039;&#039;digitalWrite()&#039;&#039;, &#039;&#039;digitalRead()&#039;&#039;, &#039;&#039;pinMode()&#039;&#039; permettent au développeur d&#039;accomplir une action sans avoir à se soucier du type de microcontrôleur utilisé mais, in fine, elles vont elles-mêmes manipuler les registres pour accomplir ces tâches !&lt;br /&gt;
&lt;br /&gt;
= Le cas de l&#039;ATmega328 =&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
L&#039;AtMega328 est le microcontrôleur utilisé sur les Arduino Uno, pro, nano et bon nombre de circuits électroniques d&#039;autres marques ! Ci à droite, l&#039;association entre le nom des broches et leurs positions dans le registre.&lt;br /&gt;
&lt;br /&gt;
On peut remarquer que :&lt;br /&gt;
* les broches D0, D1, D2, D3, D4, D5 et D6 font partie du groupe &#039;&#039;&#039;D&#039;&#039;&#039; (P&#039;&#039;&#039;D&#039;&#039;&#039;);&lt;br /&gt;
* les broches D8, D9, D10, D11, D12 et D13 font partie du groupe &#039;&#039;&#039;B&#039;&#039;&#039; (P&#039;&#039;&#039;B&#039;&#039;&#039;);&lt;br /&gt;
* les broches analogique font partie du groupe &#039;&#039;&#039;C&#039;&#039;&#039; (P&#039;&#039;&#039;C&#039;&#039;&#039;).&lt;br /&gt;
A chacun de ces groupes on peut associer trois registres :&lt;br /&gt;
* PORT &amp;amp;rarr; pour positionner l&#039;état du port (HIGH ou LOW);&lt;br /&gt;
* PIN &amp;amp;rarr; pour lire l&#039;état d&#039;un port;&lt;br /&gt;
* DDR (&#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;D&#039;&#039;&#039;irection &#039;&#039;&#039;R&#039;&#039;&#039;egister) &amp;amp;rarr; pour positionner le sens d&#039;un port (INPUT, OUTPUT);&lt;br /&gt;
On se retrouve donc avec 9 registres manipulables:&lt;br /&gt;
* PORTD, PIND et DDRD pour les broches D0, D1, D2, D3, D4, D5 et D6;&lt;br /&gt;
* PORTB, PINB et DDRB pour les broches D8, D9, D10, D11, D12 et D13;&lt;br /&gt;
* PORTC, PINC et DDRC pour les broches analogiques.&lt;br /&gt;
La position du bit dans le registre est déterminé par le numéro attenant à la lettre du groupe. Par exemple, si on prend le cas de la broche D13, c&#039;est le 5émé bit du groupe B (PB&#039;&#039;&#039;5&#039;&#039;&#039;) &lt;br /&gt;
[[Fichier:Mapping D13 atmega328.png]]&lt;br /&gt;
.&lt;br /&gt;
||&lt;br /&gt;
[[Fichier:Atmega328 pin mapping.png|centré]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Comment manipuler un registre ? =&lt;br /&gt;
Maintenant que l&#039;on à identifié le groupe et la position du bit des broches dans les registres, nous pouvons passer à la manipulation !&lt;br /&gt;
&lt;br /&gt;
Reprenons l&#039;exemple de la broche D13. Pour modifier sont état, il suffit de modifier la valeur de PORTB:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB = B00100000; // pour mettre D13 à HIGH&lt;br /&gt;
PORTB = B00000000; // pour mettre D13 à LOW&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Seulement voila, on sait que sa position dans les registres correspond au bit numéro 6 mais on a absolument aucune idée de la valeur de PORTB au moment de la modification !&lt;br /&gt;
La manipulation précédente va certainement modifier l&#039;état de D13 mais va aussi modifier l&#039;état des autre broches du groupe &#039;&#039;&#039;B&#039;&#039;&#039;. On ne peut donc pas agir de la sorte, il faut utiliser un opérateur logique pour modifier les registres et on va distinguer deux cas de figures: le passage à &#039;&#039;1&#039;&#039; et celui à &#039;&#039;0&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Passage à &#039;&#039;1&#039;&#039; ==&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;40%&amp;quot;|&lt;br /&gt;
Pour modifier l&#039;état d&#039;un registre à &#039;&#039;1&#039;&#039; sans modifier l&#039;état des autres registres nous allons utiliser l&#039;opérateur &#039;&#039;OU&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Si on reprend l&#039;exemple précédent, nous allons plutôt faire cela:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB |= B00100000; // pour mettre seulement D13 à HIGH&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Imaginons que PORTB est la valeur suivante &#039;&#039;B00010101&#039;&#039;, PORTB |= B00100000 revient à faire:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||0||1||0||1||0||1&lt;br /&gt;
|-&lt;br /&gt;
||ou||0||0||1||0||0||0||0||0&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;9&amp;quot;|&amp;lt;hr&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||1||1||0||1||0||1&lt;br /&gt;
|}&lt;br /&gt;
On a donc réussi à modifier la valeur de D13 sans modifier l&#039;état des autres broches !&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|width=&amp;quot;12%&amp;quot; valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
Table de vérité de OU&lt;br /&gt;
{|border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
A&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
B&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
A OU B&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|}&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;40%&amp;quot;|&lt;br /&gt;
On peut également effectuer cette manipulation par décalage de bit. Dans l&#039;exemple précédent nous avons utilisé &#039;&#039;B00100000&#039;&#039; pour faire le OU mais on aurait pu utiliser l&#039;écriture &#039;&#039;(1&amp;lt;&amp;lt;5)&#039;&#039; ou même la constante prévue à cet effet &#039;&#039;(1&amp;lt;&amp;lt;PB5)&#039;&#039;.&lt;br /&gt;
Le chiffre &#039;&#039;1&#039;&#039; est un entier qui s&#039;écrit en binaire comme cela:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||position||7||6||5||4||3||2||1||0&lt;br /&gt;
|-&lt;br /&gt;
||valeur||0||0||0||0||0||0||0||1&lt;br /&gt;
|}&lt;br /&gt;
Il nous suffit donc de le décaler de 5 position vers la gauche pour obtenir B00100000:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||position||7||6||5||4||3||2||1||0&lt;br /&gt;
|-&lt;br /&gt;
||valeur||0||0||1||0||0||0||0||0&lt;br /&gt;
|}&lt;br /&gt;
Les trois écritures suivantes sont donc équivalentes:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB |= B00100000;&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;5);&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Passage à &#039;&#039;0&#039;&#039; ==&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;40%&amp;quot;|&lt;br /&gt;
Pour modifier l&#039;état d&#039;un registre à &#039;&#039;1&#039;&#039; sans modifier l&#039;état des autres registres nous allons utiliser l&#039;opérateur &#039;&#039;ET&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Si on reprend l&#039;exemple précédent, nous allons plutôt faire cela:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB &amp;amp;= ~B00100000; // pour mettre seulement D13 à LOW&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
L&#039;opérateur &#039;&#039;~&#039;&#039; permet d&#039;obtenir le [https://fr.wikipedia.org/wiki/Compl%C3%A9ment_%C3%A0_un complément A1] d&#039;un nombre binaire: B00100000 devient alors B11011111.&lt;br /&gt;
&lt;br /&gt;
Imaginons que PORTB est la valeur suivante &#039;&#039;B00010101&#039;&#039;, PORTB &amp;amp;= ~B00100000 revient à faire:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||0||1||0||1||0||1&lt;br /&gt;
|-&lt;br /&gt;
||ET||1||1||0||1||1||1||1||1&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;9&amp;quot;|&amp;lt;hr&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||0||1||0||1||0||1&lt;br /&gt;
|}&lt;br /&gt;
On a donc réussi à modifier la valeur de D13 sans modifier l&#039;état des autres broches !&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|width=&amp;quot;12%&amp;quot; valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
Table de vérité de ET&lt;br /&gt;
{|border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
A&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
B&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
A ET B&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|}&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;40%&amp;quot;|&lt;br /&gt;
On peut également effectuer cette manipulation par décalage de bit. Dans l&#039;exemple précédent nous avons utilisé &#039;&#039;~B00100000&#039;&#039; pour faire le OU mais on aurait pu utiliser l&#039;écriture &#039;&#039;~(1&amp;lt;&amp;lt;5)&#039;&#039; ou même la constante prévue à cet effet &#039;&#039;~(1&amp;lt;&amp;lt;PB5)&#039;&#039;..&lt;br /&gt;
Les trois écritures suivantes sont donc équivalentes:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB &amp;amp;= ~B00100000;&lt;br /&gt;
PORTB &amp;amp;= ~(1&amp;lt;&amp;lt;5);&lt;br /&gt;
PORTB &amp;amp;= ~(1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Modification de l&#039;état d&#039;une broche =&lt;br /&gt;
Dans l&#039;exemple suivant nous allons modifier l&#039;état de la broche D13 pour la faire clignoter.&lt;br /&gt;
==Modification de sens ==&lt;br /&gt;
Dans un premier temps il faut la placer en sortie et pour cela nous allons manipuler le registre &#039;&#039;DDRB&#039;&#039;:&lt;br /&gt;
* la valeur &#039;&#039;1&#039;&#039; positionne la broche en sortie (&#039;&#039;OUTPUT&#039;&#039;);&lt;br /&gt;
* la valeur &#039;&#039;0&#039;&#039; positionne la broche en entrée (&#039;&#039;INPUT&#039;&#039;), si la l&#039;état est positionné à &#039;&#039;1&#039;&#039; cela active la résistance de &#039;&#039;pullup&#039;&#039; (&#039;&#039;INPUT_PULLUP&#039;&#039;).&lt;br /&gt;
Nous allons donc modifier le registre DDRB comme suit, les trois notations sont équivalentes:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
DDRB |= B00100000; // Positionnement de D13 en OUTPUT&lt;br /&gt;
DDRB |= (1&amp;lt;&amp;lt;5);&lt;br /&gt;
DDRB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Modification d&#039;état ==&lt;br /&gt;
On peut maintenant modifier son état grâce au registre PORTB:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB |= B00100000; // Positionnement de D13 à HIGH&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;5);&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Lecture d&#039;état==&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Il est possible de lire l&#039;état de la broche D13 grâce au registre &#039;&#039;PINB&#039;&#039;. Nous allons utiliser la même technique du masquage que précédemment avec l&#039;opérateur &#039;&#039;ET&#039;&#039;.&lt;br /&gt;
Imaginons que PINB est la valeur suivante &#039;&#039;B00110101&#039;&#039;, PINB &amp;amp;= B00100000 revient à faire:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||1||1||0||1||0||1&lt;br /&gt;
|-&lt;br /&gt;
||ET||0||0||1||0||0||0||0||0&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;9&amp;quot;|&amp;lt;hr&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||1||0||0||0||0||0&lt;br /&gt;
|}&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
On va donc retirer l&#039;état des autres broches sans modifier pour autant celui de la broche D13 !&lt;br /&gt;
Il ne nous reste plus qu&#039;à décaler ce bit de 5 positions vers la droite pour pouvoir le lire. &lt;br /&gt;
&lt;br /&gt;
Les deux écritures suivantes sont équivalentes:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t state;&lt;br /&gt;
state = (PINB &amp;amp; B00100000) &amp;gt;&amp;gt; 5;&lt;br /&gt;
state = (PINB &amp;amp; B00100000) &amp;gt;&amp;gt; PB5;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Rien n’empêche d&#039;utiliser cela pour faire des tests:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Modification de D13 en sortie&lt;br /&gt;
DDRB |= B00100000;&lt;br /&gt;
// Passage de D13 à HIGH&lt;br /&gt;
PORTB |= (1 &amp;lt;&amp;lt; PB5);&lt;br /&gt;
if ((PINB &amp;amp; B00100000) &amp;gt;&amp;gt; PB5) {&lt;br /&gt;
    Serial.println(&amp;quot;D13 is ON&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
// Passage de D13 à LOW&lt;br /&gt;
PORTB &amp;amp;= ~(1 &amp;lt;&amp;lt; PB5);&lt;br /&gt;
if ((PINB &amp;amp; B00100000) &amp;gt;&amp;gt; PB5 == 0) {&lt;br /&gt;
    Serial.println(&amp;quot;D13 is OFF&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Lorsque l&#039;on exécute le code suivant on s&#039;aperçoit que rien ne s&#039;affiche. La raison est simple, la modification de registre est tellement rapide que l&#039;état électrique n&#039;a pas le temps d&#039;être modifier avant la lecture !&lt;br /&gt;
&lt;br /&gt;
Il suffit d&#039;ajouter:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
delay(1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Avant les lignes modifiant les états:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
PORTB |= (1 &amp;lt;&amp;lt; PB5);&lt;br /&gt;
...&lt;br /&gt;
PORTB &amp;amp;= ~(1 &amp;lt;&amp;lt; PB5);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Performances =&lt;br /&gt;
La &#039;&#039;&#039;véritable&#039;&#039;&#039; interrogation réside dans le faite de savoir si le jeu en vaut la chandelle ? Est-il intéressant de sacrifier la simplicité d&#039;écriture et la compréhension du code pour la performance ?&lt;br /&gt;
&lt;br /&gt;
Pour faire un test probant, allumons la led présente sur D13 100.000 fois avec:&lt;br /&gt;
* dans un premier temps &#039;&#039;pinMode()&#039;&#039; et &#039;&#039;digitalWrite()&#039;&#039;;&lt;br /&gt;
* dans un second temps &#039;&#039;DDRB&#039;&#039; et &#039;&#039;PORTB&#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
{|width=&amp;quot;100%&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Voila le premier test:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define LIMIT 100000&lt;br /&gt;
void setup() {&lt;br /&gt;
  Serial.begin(9600);&lt;br /&gt;
  int32_t timer = millis();&lt;br /&gt;
  pinMode(13, OUTPUT);&lt;br /&gt;
  for (uint32_t i = 0; i &amp;lt; LIMIT; i++) {&lt;br /&gt;
    digitalWrite(13, HIGH);&lt;br /&gt;
    digitalWrite(13, LOW);&lt;br /&gt;
  }&lt;br /&gt;
  int32_t elapsed = millis()  - timer;&lt;br /&gt;
  Serial.print(&amp;quot;Temps d&#039;exécution: &amp;quot;);&lt;br /&gt;
  Serial.println(elapsed);&lt;br /&gt;
}&lt;br /&gt;
void loop() {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Le croquis utilise &#039;&#039;&#039;2138&#039;&#039;&#039; octets et le résultat est:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Temps d’exécution: 709&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Voila le deuxième test:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define LIMIT 100000&lt;br /&gt;
void setup() {&lt;br /&gt;
  Serial.begin(9600);&lt;br /&gt;
  int32_t timer = millis();&lt;br /&gt;
  DDRB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
  for(uint32_t i = 0; i &amp;lt; LIMIT; i++){&lt;br /&gt;
    PORTB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
    PORTB &amp;amp;= ~(1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
  }&lt;br /&gt;
  int32_t elapsed = millis()  - timer;&lt;br /&gt;
  Serial.print(&amp;quot;Temps d&#039;exécution: &amp;quot;);&lt;br /&gt;
  Serial.println(elapsed);&lt;br /&gt;
}&lt;br /&gt;
void loop() {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Le croquis utilise &#039;&#039;&#039;1844&#039;&#039;&#039; octets et le résultat est:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Temps d’exécution: 62&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
On peut constater que le code utilisant les registre est non seulement plus léger (&#039;&#039;&#039;13,67%&#039;&#039;&#039;) mais également plus rapide (&#039;&#039;&#039;11.42&#039;&#039;&#039; fois) !&lt;/div&gt;</summary>
		<author><name>Magsss</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=Atmega328_registers&amp;diff=3289</id>
		<title>Atmega328 registers</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=Atmega328_registers&amp;diff=3289"/>
		<updated>2018-11-01T18:33:22Z</updated>

		<summary type="html">&lt;p&gt;Magsss : /* Introduction */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Lorsque l&#039;on commence à écrire des programmes qui sortent de l&#039;ordinaire ou que l&#039;on veut pousser un microcontrôleur à la limite de ses capacités, il est obligatoire de descendre au niveau des registres.&lt;br /&gt;
Seulement voila, la manipulation de registre ne s&#039;improvise pas, nécessite de connaître le hardware que l&#039;on utilise et, de par sa nature, est &#039;&#039;&#039;spécifique&#039;&#039;&#039; à un type de microcontrôleur !&lt;br /&gt;
&lt;br /&gt;
Manipuler les registre implique donc de sacrifier la portabilité du code, offerte par l&#039;utilisation des fonctions &#039;&#039;haut niveau&#039;&#039; comme &#039;&#039;digitalWrite()&#039;&#039;, au profit de la vitesse d&#039;exécution et de la compacité du code.&lt;br /&gt;
&lt;br /&gt;
Les fonctions comme &#039;&#039;digitalWrite()&#039;&#039;, &#039;&#039;digitalRead()&#039;&#039;, &#039;&#039;pinMode()&#039;&#039; permettent au développeur d&#039;accomplir une action sans avoir à se soucier du type de microcontrôleur utilisé mais, in finé, elle vont elle même manipuler les registres pour accomplir ces tâches !&lt;br /&gt;
&lt;br /&gt;
= Le cas de l&#039;ATmega328 =&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
L&#039;AtMega328 est le microcontrôleur utilisé sur les Arduino Uno, pro, nano et bon nombre de circuits électroniques d&#039;autres marques ! Ci à droite, l&#039;association entre le nom des broches et leurs positions dans le registre.&lt;br /&gt;
&lt;br /&gt;
On peut remarquer que :&lt;br /&gt;
* les broches D0, D1, D2, D3, D4, D5 et D6 font partie du groupe &#039;&#039;&#039;D&#039;&#039;&#039; (P&#039;&#039;&#039;D&#039;&#039;&#039;);&lt;br /&gt;
* les broches D8, D9, D10, D11, D12 et D13 font partie du groupe &#039;&#039;&#039;B&#039;&#039;&#039; (P&#039;&#039;&#039;B&#039;&#039;&#039;);&lt;br /&gt;
* les broches analogique font partie du groupe &#039;&#039;&#039;C&#039;&#039;&#039; (P&#039;&#039;&#039;C&#039;&#039;&#039;).&lt;br /&gt;
A chacun de ces groupes on peut associer trois registres :&lt;br /&gt;
* PORT &amp;amp;rarr; pour positionner l&#039;état du port (HIGH ou LOW);&lt;br /&gt;
* PIN &amp;amp;rarr; pour lire l&#039;état d&#039;un port;&lt;br /&gt;
* DDR (&#039;&#039;&#039;D&#039;&#039;&#039;ata &#039;&#039;&#039;D&#039;&#039;&#039;irection &#039;&#039;&#039;R&#039;&#039;&#039;egister) &amp;amp;rarr; pour positionner le sens d&#039;un port (INPUT, OUTPUT);&lt;br /&gt;
On se retrouve donc avec 9 registres manipulables:&lt;br /&gt;
* PORTD, PIND et DDRD pour les broches D0, D1, D2, D3, D4, D5 et D6;&lt;br /&gt;
* PORTB, PINB et DDRB pour les broches D8, D9, D10, D11, D12 et D13;&lt;br /&gt;
* PORTC, PINC et DDRC pour les broches analogiques.&lt;br /&gt;
La position du bit dans le registre est déterminé par le numéro attenant à la lettre du groupe. Par exemple, si on prend le cas de la broche D13, c&#039;est le 5émé bit du groupe B (PB&#039;&#039;&#039;5&#039;&#039;&#039;) &lt;br /&gt;
[[Fichier:Mapping D13 atmega328.png]]&lt;br /&gt;
.&lt;br /&gt;
||&lt;br /&gt;
[[Fichier:Atmega328 pin mapping.png|centré]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Comment manipuler un registre ? =&lt;br /&gt;
Maintenant que l&#039;on à identifié le groupe et la position du bit des broches dans les registres, nous pouvons passer à la manipulation !&lt;br /&gt;
&lt;br /&gt;
Reprenons l&#039;exemple de la broche D13. Pour modifier sont état, il suffit de modifier la valeur de PORTB:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB = B00100000; // pour mettre D13 à HIGH&lt;br /&gt;
PORTB = B00000000; // pour mettre D13 à LOW&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Seulement voila, on sait que sa position dans les registres correspond au bit numéro 6 mais on a absolument aucune idée de la valeur de PORTB au moment de la modification !&lt;br /&gt;
La manipulation précédente va certainement modifier l&#039;état de D13 mais va aussi modifier l&#039;état des autre broches du groupe &#039;&#039;&#039;B&#039;&#039;&#039;. On ne peut donc pas agir de la sorte, il faut utiliser un opérateur logique pour modifier les registres et on va distinguer deux cas de figures: le passage à &#039;&#039;1&#039;&#039; et celui à &#039;&#039;0&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
== Passage à &#039;&#039;1&#039;&#039; ==&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;40%&amp;quot;|&lt;br /&gt;
Pour modifier l&#039;état d&#039;un registre à &#039;&#039;1&#039;&#039; sans modifier l&#039;état des autres registres nous allons utiliser l&#039;opérateur &#039;&#039;OU&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Si on reprend l&#039;exemple précédent, nous allons plutôt faire cela:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB |= B00100000; // pour mettre seulement D13 à HIGH&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Imaginons que PORTB est la valeur suivante &#039;&#039;B00010101&#039;&#039;, PORTB |= B00100000 revient à faire:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||0||1||0||1||0||1&lt;br /&gt;
|-&lt;br /&gt;
||ou||0||0||1||0||0||0||0||0&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;9&amp;quot;|&amp;lt;hr&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||1||1||0||1||0||1&lt;br /&gt;
|}&lt;br /&gt;
On a donc réussi à modifier la valeur de D13 sans modifier l&#039;état des autres broches !&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|width=&amp;quot;12%&amp;quot; valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
Table de vérité de OU&lt;br /&gt;
{|border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
A&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
B&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
A OU B&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|}&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;40%&amp;quot;|&lt;br /&gt;
On peut également effectuer cette manipulation par décalage de bit. Dans l&#039;exemple précédent nous avons utilisé &#039;&#039;B00100000&#039;&#039; pour faire le OU mais on aurait pu utiliser l&#039;écriture &#039;&#039;(1&amp;lt;&amp;lt;5)&#039;&#039; ou même la constante prévue à cet effet &#039;&#039;(1&amp;lt;&amp;lt;PB5)&#039;&#039;.&lt;br /&gt;
Le chiffre &#039;&#039;1&#039;&#039; est un entier qui s&#039;écrit en binaire comme cela:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||position||7||6||5||4||3||2||1||0&lt;br /&gt;
|-&lt;br /&gt;
||valeur||0||0||0||0||0||0||0||1&lt;br /&gt;
|}&lt;br /&gt;
Il nous suffit donc de le décaler de 5 position vers la gauche pour obtenir B00100000:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||position||7||6||5||4||3||2||1||0&lt;br /&gt;
|-&lt;br /&gt;
||valeur||0||0||1||0||0||0||0||0&lt;br /&gt;
|}&lt;br /&gt;
Les trois écritures suivantes sont donc équivalentes:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB |= B00100000;&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;5);&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Passage à &#039;&#039;0&#039;&#039; ==&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;40%&amp;quot;|&lt;br /&gt;
Pour modifier l&#039;état d&#039;un registre à &#039;&#039;1&#039;&#039; sans modifier l&#039;état des autres registres nous allons utiliser l&#039;opérateur &#039;&#039;ET&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Si on reprend l&#039;exemple précédent, nous allons plutôt faire cela:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB &amp;amp;= ~B00100000; // pour mettre seulement D13 à LOW&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
L&#039;opérateur &#039;&#039;~&#039;&#039; permet d&#039;obtenir le [https://fr.wikipedia.org/wiki/Compl%C3%A9ment_%C3%A0_un complément A1] d&#039;un nombre binaire: B00100000 devient alors B11011111.&lt;br /&gt;
&lt;br /&gt;
Imaginons que PORTB est la valeur suivante &#039;&#039;B00010101&#039;&#039;, PORTB &amp;amp;= ~B00100000 revient à faire:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||0||1||0||1||0||1&lt;br /&gt;
|-&lt;br /&gt;
||ET||1||1||0||1||1||1||1||1&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;9&amp;quot;|&amp;lt;hr&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||0||1||0||1||0||1&lt;br /&gt;
|}&lt;br /&gt;
On a donc réussi à modifier la valeur de D13 sans modifier l&#039;état des autres broches !&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|width=&amp;quot;12%&amp;quot; valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
Table de vérité de ET&lt;br /&gt;
{|border=&amp;quot;1&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
A&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
B&lt;br /&gt;
|width=&amp;quot;60px&amp;quot; align=&amp;quot;center&amp;quot;|&lt;br /&gt;
A ET B&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
0&lt;br /&gt;
|-&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|align=&amp;quot;center&amp;quot;|&lt;br /&gt;
1&lt;br /&gt;
|}&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;40%&amp;quot;|&lt;br /&gt;
On peut également effectuer cette manipulation par décalage de bit. Dans l&#039;exemple précédent nous avons utilisé &#039;&#039;~B00100000&#039;&#039; pour faire le OU mais on aurait pu utiliser l&#039;écriture &#039;&#039;~(1&amp;lt;&amp;lt;5)&#039;&#039; ou même la constante prévue à cet effet &#039;&#039;~(1&amp;lt;&amp;lt;PB5)&#039;&#039;..&lt;br /&gt;
Les trois écritures suivantes sont donc équivalentes:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB &amp;amp;= ~B00100000;&lt;br /&gt;
PORTB &amp;amp;= ~(1&amp;lt;&amp;lt;5);&lt;br /&gt;
PORTB &amp;amp;= ~(1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Modification de l&#039;état d&#039;une broche =&lt;br /&gt;
Dans l&#039;exemple suivant nous allons modifier l&#039;état de la broche D13 pour la faire clignoter.&lt;br /&gt;
==Modification de sens ==&lt;br /&gt;
Dans un premier temps il faut la placer en sortie et pour cela nous allons manipuler le registre &#039;&#039;DDRB&#039;&#039;:&lt;br /&gt;
* la valeur &#039;&#039;1&#039;&#039; positionne la broche en sortie (&#039;&#039;OUTPUT&#039;&#039;);&lt;br /&gt;
* la valeur &#039;&#039;0&#039;&#039; positionne la broche en entrée (&#039;&#039;INPUT&#039;&#039;), si la l&#039;état est positionné à &#039;&#039;1&#039;&#039; cela active la résistance de &#039;&#039;pullup&#039;&#039; (&#039;&#039;INPUT_PULLUP&#039;&#039;).&lt;br /&gt;
Nous allons donc modifier le registre DDRB comme suit, les trois notations sont équivalentes:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
DDRB |= B00100000; // Positionnement de D13 en OUTPUT&lt;br /&gt;
DDRB |= (1&amp;lt;&amp;lt;5);&lt;br /&gt;
DDRB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Modification d&#039;état ==&lt;br /&gt;
On peut maintenant modifier son état grâce au registre PORTB:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
PORTB |= B00100000; // Positionnement de D13 à HIGH&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;5);&lt;br /&gt;
PORTB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Lecture d&#039;état==&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Il est possible de lire l&#039;état de la broche D13 grâce au registre &#039;&#039;PINB&#039;&#039;. Nous allons utiliser la même technique du masquage que précédemment avec l&#039;opérateur &#039;&#039;ET&#039;&#039;.&lt;br /&gt;
Imaginons que PINB est la valeur suivante &#039;&#039;B00110101&#039;&#039;, PINB &amp;amp;= B00100000 revient à faire:&lt;br /&gt;
{|align=&amp;quot;center&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||1||1||0||1||0||1&lt;br /&gt;
|-&lt;br /&gt;
||ET||0||0||1||0||0||0||0||0&lt;br /&gt;
|-&lt;br /&gt;
|colspan=&amp;quot;9&amp;quot;|&amp;lt;hr&amp;gt;&lt;br /&gt;
|-&lt;br /&gt;
||||0||0||1||0||0||0||0||0&lt;br /&gt;
|}&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
On va donc retirer l&#039;état des autres broches sans modifier pour autant celui de la broche D13 !&lt;br /&gt;
Il ne nous reste plus qu&#039;à décaler ce bit de 5 positions vers la droite pour pouvoir le lire. &lt;br /&gt;
&lt;br /&gt;
Les deux écritures suivantes sont équivalentes:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
uint8_t state;&lt;br /&gt;
state = (PINB &amp;amp; B00100000) &amp;gt;&amp;gt; 5;&lt;br /&gt;
state = (PINB &amp;amp; B00100000) &amp;gt;&amp;gt; PB5;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;hr&amp;gt;&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Rien n’empêche d&#039;utiliser cela pour faire des tests:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Modification de D13 en sortie&lt;br /&gt;
DDRB |= B00100000;&lt;br /&gt;
// Passage de D13 à HIGH&lt;br /&gt;
PORTB |= (1 &amp;lt;&amp;lt; PB5);&lt;br /&gt;
if ((PINB &amp;amp; B00100000) &amp;gt;&amp;gt; PB5) {&lt;br /&gt;
    Serial.println(&amp;quot;D13 is ON&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
// Passage de D13 à LOW&lt;br /&gt;
PORTB &amp;amp;= ~(1 &amp;lt;&amp;lt; PB5);&lt;br /&gt;
if ((PINB &amp;amp; B00100000) &amp;gt;&amp;gt; PB5 == 0) {&lt;br /&gt;
    Serial.println(&amp;quot;D13 is OFF&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Lorsque l&#039;on exécute le code suivant on s&#039;aperçoit que rien ne s&#039;affiche. La raison est simple, la modification de registre est tellement rapide que l&#039;état électrique n&#039;a pas le temps d&#039;être modifier avant la lecture !&lt;br /&gt;
&lt;br /&gt;
Il suffit d&#039;ajouter:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
delay(1);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Avant les lignes modifiant les états:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
...&lt;br /&gt;
PORTB |= (1 &amp;lt;&amp;lt; PB5);&lt;br /&gt;
...&lt;br /&gt;
PORTB &amp;amp;= ~(1 &amp;lt;&amp;lt; PB5);&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Performances =&lt;br /&gt;
La &#039;&#039;&#039;véritable&#039;&#039;&#039; interrogation réside dans le faite de savoir si le jeu en vaut la chandelle ? Est-il intéressant de sacrifier la simplicité d&#039;écriture et la compréhension du code pour la performance ?&lt;br /&gt;
&lt;br /&gt;
Pour faire un test probant, allumons la led présente sur D13 100.000 fois avec:&lt;br /&gt;
* dans un premier temps &#039;&#039;pinMode()&#039;&#039; et &#039;&#039;digitalWrite()&#039;&#039;;&lt;br /&gt;
* dans un second temps &#039;&#039;DDRB&#039;&#039; et &#039;&#039;PORTB&#039;&#039;;&lt;br /&gt;
&lt;br /&gt;
{|width=&amp;quot;100%&amp;quot;&lt;br /&gt;
|-&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Voila le premier test:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define LIMIT 100000&lt;br /&gt;
void setup() {&lt;br /&gt;
  Serial.begin(9600);&lt;br /&gt;
  int32_t timer = millis();&lt;br /&gt;
  pinMode(13, OUTPUT);&lt;br /&gt;
  for (uint32_t i = 0; i &amp;lt; LIMIT; i++) {&lt;br /&gt;
    digitalWrite(13, HIGH);&lt;br /&gt;
    digitalWrite(13, LOW);&lt;br /&gt;
  }&lt;br /&gt;
  int32_t elapsed = millis()  - timer;&lt;br /&gt;
  Serial.print(&amp;quot;Temps d&#039;exécution: &amp;quot;);&lt;br /&gt;
  Serial.println(elapsed);&lt;br /&gt;
}&lt;br /&gt;
void loop() {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Le croquis utilise &#039;&#039;&#039;2138&#039;&#039;&#039; octets et le résultat est:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Temps d’exécution: 709&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|width=&amp;quot;1%&amp;quot;|&lt;br /&gt;
|valign=&amp;quot;top&amp;quot; width=&amp;quot;50%&amp;quot;|&lt;br /&gt;
Voila le deuxième test:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define LIMIT 100000&lt;br /&gt;
void setup() {&lt;br /&gt;
  Serial.begin(9600);&lt;br /&gt;
  int32_t timer = millis();&lt;br /&gt;
  DDRB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
  for(uint32_t i = 0; i &amp;lt; LIMIT; i++){&lt;br /&gt;
    PORTB |= (1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
    PORTB &amp;amp;= ~(1&amp;lt;&amp;lt;PB5);&lt;br /&gt;
  }&lt;br /&gt;
  int32_t elapsed = millis()  - timer;&lt;br /&gt;
  Serial.print(&amp;quot;Temps d&#039;exécution: &amp;quot;);&lt;br /&gt;
  Serial.println(elapsed);&lt;br /&gt;
}&lt;br /&gt;
void loop() {&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Le croquis utilise &#039;&#039;&#039;1844&#039;&#039;&#039; octets et le résultat est:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Temps d’exécution: 62&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
|}&lt;br /&gt;
On peut constater que le code utilisant les registre est non seulement plus léger (&#039;&#039;&#039;13,67%&#039;&#039;&#039;) mais également plus rapide (&#039;&#039;&#039;11.42&#039;&#039;&#039; fois) !&lt;/div&gt;</summary>
		<author><name>Magsss</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=C_socket&amp;diff=3288</id>
		<title>C socket</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=C_socket&amp;diff=3288"/>
		<updated>2018-11-01T18:31:58Z</updated>

		<summary type="html">&lt;p&gt;Magsss : /* Performances */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
||&lt;br /&gt;
Les sockets permettent de connecter deux programmes qui s&#039;exécutent sur deux machines différentes. Cette connexion se fait à travers le réseau grâce à l&#039;utilisation d&#039;un port (TCP ou UDP) et d&#039;une adresse IP. &lt;br /&gt;
&lt;br /&gt;
Il y a deux types de sockets, une qui est démarrée par la partie serveur en &#039;&#039;écoute&#039;&#039; et l&#039;autre démarrée par la partie cliente qui se connecte à la première.&lt;br /&gt;
&lt;br /&gt;
Ci-contre une image résumant les différentes étapes pour arriver à l&#039;envoi de données.&lt;br /&gt;
||&lt;br /&gt;
[[Fichier:Socket workflow.png|centré|200px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Création  =&lt;br /&gt;
== Côté serveur ==&lt;br /&gt;
Tout d&#039;abord il faut créer l&#039;objet socket:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt; &lt;br /&gt;
&lt;br /&gt;
int socket(int domain, int type, int protocol)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*domain &amp;amp;rarr; integer, communication domain e.g., AF_INET (IPv4 protocol) , AF_INET6 (IPv6 protocol)&lt;br /&gt;
*type &amp;amp;rarr; type de communication&lt;br /&gt;
**SOCK_STREAM: TCP&lt;br /&gt;
**SOCK_DGRAM: UDP&lt;br /&gt;
**SOCK_RAW: socket à l&#039;état brut (bas niveau)&lt;br /&gt;
*protocol &amp;amp;rarr; valeur du champ &#039;&#039;protocol&#039;&#039; de l&#039;en-tête de niveau 3 (généralement &#039;&#039;0&#039;&#039;)&lt;br /&gt;
*la valeur de retour est le fichier descripteur de la socket ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Une fois le descripteur de socket créé, il est possible de le configurer:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt; &lt;br /&gt;
&lt;br /&gt;
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* level &amp;amp;rarr; niveau d&#039;application de l&#039;option (SOL_SOCKET, TCP, UDP, ...);&lt;br /&gt;
* optname &amp;amp;rarr; le nom de l&#039;option (&#039;&#039;SO_REUSEADDR&#039;&#039;, &#039;&#039;SO_REUSEPORT&#039;&#039;, &#039;&#039;SO_KEEPALIVE&#039;&#039;, ...)&lt;br /&gt;
* optval, optlen  &amp;amp;rarr; utilisé pour accéder aux options de la socket;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
Il existe plusieurs options de socket, les plus utilisées étant :&lt;br /&gt;
* &#039;&#039;SO_REUSEADDR&#039;&#039; &amp;amp;rarr; permet de réutiliser l&#039;adresse de la socket tout de suite même si cette dernière est dans l&#039;état &#039;&#039;wait&#039;&#039;;&lt;br /&gt;
* &#039;&#039;SO_REUSEPORT&#039;&#039; &amp;amp;rarr; permet de réutiliser le port de la socket tout de suite même si cette dernière est dans l&#039;état &#039;&#039;wait&#039;&#039;;&lt;br /&gt;
* &#039;&#039;SO_KEEPALIVE&#039;&#039; &amp;amp;rarr; envoie des informations périodiquement pour tester si l&#039;extrémité du tunnel est toujours présente;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Il faut maintenant attacher la socket à un port Internet et une adresse IP:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* addr &amp;amp;rarr; une structure symbolisant l&#039;adresse &lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
struct sockaddr_in {&lt;br /&gt;
    short            sin_family;   // e.g: AF_INET&lt;br /&gt;
    unsigned short   sin_port;     // e.g: htons(3490)&lt;br /&gt;
    struct in_addr   sin_addr;     // détaillé ci-dessous&lt;br /&gt;
    char             sin_zero[8];  // &#039;0&#039; habituellement&lt;br /&gt;
};&lt;br /&gt;
struct in_addr {&lt;br /&gt;
    unsigned long s_addr;  // initialiser avec inet_aton()&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On peut maintenant passer la socket en état d&#039;écoute, elle est prête à recevoir des connexions:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int listen(int sockfd, int backlog)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* backlog &amp;amp;rarr; taille maximum de la file d&#039;attente de la socket après laquelle le système répond avec &#039;&#039;ECONNREFUSED&#039;&#039;;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On peut maintenant attendre une connexion:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* addr &amp;amp;rarr; l&#039;adresse du client;&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de la structure addr;&lt;br /&gt;
* le code retour varie entre un entier positif qui correspond au descripteur de la socket cliente et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
== Côté client ==&lt;br /&gt;
Après avoir créé la socket avec &#039;&#039;socket&#039;&#039; on peut se connecter à la partie serveur:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* addr &amp;amp;rarr; une structure symbolisant l&#039;adresse (cf. ci-dessus)&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
= Lecture / écriture =&lt;br /&gt;
== Fonctions génériques ==&lt;br /&gt;
Tout d&#039;abord, on peut dire que la socket est un fichier et se manipule donc comme un fichier !&lt;br /&gt;
===Lecture===&lt;br /&gt;
Pour lire dans une socket on peut utiliser &#039;&#039;read&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t read(int fd, void *buf, size_t count);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères où mettre le message reçu;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à recevoir;&lt;br /&gt;
* le code retour correspond au nombre de caractères reçus ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
===Écriture===&lt;br /&gt;
Pour écrire dans une socket ou peut utiliser &#039;&#039;write&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t write(int fd, const void *buf, size_t count);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères contenant le message à envoyer;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à envoyer;&lt;br /&gt;
* le code retour correspond au nombre de caractères envoyés ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
===Modification===&lt;br /&gt;
Pour modifier un descripteur fichier de socket on utilise &#039;&#039;fcntl&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int fcntl(int fd, int cmd, ... /* int args */ );&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* cmd &amp;amp;rarr; la commande de modification (descripteur, drapeaux, ...);&lt;br /&gt;
* args &amp;amp;rarr; les arguments correspondant à la commande&lt;br /&gt;
&lt;br /&gt;
Cette commande va nous permettre de modifier le drapeau du descripteur de la socket (&#039;&#039;F_SETFL&#039;&#039;) pour le rendre non bloquant (&#039;&#039;O_NONBLOCK&#039;&#039;) !&lt;br /&gt;
&lt;br /&gt;
== Fonctions spécifiques ==&lt;br /&gt;
Il existe des fonctions spécifiques pour manipuler les sockets. Ces fonctions ressemblent aux précédentes mais acceptent des drapeaux en plus pour pouvoir modifier le descripteur &#039;&#039;à la volée&#039;&#039;.&lt;br /&gt;
===Lecture===&lt;br /&gt;
Pour lire dans une socket on peut utiliser &#039;&#039;recv&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t recv(int fd, void *buf, size_t count, int flags);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères où mettre le message reçu;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à recevoir;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;O_NONBLOCK&#039;&#039;);&lt;br /&gt;
* le code retour correspond au nombre de caractères reçus ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
===Écriture===&lt;br /&gt;
Pour écrire dans une socket ou peut utiliser &#039;&#039;send&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t send(int fd, const void *buf, size_t count, int flags);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères contenant le message à envoyer;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à envoyer;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;MSG_CONFIRM&#039;&#039;, &#039;&#039;MSG_DONTWAIT &#039;&#039;, ...);&lt;br /&gt;
* le code retour correspond au nombre de caractères envoyés ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
= Cas d&#039;utilisation =&lt;br /&gt;
== Création ==&lt;br /&gt;
Pour commencer, il faut créer le descripteur de fichier:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int fdsocket;&lt;br /&gt;
if ((fdsocket = socket(AF_INET, SOCK_STREAM, 0)) == -1) {&lt;br /&gt;
	printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Paramétrage des options ==&lt;br /&gt;
Il faut maintenant configurer la réutilisation de l&#039;adresse et du port&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int opt = 1;&lt;br /&gt;
if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,	sizeof(opt)) != 0) {&lt;br /&gt;
	printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Attachement ==&lt;br /&gt;
On peut maintenant attacher la socket à un port et une adresse:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
struct sockaddr_in adresse;&lt;br /&gt;
&lt;br /&gt;
adresse.sin_family = AF_INET;&lt;br /&gt;
// Ecoute sur toutes les adresses (INADDR_ANY &amp;lt;=&amp;gt; 0.0.0.0)&lt;br /&gt;
adresse.sin_addr.s_addr = INADDR_ANY;&lt;br /&gt;
// Conversion du port en valeur réseaux (Host TO Network Short)&lt;br /&gt;
adresse.sin_port = htons(8080);&lt;br /&gt;
&lt;br /&gt;
if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
	printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mise en écoute ==&lt;br /&gt;
La socket est prête à passer à l&#039;écoute des connexions des clients:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
&lt;br /&gt;
if (listen(fdsocket, BACKLOG) != 0) {&lt;br /&gt;
	printf(&amp;quot;Echéc de la mise en écoute: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Acceptation des connexions ==&lt;br /&gt;
Il est possible d&#039;accepter les nouvelles connexions:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int clientSocket;&lt;br /&gt;
// Structure contenant l&#039;adresse du client&lt;br /&gt;
struct sockaddr_in clientAdresse;&lt;br /&gt;
int addrLen = sizeof(clientAdresse);&lt;br /&gt;
if ((clientSocket = accept(*serverSocket, (struct sockaddr*) &amp;amp;clientAdresse, (socklen_t*) &amp;amp;addrLen)) != -1) {&lt;br /&gt;
	// Convertion de l&#039;IP en texte&lt;br /&gt;
	char ip[INET_ADDRSTRLEN];&lt;br /&gt;
	inet_ntop(AF_INET, &amp;amp;(clientAdresse.sin_addr), ip, INET_ADDRSTRLEN);&lt;br /&gt;
	printf(&amp;quot;Connexion de %s:%i\n&amp;quot;, ip, clientAdresse.sin_port);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Il est possible d&#039;utiliser &#039;&#039;fcntl&#039;&#039; pour passer le descripteur de la socket en non bloquant, avant l&#039;appel à la fonction &#039;&#039;accept&#039;&#039;. Si le descripteur reste bloquant, la fonction &#039;&#039;accept&#039;&#039; ne rendra pas la main tant qu&#039;un client ne se sera connecté, ayant pour résultat le blocage complet du programme !&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Passage en mode non bloquant&lt;br /&gt;
fcntl(fdsocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
On peut remarquer, au passage, l&#039;utilisation de &#039;&#039;inet_ntop&#039;&#039; pour convertir l&#039;adresse du client du format binaire au format text.&lt;br /&gt;
&lt;br /&gt;
== Lecture / Écriture ==&lt;br /&gt;
=== Mode connecté (TCP) ===&lt;br /&gt;
On peut maintenant utiliser le descripteur de la socket du client pour lire et écrire.&lt;br /&gt;
* avec les fonctions génériques:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Descripteur de la socket du client&lt;br /&gt;
int clientSocket;&lt;br /&gt;
char buffer[BUFFER_LEN];&lt;br /&gt;
// Passage en mode non bloquant, sinon read attend&lt;br /&gt;
fcntl(clientSocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
int len = read(clientSocket, buffer, BUFFER_LEN);&lt;br /&gt;
&lt;br /&gt;
send(clientSocket, &amp;quot;Coucou\n&amp;quot;, strlen(&amp;quot;Coucou\n&amp;quot;));&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* avec les fonctions spécifiques:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Descripteur de la socket du client&lt;br /&gt;
int clientSocket;&lt;br /&gt;
char buffer[BUFFER_LEN];&lt;br /&gt;
&lt;br /&gt;
int len = recv(clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
&lt;br /&gt;
send(clientSocket, &amp;quot;Coucou\n&amp;quot;, strlen(&amp;quot;Coucou\n&amp;quot;), MSG_DONTWAIT);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Analysons le retour de la fonction &#039;&#039;read&#039;&#039; ou &#039;&#039;recv&#039;&#039; lorsque l&#039;on utilise un descripteur non bloquant d&#039;après la [http://man7.org/linux/man-pages/man2/recvmsg.2.html documentation] :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
NAME &lt;br /&gt;
       recv, recvfrom, recvmsg - receive a message from a socket&lt;br /&gt;
[...]&lt;br /&gt;
MSG_DONTWAIT (since Linux 2.2)&lt;br /&gt;
    Enables nonblocking operation; if the operation would block, the call fails with the error EAGAIN or EWOULDBLOCK (this can also be enabled using the O_NONBLOCK flag with the F_SETFL fcntl(2)). &lt;br /&gt;
[...]&lt;br /&gt;
RETURN VALUE&lt;br /&gt;
[...]&lt;br /&gt;
These calls return the number of bytes received, or -1 if an error&lt;br /&gt;
       occurred.  In the event of an error, errno is set to indicate the&lt;br /&gt;
       error.&lt;br /&gt;
When a stream socket peer has performed an orderly shutdown, the&lt;br /&gt;
       return value will be 0 (the traditional &amp;quot;end-of-file&amp;quot; return).&lt;br /&gt;
[...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Le retour est :&lt;br /&gt;
* lorsque le descripteur est vide (aucune donnée de la part du client) &#039;&#039;-1&#039;&#039; et &#039;&#039;errno&#039;&#039; sera positionné à la valeur &#039;&#039;EAGAIN&#039;&#039;, il ne faut donc pas fermer la socket si le retour est &#039;&#039;-1&#039;&#039; ! Il faut d&#039;abord vérifier que &#039;&#039;errno&#039;&#039; &#039;&#039;&#039;n&#039;a pas&#039;&#039;&#039; la valeur &#039;&#039;EAGAIN&#039;&#039;;&lt;br /&gt;
* lorsque le retour est &#039;&#039;0&#039;&#039;, cela signifie que l&#039;extrémité est fermée, il faut donc fermer le descripteur local;&lt;br /&gt;
* lorsque la valeur est supérieure à &#039;&#039;0&#039;&#039; elle correspond au nombre d&#039;octets reçus.&lt;br /&gt;
&lt;br /&gt;
On peut donc en déduire le morceau de code suivant pour gérer le retour de &#039;&#039;read&#039;&#039; ou &#039;&#039;recv&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int len = recv(clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
&lt;br /&gt;
if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
	// Une erreur est survenue&lt;br /&gt;
} else if (len == 0) {&lt;br /&gt;
	// Le client s&#039;est déconnecté (extrémité de la socket fermée)			&lt;br /&gt;
} else if (len &amp;gt; 0) {&lt;br /&gt;
	// Le client à envoyé des données	&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Mode datagramme (UDP) ===&lt;br /&gt;
===Lecture===&lt;br /&gt;
Pour lire dans une socket on peut utiliser &#039;&#039;recvfrom&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t recvfrom(int fd, void *buf, size_t count, int flags, const struct sockaddr *addr, socklen_t addrlen);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères où mettre le message reçu;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à recevoir;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;O_NONBLOCK&#039;&#039;);&lt;br /&gt;
*  addr &amp;amp;rarr; une structure symbolisant l&#039;adresse du client (cf. ci-dessus)&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour correspond au nombre de caractères reçus ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
===Écriture===&lt;br /&gt;
Pour écrire dans une socket ou peut utiliser &#039;&#039;sendto&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t sendto(int fd, const void *buf, size_t count, const struct sockaddr *addr, socklen_t addrlen);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères contenant le message à envoyer;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à envoyer;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;MSG_DONTWAIT &#039;&#039;, ...);&lt;br /&gt;
* addr &amp;amp;rarr; une structure symbolisant l&#039;adresse du client (cf. ci-dessus)&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour correspond au nombre de caractères envoyer ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
= Exemples =&lt;br /&gt;
== Serveur TCP ==&lt;br /&gt;
Voici un exemple de serveur &#039;&#039;echo&#039;&#039; qui renvoie le message au client. Les connexions utilisent TCP et un simple client comme telnet suffit pour l&#039;utiliser.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;arpa/inet.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Port d&#039;écoute de la socket&lt;br /&gt;
#define PORT 8080&lt;br /&gt;
// Adresse d&#039;écoute (toutes les adresses)&lt;br /&gt;
#define IP INADDR_ANY&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
// Nombre de connexions clients&lt;br /&gt;
#define NB_CLIENTS 2&lt;br /&gt;
// Taille du tampon de lecture des messages&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Commande pour arrêter le serveur&lt;br /&gt;
#define EXIT_WORD &amp;quot;exit&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse);&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse);&lt;br /&gt;
int waitForClient(int * serverSocket);&lt;br /&gt;
void addClientToTab(int clientSocket, int clients[]);&lt;br /&gt;
void manageClient(int clients[]);&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
	// Création et initialisation du tableau contenant les descripteurs des sockets clients&lt;br /&gt;
	int clients[NB_CLIENTS];&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_CLIENTS; i++) {&lt;br /&gt;
		clients[i] = -1;&lt;br /&gt;
	}&lt;br /&gt;
	// Structure contenant l&#039;adresse&lt;br /&gt;
	struct sockaddr_in adresse;&lt;br /&gt;
	initAdresse(&amp;amp;adresse);&lt;br /&gt;
	// Descripteur de la socket du serveur&lt;br /&gt;
	int serverSocket = initSocket(&amp;amp;adresse);&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	while (1) {&lt;br /&gt;
		// Descripteur de la socket du client, on attend une connexion&lt;br /&gt;
		if ((clientSocket = waitForClient(&amp;amp;serverSocket)) != -1) {&lt;br /&gt;
			// On ajoute le nouveau client au tableau des descripteurs&lt;br /&gt;
			addClientToTab(clientSocket, clients);&lt;br /&gt;
		}&lt;br /&gt;
		manageClient(clients);&lt;br /&gt;
	}&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
// Initialisation de la structure sockaddr_in&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse) {&lt;br /&gt;
	(*adresse).sin_family = AF_INET;&lt;br /&gt;
	(*adresse).sin_addr.s_addr = IP;&lt;br /&gt;
	(*adresse).sin_port = htons( PORT);&lt;br /&gt;
}&lt;br /&gt;
// Démarrage de la socket serveur&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse) {&lt;br /&gt;
	// Descripteur de socket&lt;br /&gt;
	int fdsocket;&lt;br /&gt;
	// Nombre d&#039;options&lt;br /&gt;
	int opt = 1;&lt;br /&gt;
	// Création de la socket en TCP&lt;br /&gt;
	if ((fdsocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Création de la socket\n&amp;quot;);&lt;br /&gt;
	// Paramètrage de la socket&lt;br /&gt;
	if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,&lt;br /&gt;
			sizeof(opt)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Paramètrage de la socket\n&amp;quot;);&lt;br /&gt;
	// Attachement de la socket sur le port et l&#039;adresse IP&lt;br /&gt;
	if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Attachement de la socket sur le port %i\n&amp;quot;, PORT);&lt;br /&gt;
	// Passage en écoute de la socket&lt;br /&gt;
	if (listen(fdsocket, BACKLOG) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la mise en écoute: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	// Passage en mode non bloquant&lt;br /&gt;
	fcntl(fdsocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
	printf(&amp;quot;Mise en écoute de la socket\n&amp;quot;);&lt;br /&gt;
	return fdsocket;&lt;br /&gt;
}&lt;br /&gt;
// Attente de connexion d&#039;un client&lt;br /&gt;
int waitForClient(int * serverSocket) {&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	// Structure contenant l&#039;adresse du client&lt;br /&gt;
	struct sockaddr_in clientAdresse;&lt;br /&gt;
	int addrLen = sizeof(clientAdresse);&lt;br /&gt;
	if ((clientSocket = accept(*serverSocket, (struct sockaddr*) &amp;amp;clientAdresse,&lt;br /&gt;
			(socklen_t*) &amp;amp;addrLen)) != -1) {&lt;br /&gt;
		// Convertion de l&#039;IP en texte&lt;br /&gt;
		char ip[INET_ADDRSTRLEN];&lt;br /&gt;
		inet_ntop(AF_INET, &amp;amp;(clientAdresse.sin_addr), ip, INET_ADDRSTRLEN);&lt;br /&gt;
		printf(&amp;quot;Connexion de %s:%i\n&amp;quot;, ip, clientAdresse.sin_port);&lt;br /&gt;
		// Passage en mode non bloquant&lt;br /&gt;
		int opt = 1;&lt;br /&gt;
		setsockopt(clientSocket, SOL_SOCKET, SO_KEEPALIVE, &amp;amp;opt, sizeof(1));&lt;br /&gt;
	}&lt;br /&gt;
	return clientSocket;&lt;br /&gt;
}&lt;br /&gt;
// Ajoute les clients au tableau&lt;br /&gt;
void addClientToTab(int clientSocket, int clients[]) {&lt;br /&gt;
	// On vérifie si on à de la place de libre&lt;br /&gt;
	int found = -1;&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_CLIENTS; i++) {&lt;br /&gt;
		// On cherche de la place&lt;br /&gt;
		if (clients[i] == -1) {&lt;br /&gt;
			// On ajoute le client&lt;br /&gt;
			printf(&amp;quot;Ajout du client à l&#039;index %i\n&amp;quot;, i);&lt;br /&gt;
			clients[i] = clientSocket;&lt;br /&gt;
			// Nouvelle connexion, envoie du message de bienvenu&lt;br /&gt;
			send(clientSocket, &amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;, 	strlen(&amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;),&lt;br /&gt;
					MSG_DONTWAIT);&lt;br /&gt;
			found = 0;&lt;br /&gt;
			break;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	if (found == -1) {&lt;br /&gt;
		// On a plus de place de libre&lt;br /&gt;
		puts(&amp;quot;Plus de place, désolé&amp;quot;);&lt;br /&gt;
		close(clientSocket);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// On traite l&#039;input des clients&lt;br /&gt;
void manageClient(int clients[]) {&lt;br /&gt;
	// Création d&#039;un tampon pour stocker les messages des clients dans la heap&lt;br /&gt;
	static char buffer[BUFFER_LEN + 1];&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_CLIENTS; i++) {&lt;br /&gt;
		// On vérifie les messages&lt;br /&gt;
		int clientSocket = clients[i];&lt;br /&gt;
		if (clientSocket == -1) {&lt;br /&gt;
			continue;&lt;br /&gt;
		}&lt;br /&gt;
		int len = recv(clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
		// Booléen pour suivre l&#039;état de la socket&lt;br /&gt;
		int isClosed = 0;&lt;br /&gt;
		if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
			// Une erreur est survenue&lt;br /&gt;
			printf(&amp;quot;Errno [%i] : %s\n&amp;quot;, errno, strerror(errno));&lt;br /&gt;
			isClosed = 1;&lt;br /&gt;
		} else if (len == 0) {&lt;br /&gt;
			// Le client s&#039;est déconnecté (extrémité de la socket fermée)&lt;br /&gt;
			isClosed = 1;&lt;br /&gt;
		} else if (len &amp;gt; 0) {&lt;br /&gt;
			// Ajout du terminateur de chaîne&lt;br /&gt;
			buffer[len] = &#039;\0&#039;;&lt;br /&gt;
			if (strncmp(buffer, EXIT_WORD, 4) == 0) {&lt;br /&gt;
				// Le client veut se déconnecter&lt;br /&gt;
				send(clientSocket, &amp;quot;Bye\n&amp;quot;, strlen(&amp;quot;Bye\n&amp;quot;), 0);&lt;br /&gt;
				isClosed = 1;&lt;br /&gt;
			} else {&lt;br /&gt;
				// On renvoie le texte au client dans un buffer assez grand&lt;br /&gt;
				int len = strlen(&amp;quot;Vous avez dit : &amp;quot;) + strlen(buffer) + 1;&lt;br /&gt;
				char response[len];&lt;br /&gt;
				strcpy(response, &amp;quot;Vous avez dit : &amp;quot;);&lt;br /&gt;
				strcat(response, buffer);&lt;br /&gt;
				// Un seul envoie permet de ne pas surcharger le réseau&lt;br /&gt;
				send(clientSocket, response, strlen(response), 0);&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
		if (isClosed == 1) {&lt;br /&gt;
			// La socket est fermé ou le client veut quitter le serveur !&lt;br /&gt;
			printf(&amp;quot;Fermeture de la connexion avec le client\n&amp;quot;);&lt;br /&gt;
			// Fermeture de la socket&lt;br /&gt;
			close(clientSocket);&lt;br /&gt;
			// On fait de la place dans le tableau&lt;br /&gt;
			clients[i] = -1;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On peut interroger notre serveur avec &#039;&#039;telnet&#039;&#039; (&#039;&#039;yum -y install telnet&#039;&#039;):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# telnet 127.0.0.1 8080&lt;br /&gt;
Trying 127.0.0.1...&lt;br /&gt;
Connected to 127.0.0.1.&lt;br /&gt;
Escape character is &#039;^]&#039;.&lt;br /&gt;
Entrez &#039;exit&#039; pour quitter&lt;br /&gt;
coucou&lt;br /&gt;
Vous avez dit : coucou&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== Serveur UDP ==&lt;br /&gt;
Si on essaie de reproduire le même type de serveur mais cette fois-ci avec une socket en UDP, cela devient beaucoup plus simple. En effet, en UDP, plus besoin de tenir un tableau avec les descripteurs des sockets des clients car... on est en mode déconnecté (datagramme) ! Entendez par là que les clients envoient un message sans attendre de réponse et sans se soucier de savoir si le message est arrivé au serveur.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;arpa/inet.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Port d&#039;écoute de la socket&lt;br /&gt;
#define PORT 8080&lt;br /&gt;
// Adresse d&#039;écoute (toutes les adresses)&lt;br /&gt;
#define IP INADDR_ANY&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
// Taille du tampon de lecture des messages&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse);&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse);&lt;br /&gt;
void manageClient(int serverSocket);&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
	// Structure contenant l&#039;adresse&lt;br /&gt;
	struct sockaddr_in adresse;&lt;br /&gt;
	initAdresse(&amp;amp;adresse);&lt;br /&gt;
	// Descripteur de la socket du serveur&lt;br /&gt;
	int serverSocket = initSocket(&amp;amp;adresse);&lt;br /&gt;
	while (1) {&lt;br /&gt;
		// Descripteur de la socket du client, on attend une connexion&lt;br /&gt;
		manageClient(serverSocket);&lt;br /&gt;
	}&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
// Initialisation de la structure sockaddr_in&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse) {&lt;br /&gt;
	(*adresse).sin_family = AF_INET;&lt;br /&gt;
	(*adresse).sin_addr.s_addr = IP;&lt;br /&gt;
	(*adresse).sin_port = htons( PORT);&lt;br /&gt;
}&lt;br /&gt;
// Démarrage de la socket serveur&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse) {&lt;br /&gt;
	// Descripteur de socket&lt;br /&gt;
	int fdsocket;&lt;br /&gt;
	// Nombre d&#039;options&lt;br /&gt;
	int opt = 1;&lt;br /&gt;
	// Création de la socket en UDP&lt;br /&gt;
	if ((fdsocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Création de la socket\n&amp;quot;);&lt;br /&gt;
	// Paramètrage de la socket&lt;br /&gt;
	if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,&lt;br /&gt;
			sizeof(opt)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Paramètrage de la socket\n&amp;quot;);&lt;br /&gt;
	// Attachement de la socket sur le port et l&#039;adresse IP&lt;br /&gt;
	if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Attachement de la socket sur le port %i\n&amp;quot;, PORT);&lt;br /&gt;
	return fdsocket;&lt;br /&gt;
}&lt;br /&gt;
// On traite l&#039;input des clients&lt;br /&gt;
void manageClient(int serverSocket) {&lt;br /&gt;
	// Création d&#039;un tampon pour stocker les messages des clients dans la heap&lt;br /&gt;
	static char buffer[BUFFER_LEN + 1];&lt;br /&gt;
	// Structure contenant l&#039;adresse du client&lt;br /&gt;
	struct sockaddr_in clientAdresse;&lt;br /&gt;
	unsigned int addrLen = sizeof(clientAdresse);&lt;br /&gt;
	// On vérifie si on a reçu un message&lt;br /&gt;
	int len = recvfrom(serverSocket, buffer, BUFFER_LEN, MSG_DONTWAIT,&lt;br /&gt;
			(struct sockaddr*) &amp;amp;clientAdresse, &amp;amp;addrLen);&lt;br /&gt;
	if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
		printf(&amp;quot;Problème de socket %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	} else if (len &amp;gt; 0) {&lt;br /&gt;
		printf(&amp;quot;Message reçu de %s:%i\n&amp;quot;, inet_ntoa(clientAdresse.sin_addr),&lt;br /&gt;
				ntohs(clientAdresse.sin_port));&lt;br /&gt;
		// Ajout du terminateur de chaîne&lt;br /&gt;
		buffer[len] = &#039;\0&#039;;&lt;br /&gt;
		// On renvoie le texte au client dans un buffer assez grand&lt;br /&gt;
		len = strlen(&amp;quot;Vous avez dit : &amp;quot;) + strlen(buffer) + 1;&lt;br /&gt;
		char response[len];&lt;br /&gt;
		strcpy(response, &amp;quot;Vous avez dit : &amp;quot;);&lt;br /&gt;
		strcat(response, buffer);&lt;br /&gt;
		// Un seul envoie permet de ne pas surcharger le réseau&lt;br /&gt;
		sendto(serverSocket, response, strlen(response), MSG_DONTWAIT,&lt;br /&gt;
				(struct sockaddr*) &amp;amp;clientAdresse, addrLen);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
On peut interroger notre serveur avec &#039;&#039;netcat&#039;&#039; (&#039;&#039;yum -y install nc&#039;&#039;):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# nc -u 127.0.0.1 8080&lt;br /&gt;
coucou&lt;br /&gt;
Vous avez dit : coucou&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Notification évènementielle avec epoll =&lt;br /&gt;
Depuis la version 2.5 du noyau Linux il est possible d&#039;utiliser une série de fonctions, appartenant à l&#039;espace utilisateur (user-space), permettant de suivre l&#039;état d&#039;un descripteur de fichier.&lt;br /&gt;
La différence avec read, recv ou encore recvfrom c&#039;est que l&#039;on va pouvoir surveiller plusieurs descripteurs de fichiers en même temps, laissant le CPU libre pour effectuer autre chose !&lt;br /&gt;
&lt;br /&gt;
== Utilisation ==&lt;br /&gt;
Tout d&#039;abord il faut créer un descripteur de fichier de type &#039;&#039;epoll&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int epoll_create1(int flags);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* flags &amp;amp;rarr; EPOLL_CLOEXEC pour éviter les situations de compétition en environnement parallélisé;&lt;br /&gt;
* le code retour correspond au descripteur &#039;&#039;epoll&#039;&#039; ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée).&lt;br /&gt;
&lt;br /&gt;
Une fois le descripteur &#039;&#039;epoll&#039;&#039; créé, il faut lui associer un événement:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* epfd &amp;amp;rarr; le descripteur &#039;&#039;epoll&#039;&#039; à modifier;&lt;br /&gt;
* op &amp;amp;rarr; une opération parmi:&lt;br /&gt;
** EPOLL_CTL_ADD &amp;amp;rarr; pour ajouter un événement sur un descripteur de fichier;&lt;br /&gt;
** EPOLL_CTL_MOD &amp;amp;rarr; pour modifier un événement sur un descripteur de fichier;&lt;br /&gt;
** EPOLL_CTL_DEL &amp;amp;rarr; pour supprimer un événement sur un descripteur de fichier;&lt;br /&gt;
* fd &amp;amp;rarr; un descripteur de fichier à surveiller;&lt;br /&gt;
* event &amp;amp;rarr; la structure contenant l&#039;événement à surveiller (&#039;&#039;EPOLLIN&#039;&#039;, &#039;&#039;EPOLLOUT&#039;&#039;, EPOLLRDHUP&#039;&#039;, ...);&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; en cas de succès ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée).&lt;br /&gt;
&lt;br /&gt;
Voici la structure &#039;&#039;epoll_event&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
struct epoll_event {&lt;br /&gt;
	uint32_t events;    /* Epoll events */&lt;br /&gt;
	epoll_data_t data;  /* User data variable */&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
typedef union epoll_data {&lt;br /&gt;
	void    *ptr;&lt;br /&gt;
	int      fd;&lt;br /&gt;
	uint32_t u32;&lt;br /&gt;
	uint64_t u64;&lt;br /&gt;
} epoll_data_t;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Enfin, il ne reste plus qu&#039;à &#039;&#039;attendre&#039;&#039; des événements:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* epfd &amp;amp;rarr; le descripteur &#039;&#039;epoll&#039;&#039; à modifier;&lt;br /&gt;
* events &amp;amp;rarr; un tableau de &#039;&#039;epoll_event contenant les événements;&lt;br /&gt;
* maxevents &amp;amp;rarr; le nombre maximum d&#039;événements à traiter (selon la taille du tableau);&lt;br /&gt;
* timeout &amp;amp;rarr; le temps, en millisecondes que la fonction attend:&lt;br /&gt;
** &#039;&#039;-1&#039;&#039; &amp;amp;rarr; la fonction devient bloquante;&lt;br /&gt;
** &#039;&#039;0&#039;&#039; &amp;amp;rarr; retourne immédiatement;&lt;br /&gt;
** &#039;&#039;&amp;gt; 0&#039;&#039; &amp;amp;rarr; attend le nombre de millisecondes spécifiées;&lt;br /&gt;
* le code retour correspond au nombre d&#039;événements ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée).&lt;br /&gt;
&lt;br /&gt;
== Améliorations architecturales ==&lt;br /&gt;
=== Gestion des utilisateurs===&lt;br /&gt;
Tout d&#039;abord, le fait de laisser &#039;&#039;epoll&#039;&#039; gérer les descripteurs de socket nous permet de supprimer le tableau &#039;&#039;clients&#039;&#039; ainsi que la variable &#039;&#039;NB_CLIENTS&#039;&#039;. En effet, la référence vers le descripteur est maintenue par &#039;&#039;epoll&#039;&#039; et nous pouvons gérer maintenant un nombre &#039;&#039;illimité&#039;&#039; de clients.&lt;br /&gt;
&lt;br /&gt;
=== Performances ===&lt;br /&gt;
La boucle &#039;&#039;while&#039;&#039; de notre serveur consomme énormément de CPU, environ 100% de CPU par client ! On aurait pu utiliser un temporisateur avec &#039;&#039;nanosleep&#039;&#039; pour baisser la consommation CPU entre 5% et 10%...&lt;br /&gt;
Le fait de basculer sur une fonction bloquante comme &#039;&#039;epoll_wait&#039;&#039; qui utilise des signaux (interruptions) permet de réduire considérablement la consommation CPU. Gardez à l&#039;esprit qu&#039;il n&#039;y a rien de mieux que les interruptions, la preuve, lorsque l&#039;on fait un &#039;&#039;top&#039;&#039;, notre programme a complètement disparu de la liste !&lt;br /&gt;
&lt;br /&gt;
== Modification du serveur ==&lt;br /&gt;
Modifions le code du serveur TCP pour y intégrer &#039;&#039;epoll&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;arpa/inet.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Port d&#039;écoute de la socket&lt;br /&gt;
#define PORT 8080&lt;br /&gt;
// Adresse d&#039;écoute (toutes les adresses)&lt;br /&gt;
#define IP INADDR_ANY&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
// Nombre de connexions clients&lt;br /&gt;
#define NB_EVENTS 2&lt;br /&gt;
// Taille du tampon de lecture des messages&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Commande pour arrêter le serveur&lt;br /&gt;
#define EXIT_WORD &amp;quot;exit&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse);&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse);&lt;br /&gt;
int waitForClient(int * serverSocket);&lt;br /&gt;
void addClientToTab(int * epfd, int * clientSocket);&lt;br /&gt;
void manageClient(int * epfd, int * clientSocket);&lt;br /&gt;
void addEpollEvent(int * epfd, int * socket);&lt;br /&gt;
void delEpollEvent(int * epfd, int * socket);&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
	// Création du descripteur EPOLL&lt;br /&gt;
	int epfd = epoll_create1(0);&lt;br /&gt;
	// Structure contenant les événements EPOLL&lt;br /&gt;
	struct epoll_event events[NB_EVENTS];&lt;br /&gt;
	// Structure contenant l&#039;adresse&lt;br /&gt;
	struct sockaddr_in adresse;&lt;br /&gt;
	initAdresse(&amp;amp;adresse);&lt;br /&gt;
	// Descripteur de la socket du serveur&lt;br /&gt;
	int serverSocket = initSocket(&amp;amp;adresse);&lt;br /&gt;
	addEpollEvent(&amp;amp;epfd, &amp;amp;serverSocket);&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	while (1) {&lt;br /&gt;
		int nfds = epoll_wait(epfd, events, NB_EVENTS, -1);&lt;br /&gt;
		for (int i = 0; i &amp;lt; nfds; i++) {&lt;br /&gt;
			if (events[i].data.fd == serverSocket) {&lt;br /&gt;
				if ((clientSocket = waitForClient(&amp;amp;serverSocket)) != -1) {&lt;br /&gt;
					// On ajoute le nouveau client au tableau des descripteurs&lt;br /&gt;
					addClientToTab(&amp;amp;epfd, &amp;amp;clientSocket);&lt;br /&gt;
				}&lt;br /&gt;
			} else if (events[i].events &amp;amp; EPOLLIN) {&lt;br /&gt;
				// On a reçu quelque chose&lt;br /&gt;
				manageClient(&amp;amp;epfd, &amp;amp;events[i].data.fd);&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	close(serverSocket);&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
// Initialisation de la structure sockaddr_in&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse) {&lt;br /&gt;
	(*adresse).sin_family = AF_INET;&lt;br /&gt;
	(*adresse).sin_addr.s_addr = IP;&lt;br /&gt;
	(*adresse).sin_port = htons( PORT);&lt;br /&gt;
}&lt;br /&gt;
// Démarrage de la socket serveur&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse) {&lt;br /&gt;
	// Descripteur de socket&lt;br /&gt;
	int fdsocket;&lt;br /&gt;
	// Nombre d&#039;options&lt;br /&gt;
	int opt = 1;&lt;br /&gt;
	// Création de la socket en TCP&lt;br /&gt;
	if ((fdsocket = socket(AF_INET, SOCK_STREAM, 0)) == -1) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Création de la socket\n&amp;quot;);&lt;br /&gt;
	// Paramètrage de la socket&lt;br /&gt;
	if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,&lt;br /&gt;
			sizeof(opt)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Paramètrage de la socket\n&amp;quot;);&lt;br /&gt;
	// Attachement de la socket sur le port et l&#039;adresse IP&lt;br /&gt;
	if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Attachement de la socket sur le port %i\n&amp;quot;, PORT);&lt;br /&gt;
	// Passage en écoute de la socket&lt;br /&gt;
	if (listen(fdsocket, BACKLOG) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la mise en écoute: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	// Passage en mode non bloquant&lt;br /&gt;
	fcntl(fdsocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
	printf(&amp;quot;Mise en écoute de la socket\n&amp;quot;);&lt;br /&gt;
	return fdsocket;&lt;br /&gt;
}&lt;br /&gt;
// Attente de connexion d&#039;un client&lt;br /&gt;
int waitForClient(int * serverSocket) {&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	// Structure contenant l&#039;adresse du client&lt;br /&gt;
	struct sockaddr_in clientAdresse;&lt;br /&gt;
	int addrLen = sizeof(clientAdresse);&lt;br /&gt;
	if ((clientSocket = accept(*serverSocket, (struct sockaddr*) &amp;amp;clientAdresse,&lt;br /&gt;
			(socklen_t*) &amp;amp;addrLen)) != -1) {&lt;br /&gt;
		// Convertion de l&#039;IP en texte&lt;br /&gt;
		char ip[INET_ADDRSTRLEN];&lt;br /&gt;
		inet_ntop(AF_INET, &amp;amp;(clientAdresse.sin_addr), ip, INET_ADDRSTRLEN);&lt;br /&gt;
		printf(&amp;quot;Connexion de %s:%i\n&amp;quot;, ip, clientAdresse.sin_port);&lt;br /&gt;
		// Passage en mode non bloquant&lt;br /&gt;
		int opt = 1;&lt;br /&gt;
		setsockopt(clientSocket, SOL_SOCKET, SO_KEEPALIVE, &amp;amp;opt, sizeof(1));&lt;br /&gt;
	}&lt;br /&gt;
	return clientSocket;&lt;br /&gt;
}&lt;br /&gt;
// Ajoute les clients au tableau&lt;br /&gt;
void addClientToTab(int * epfd, int * clientSocket) {&lt;br /&gt;
	addEpollEvent(epfd, clientSocket);&lt;br /&gt;
	// Nouvelle connexion, envoie du message de bienvenu&lt;br /&gt;
	send(*clientSocket, &amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;,&lt;br /&gt;
			strlen(&amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;), MSG_DONTWAIT);&lt;br /&gt;
}&lt;br /&gt;
// On traite l&#039;input des clients&lt;br /&gt;
void manageClient(int * epfd, int * clientSocket) {&lt;br /&gt;
	// Création d&#039;un tampon pour stocker les messages des clients dans la heap&lt;br /&gt;
	static char buffer[BUFFER_LEN + 1];&lt;br /&gt;
	int len = recv(*clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
	// Booléen pour suivre l&#039;état de la socket&lt;br /&gt;
	int isClosed = 0;&lt;br /&gt;
	if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
		// Une erreur est survenue&lt;br /&gt;
		printf(&amp;quot;Errno [%i] : %s\n&amp;quot;, errno, strerror(errno));&lt;br /&gt;
		isClosed = 1;&lt;br /&gt;
	} else if (len == 0) {&lt;br /&gt;
		// Le client s&#039;est déconnecté (extrémité de la socket fermée)&lt;br /&gt;
		isClosed = 1;&lt;br /&gt;
	} else if (len &amp;gt; 0) {&lt;br /&gt;
		// Ajout du terminateur de chaîne&lt;br /&gt;
		buffer[len] = &#039;\0&#039;;&lt;br /&gt;
		if (strncmp(buffer, EXIT_WORD, 4) == 0) {&lt;br /&gt;
			// Le client veut se déconnecter&lt;br /&gt;
			send(*clientSocket, &amp;quot;Bye\n&amp;quot;, strlen(&amp;quot;Bye\n&amp;quot;), 0);&lt;br /&gt;
			isClosed = 1;&lt;br /&gt;
		} else {&lt;br /&gt;
			// On renvoie le texte au client dans un buffer assez grand&lt;br /&gt;
			int len = strlen(&amp;quot;Vous avez dit : &amp;quot;) + strlen(buffer) + 1;&lt;br /&gt;
			char response[len];&lt;br /&gt;
			strcpy(response, &amp;quot;Vous avez dit : &amp;quot;);&lt;br /&gt;
			strcat(response, buffer);&lt;br /&gt;
			// Un seul envoie permet de ne pas surcharger le réseau&lt;br /&gt;
			send(*clientSocket, response, strlen(response), 0);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	if (isClosed == 1) {&lt;br /&gt;
		// On enlève la socket des événements EPOLL&lt;br /&gt;
		delEpollEvent(epfd, clientSocket);&lt;br /&gt;
		// La socket est fermé ou le client veut quitter le serveur !&lt;br /&gt;
		printf(&amp;quot;Fermeture de la connexion avec le client\n&amp;quot;);&lt;br /&gt;
		// Fermeture de la socket&lt;br /&gt;
		close(*clientSocket);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// Ajoute un descripteur de socket aux événements EPOLL&lt;br /&gt;
void addEpollEvent(int * epfd, int * socket) {&lt;br /&gt;
	printf(&amp;quot;Ajout de la socket %i dans les événements EPOLL\n&amp;quot;, *socket);&lt;br /&gt;
	// Structure contenant les événements EPOLL&lt;br /&gt;
	struct epoll_event event;&lt;br /&gt;
	// On enregistre la socket du serveur comme descripteur EPOLL&lt;br /&gt;
	event.data.fd = *socket;&lt;br /&gt;
	// On surveille les message entrant, la fermeture de la socket&lt;br /&gt;
	event.events = EPOLLIN | EPOLLRDHUP;&lt;br /&gt;
	epoll_ctl(*epfd, EPOLL_CTL_ADD, *socket, &amp;amp;event);&lt;br /&gt;
}&lt;br /&gt;
// Efface un descripteur de socket des événements EPOLL&lt;br /&gt;
void delEpollEvent(int * epfd, int * socket) {&lt;br /&gt;
	printf(&amp;quot;Suppression de la socket %i des événements EPOLL\n&amp;quot;, *socket);&lt;br /&gt;
	// Structure contenant les événements EPOLL&lt;br /&gt;
	struct epoll_event event;&lt;br /&gt;
	// On enregistre la socket du serveur comme descripteur EPOLL&lt;br /&gt;
	event.data.fd = *socket;&lt;br /&gt;
	// On surveille les message entrant, la fermeture de la socket&lt;br /&gt;
	event.events = EPOLLIN | EPOLLRDHUP;&lt;br /&gt;
	epoll_ctl(*epfd, EPOLL_CTL_DEL, *socket, &amp;amp;event);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Voici ce que l&#039;on peut observer dans la console:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Création de la socket&lt;br /&gt;
Paramètrage de la socket&lt;br /&gt;
Attachement de la socket sur le port 8080&lt;br /&gt;
Mise en écoute de la socket&lt;br /&gt;
Ajout de la socket 4 dans les événements EPOLL&lt;br /&gt;
Connexion de 127.0.0.1:17607&lt;br /&gt;
Ajout de la socket 5 dans les événements EPOLL&lt;br /&gt;
Suppression de la socket 5 des événements EPOLL&lt;br /&gt;
Fermeture de la connexion avec le client&lt;br /&gt;
Connexion de 127.0.0.1:18119&lt;br /&gt;
Ajout de la socket 5 dans les événements EPOLL&lt;br /&gt;
Connexion de 127.0.0.1:18631&lt;br /&gt;
Ajout de la socket 6 dans les événements EPOLL&lt;br /&gt;
Suppression de la socket 5 des événements EPOLL&lt;br /&gt;
Fermeture de la connexion avec le client&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Magsss</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=C_socket&amp;diff=3287</id>
		<title>C socket</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=C_socket&amp;diff=3287"/>
		<updated>2018-11-01T18:30:51Z</updated>

		<summary type="html">&lt;p&gt;Magsss : /* Gestion des utilisateurs */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
||&lt;br /&gt;
Les sockets permettent de connecter deux programmes qui s&#039;exécutent sur deux machines différentes. Cette connexion se fait à travers le réseau grâce à l&#039;utilisation d&#039;un port (TCP ou UDP) et d&#039;une adresse IP. &lt;br /&gt;
&lt;br /&gt;
Il y a deux types de sockets, une qui est démarrée par la partie serveur en &#039;&#039;écoute&#039;&#039; et l&#039;autre démarrée par la partie cliente qui se connecte à la première.&lt;br /&gt;
&lt;br /&gt;
Ci-contre une image résumant les différentes étapes pour arriver à l&#039;envoi de données.&lt;br /&gt;
||&lt;br /&gt;
[[Fichier:Socket workflow.png|centré|200px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Création  =&lt;br /&gt;
== Côté serveur ==&lt;br /&gt;
Tout d&#039;abord il faut créer l&#039;objet socket:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt; &lt;br /&gt;
&lt;br /&gt;
int socket(int domain, int type, int protocol)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*domain &amp;amp;rarr; integer, communication domain e.g., AF_INET (IPv4 protocol) , AF_INET6 (IPv6 protocol)&lt;br /&gt;
*type &amp;amp;rarr; type de communication&lt;br /&gt;
**SOCK_STREAM: TCP&lt;br /&gt;
**SOCK_DGRAM: UDP&lt;br /&gt;
**SOCK_RAW: socket à l&#039;état brut (bas niveau)&lt;br /&gt;
*protocol &amp;amp;rarr; valeur du champ &#039;&#039;protocol&#039;&#039; de l&#039;en-tête de niveau 3 (généralement &#039;&#039;0&#039;&#039;)&lt;br /&gt;
*la valeur de retour est le fichier descripteur de la socket ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Une fois le descripteur de socket créé, il est possible de le configurer:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt; &lt;br /&gt;
&lt;br /&gt;
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* level &amp;amp;rarr; niveau d&#039;application de l&#039;option (SOL_SOCKET, TCP, UDP, ...);&lt;br /&gt;
* optname &amp;amp;rarr; le nom de l&#039;option (&#039;&#039;SO_REUSEADDR&#039;&#039;, &#039;&#039;SO_REUSEPORT&#039;&#039;, &#039;&#039;SO_KEEPALIVE&#039;&#039;, ...)&lt;br /&gt;
* optval, optlen  &amp;amp;rarr; utilisé pour accéder aux options de la socket;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
Il existe plusieurs options de socket, les plus utilisées étant :&lt;br /&gt;
* &#039;&#039;SO_REUSEADDR&#039;&#039; &amp;amp;rarr; permet de réutiliser l&#039;adresse de la socket tout de suite même si cette dernière est dans l&#039;état &#039;&#039;wait&#039;&#039;;&lt;br /&gt;
* &#039;&#039;SO_REUSEPORT&#039;&#039; &amp;amp;rarr; permet de réutiliser le port de la socket tout de suite même si cette dernière est dans l&#039;état &#039;&#039;wait&#039;&#039;;&lt;br /&gt;
* &#039;&#039;SO_KEEPALIVE&#039;&#039; &amp;amp;rarr; envoie des informations périodiquement pour tester si l&#039;extrémité du tunnel est toujours présente;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Il faut maintenant attacher la socket à un port Internet et une adresse IP:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* addr &amp;amp;rarr; une structure symbolisant l&#039;adresse &lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
struct sockaddr_in {&lt;br /&gt;
    short            sin_family;   // e.g: AF_INET&lt;br /&gt;
    unsigned short   sin_port;     // e.g: htons(3490)&lt;br /&gt;
    struct in_addr   sin_addr;     // détaillé ci-dessous&lt;br /&gt;
    char             sin_zero[8];  // &#039;0&#039; habituellement&lt;br /&gt;
};&lt;br /&gt;
struct in_addr {&lt;br /&gt;
    unsigned long s_addr;  // initialiser avec inet_aton()&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On peut maintenant passer la socket en état d&#039;écoute, elle est prête à recevoir des connexions:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int listen(int sockfd, int backlog)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* backlog &amp;amp;rarr; taille maximum de la file d&#039;attente de la socket après laquelle le système répond avec &#039;&#039;ECONNREFUSED&#039;&#039;;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On peut maintenant attendre une connexion:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* addr &amp;amp;rarr; l&#039;adresse du client;&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de la structure addr;&lt;br /&gt;
* le code retour varie entre un entier positif qui correspond au descripteur de la socket cliente et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
== Côté client ==&lt;br /&gt;
Après avoir créé la socket avec &#039;&#039;socket&#039;&#039; on peut se connecter à la partie serveur:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* addr &amp;amp;rarr; une structure symbolisant l&#039;adresse (cf. ci-dessus)&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
= Lecture / écriture =&lt;br /&gt;
== Fonctions génériques ==&lt;br /&gt;
Tout d&#039;abord, on peut dire que la socket est un fichier et se manipule donc comme un fichier !&lt;br /&gt;
===Lecture===&lt;br /&gt;
Pour lire dans une socket on peut utiliser &#039;&#039;read&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t read(int fd, void *buf, size_t count);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères où mettre le message reçu;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à recevoir;&lt;br /&gt;
* le code retour correspond au nombre de caractères reçus ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
===Écriture===&lt;br /&gt;
Pour écrire dans une socket ou peut utiliser &#039;&#039;write&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t write(int fd, const void *buf, size_t count);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères contenant le message à envoyer;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à envoyer;&lt;br /&gt;
* le code retour correspond au nombre de caractères envoyés ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
===Modification===&lt;br /&gt;
Pour modifier un descripteur fichier de socket on utilise &#039;&#039;fcntl&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int fcntl(int fd, int cmd, ... /* int args */ );&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* cmd &amp;amp;rarr; la commande de modification (descripteur, drapeaux, ...);&lt;br /&gt;
* args &amp;amp;rarr; les arguments correspondant à la commande&lt;br /&gt;
&lt;br /&gt;
Cette commande va nous permettre de modifier le drapeau du descripteur de la socket (&#039;&#039;F_SETFL&#039;&#039;) pour le rendre non bloquant (&#039;&#039;O_NONBLOCK&#039;&#039;) !&lt;br /&gt;
&lt;br /&gt;
== Fonctions spécifiques ==&lt;br /&gt;
Il existe des fonctions spécifiques pour manipuler les sockets. Ces fonctions ressemblent aux précédentes mais acceptent des drapeaux en plus pour pouvoir modifier le descripteur &#039;&#039;à la volée&#039;&#039;.&lt;br /&gt;
===Lecture===&lt;br /&gt;
Pour lire dans une socket on peut utiliser &#039;&#039;recv&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t recv(int fd, void *buf, size_t count, int flags);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères où mettre le message reçu;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à recevoir;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;O_NONBLOCK&#039;&#039;);&lt;br /&gt;
* le code retour correspond au nombre de caractères reçus ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
===Écriture===&lt;br /&gt;
Pour écrire dans une socket ou peut utiliser &#039;&#039;send&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t send(int fd, const void *buf, size_t count, int flags);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères contenant le message à envoyer;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à envoyer;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;MSG_CONFIRM&#039;&#039;, &#039;&#039;MSG_DONTWAIT &#039;&#039;, ...);&lt;br /&gt;
* le code retour correspond au nombre de caractères envoyés ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
= Cas d&#039;utilisation =&lt;br /&gt;
== Création ==&lt;br /&gt;
Pour commencer, il faut créer le descripteur de fichier:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int fdsocket;&lt;br /&gt;
if ((fdsocket = socket(AF_INET, SOCK_STREAM, 0)) == -1) {&lt;br /&gt;
	printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Paramétrage des options ==&lt;br /&gt;
Il faut maintenant configurer la réutilisation de l&#039;adresse et du port&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int opt = 1;&lt;br /&gt;
if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,	sizeof(opt)) != 0) {&lt;br /&gt;
	printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Attachement ==&lt;br /&gt;
On peut maintenant attacher la socket à un port et une adresse:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
struct sockaddr_in adresse;&lt;br /&gt;
&lt;br /&gt;
adresse.sin_family = AF_INET;&lt;br /&gt;
// Ecoute sur toutes les adresses (INADDR_ANY &amp;lt;=&amp;gt; 0.0.0.0)&lt;br /&gt;
adresse.sin_addr.s_addr = INADDR_ANY;&lt;br /&gt;
// Conversion du port en valeur réseaux (Host TO Network Short)&lt;br /&gt;
adresse.sin_port = htons(8080);&lt;br /&gt;
&lt;br /&gt;
if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
	printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mise en écoute ==&lt;br /&gt;
La socket est prête à passer à l&#039;écoute des connexions des clients:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
&lt;br /&gt;
if (listen(fdsocket, BACKLOG) != 0) {&lt;br /&gt;
	printf(&amp;quot;Echéc de la mise en écoute: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Acceptation des connexions ==&lt;br /&gt;
Il est possible d&#039;accepter les nouvelles connexions:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int clientSocket;&lt;br /&gt;
// Structure contenant l&#039;adresse du client&lt;br /&gt;
struct sockaddr_in clientAdresse;&lt;br /&gt;
int addrLen = sizeof(clientAdresse);&lt;br /&gt;
if ((clientSocket = accept(*serverSocket, (struct sockaddr*) &amp;amp;clientAdresse, (socklen_t*) &amp;amp;addrLen)) != -1) {&lt;br /&gt;
	// Convertion de l&#039;IP en texte&lt;br /&gt;
	char ip[INET_ADDRSTRLEN];&lt;br /&gt;
	inet_ntop(AF_INET, &amp;amp;(clientAdresse.sin_addr), ip, INET_ADDRSTRLEN);&lt;br /&gt;
	printf(&amp;quot;Connexion de %s:%i\n&amp;quot;, ip, clientAdresse.sin_port);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Il est possible d&#039;utiliser &#039;&#039;fcntl&#039;&#039; pour passer le descripteur de la socket en non bloquant, avant l&#039;appel à la fonction &#039;&#039;accept&#039;&#039;. Si le descripteur reste bloquant, la fonction &#039;&#039;accept&#039;&#039; ne rendra pas la main tant qu&#039;un client ne se sera connecté, ayant pour résultat le blocage complet du programme !&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Passage en mode non bloquant&lt;br /&gt;
fcntl(fdsocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
On peut remarquer, au passage, l&#039;utilisation de &#039;&#039;inet_ntop&#039;&#039; pour convertir l&#039;adresse du client du format binaire au format text.&lt;br /&gt;
&lt;br /&gt;
== Lecture / Écriture ==&lt;br /&gt;
=== Mode connecté (TCP) ===&lt;br /&gt;
On peut maintenant utiliser le descripteur de la socket du client pour lire et écrire.&lt;br /&gt;
* avec les fonctions génériques:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Descripteur de la socket du client&lt;br /&gt;
int clientSocket;&lt;br /&gt;
char buffer[BUFFER_LEN];&lt;br /&gt;
// Passage en mode non bloquant, sinon read attend&lt;br /&gt;
fcntl(clientSocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
int len = read(clientSocket, buffer, BUFFER_LEN);&lt;br /&gt;
&lt;br /&gt;
send(clientSocket, &amp;quot;Coucou\n&amp;quot;, strlen(&amp;quot;Coucou\n&amp;quot;));&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* avec les fonctions spécifiques:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Descripteur de la socket du client&lt;br /&gt;
int clientSocket;&lt;br /&gt;
char buffer[BUFFER_LEN];&lt;br /&gt;
&lt;br /&gt;
int len = recv(clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
&lt;br /&gt;
send(clientSocket, &amp;quot;Coucou\n&amp;quot;, strlen(&amp;quot;Coucou\n&amp;quot;), MSG_DONTWAIT);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Analysons le retour de la fonction &#039;&#039;read&#039;&#039; ou &#039;&#039;recv&#039;&#039; lorsque l&#039;on utilise un descripteur non bloquant d&#039;après la [http://man7.org/linux/man-pages/man2/recvmsg.2.html documentation] :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
NAME &lt;br /&gt;
       recv, recvfrom, recvmsg - receive a message from a socket&lt;br /&gt;
[...]&lt;br /&gt;
MSG_DONTWAIT (since Linux 2.2)&lt;br /&gt;
    Enables nonblocking operation; if the operation would block, the call fails with the error EAGAIN or EWOULDBLOCK (this can also be enabled using the O_NONBLOCK flag with the F_SETFL fcntl(2)). &lt;br /&gt;
[...]&lt;br /&gt;
RETURN VALUE&lt;br /&gt;
[...]&lt;br /&gt;
These calls return the number of bytes received, or -1 if an error&lt;br /&gt;
       occurred.  In the event of an error, errno is set to indicate the&lt;br /&gt;
       error.&lt;br /&gt;
When a stream socket peer has performed an orderly shutdown, the&lt;br /&gt;
       return value will be 0 (the traditional &amp;quot;end-of-file&amp;quot; return).&lt;br /&gt;
[...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Le retour est :&lt;br /&gt;
* lorsque le descripteur est vide (aucune donnée de la part du client) &#039;&#039;-1&#039;&#039; et &#039;&#039;errno&#039;&#039; sera positionné à la valeur &#039;&#039;EAGAIN&#039;&#039;, il ne faut donc pas fermer la socket si le retour est &#039;&#039;-1&#039;&#039; ! Il faut d&#039;abord vérifier que &#039;&#039;errno&#039;&#039; &#039;&#039;&#039;n&#039;a pas&#039;&#039;&#039; la valeur &#039;&#039;EAGAIN&#039;&#039;;&lt;br /&gt;
* lorsque le retour est &#039;&#039;0&#039;&#039;, cela signifie que l&#039;extrémité est fermée, il faut donc fermer le descripteur local;&lt;br /&gt;
* lorsque la valeur est supérieure à &#039;&#039;0&#039;&#039; elle correspond au nombre d&#039;octets reçus.&lt;br /&gt;
&lt;br /&gt;
On peut donc en déduire le morceau de code suivant pour gérer le retour de &#039;&#039;read&#039;&#039; ou &#039;&#039;recv&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int len = recv(clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
&lt;br /&gt;
if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
	// Une erreur est survenue&lt;br /&gt;
} else if (len == 0) {&lt;br /&gt;
	// Le client s&#039;est déconnecté (extrémité de la socket fermée)			&lt;br /&gt;
} else if (len &amp;gt; 0) {&lt;br /&gt;
	// Le client à envoyé des données	&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Mode datagramme (UDP) ===&lt;br /&gt;
===Lecture===&lt;br /&gt;
Pour lire dans une socket on peut utiliser &#039;&#039;recvfrom&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t recvfrom(int fd, void *buf, size_t count, int flags, const struct sockaddr *addr, socklen_t addrlen);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères où mettre le message reçu;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à recevoir;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;O_NONBLOCK&#039;&#039;);&lt;br /&gt;
*  addr &amp;amp;rarr; une structure symbolisant l&#039;adresse du client (cf. ci-dessus)&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour correspond au nombre de caractères reçus ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
===Écriture===&lt;br /&gt;
Pour écrire dans une socket ou peut utiliser &#039;&#039;sendto&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t sendto(int fd, const void *buf, size_t count, const struct sockaddr *addr, socklen_t addrlen);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères contenant le message à envoyer;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à envoyer;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;MSG_DONTWAIT &#039;&#039;, ...);&lt;br /&gt;
* addr &amp;amp;rarr; une structure symbolisant l&#039;adresse du client (cf. ci-dessus)&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour correspond au nombre de caractères envoyer ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
= Exemples =&lt;br /&gt;
== Serveur TCP ==&lt;br /&gt;
Voici un exemple de serveur &#039;&#039;echo&#039;&#039; qui renvoie le message au client. Les connexions utilisent TCP et un simple client comme telnet suffit pour l&#039;utiliser.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;arpa/inet.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Port d&#039;écoute de la socket&lt;br /&gt;
#define PORT 8080&lt;br /&gt;
// Adresse d&#039;écoute (toutes les adresses)&lt;br /&gt;
#define IP INADDR_ANY&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
// Nombre de connexions clients&lt;br /&gt;
#define NB_CLIENTS 2&lt;br /&gt;
// Taille du tampon de lecture des messages&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Commande pour arrêter le serveur&lt;br /&gt;
#define EXIT_WORD &amp;quot;exit&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse);&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse);&lt;br /&gt;
int waitForClient(int * serverSocket);&lt;br /&gt;
void addClientToTab(int clientSocket, int clients[]);&lt;br /&gt;
void manageClient(int clients[]);&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
	// Création et initialisation du tableau contenant les descripteurs des sockets clients&lt;br /&gt;
	int clients[NB_CLIENTS];&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_CLIENTS; i++) {&lt;br /&gt;
		clients[i] = -1;&lt;br /&gt;
	}&lt;br /&gt;
	// Structure contenant l&#039;adresse&lt;br /&gt;
	struct sockaddr_in adresse;&lt;br /&gt;
	initAdresse(&amp;amp;adresse);&lt;br /&gt;
	// Descripteur de la socket du serveur&lt;br /&gt;
	int serverSocket = initSocket(&amp;amp;adresse);&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	while (1) {&lt;br /&gt;
		// Descripteur de la socket du client, on attend une connexion&lt;br /&gt;
		if ((clientSocket = waitForClient(&amp;amp;serverSocket)) != -1) {&lt;br /&gt;
			// On ajoute le nouveau client au tableau des descripteurs&lt;br /&gt;
			addClientToTab(clientSocket, clients);&lt;br /&gt;
		}&lt;br /&gt;
		manageClient(clients);&lt;br /&gt;
	}&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
// Initialisation de la structure sockaddr_in&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse) {&lt;br /&gt;
	(*adresse).sin_family = AF_INET;&lt;br /&gt;
	(*adresse).sin_addr.s_addr = IP;&lt;br /&gt;
	(*adresse).sin_port = htons( PORT);&lt;br /&gt;
}&lt;br /&gt;
// Démarrage de la socket serveur&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse) {&lt;br /&gt;
	// Descripteur de socket&lt;br /&gt;
	int fdsocket;&lt;br /&gt;
	// Nombre d&#039;options&lt;br /&gt;
	int opt = 1;&lt;br /&gt;
	// Création de la socket en TCP&lt;br /&gt;
	if ((fdsocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Création de la socket\n&amp;quot;);&lt;br /&gt;
	// Paramètrage de la socket&lt;br /&gt;
	if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,&lt;br /&gt;
			sizeof(opt)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Paramètrage de la socket\n&amp;quot;);&lt;br /&gt;
	// Attachement de la socket sur le port et l&#039;adresse IP&lt;br /&gt;
	if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Attachement de la socket sur le port %i\n&amp;quot;, PORT);&lt;br /&gt;
	// Passage en écoute de la socket&lt;br /&gt;
	if (listen(fdsocket, BACKLOG) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la mise en écoute: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	// Passage en mode non bloquant&lt;br /&gt;
	fcntl(fdsocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
	printf(&amp;quot;Mise en écoute de la socket\n&amp;quot;);&lt;br /&gt;
	return fdsocket;&lt;br /&gt;
}&lt;br /&gt;
// Attente de connexion d&#039;un client&lt;br /&gt;
int waitForClient(int * serverSocket) {&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	// Structure contenant l&#039;adresse du client&lt;br /&gt;
	struct sockaddr_in clientAdresse;&lt;br /&gt;
	int addrLen = sizeof(clientAdresse);&lt;br /&gt;
	if ((clientSocket = accept(*serverSocket, (struct sockaddr*) &amp;amp;clientAdresse,&lt;br /&gt;
			(socklen_t*) &amp;amp;addrLen)) != -1) {&lt;br /&gt;
		// Convertion de l&#039;IP en texte&lt;br /&gt;
		char ip[INET_ADDRSTRLEN];&lt;br /&gt;
		inet_ntop(AF_INET, &amp;amp;(clientAdresse.sin_addr), ip, INET_ADDRSTRLEN);&lt;br /&gt;
		printf(&amp;quot;Connexion de %s:%i\n&amp;quot;, ip, clientAdresse.sin_port);&lt;br /&gt;
		// Passage en mode non bloquant&lt;br /&gt;
		int opt = 1;&lt;br /&gt;
		setsockopt(clientSocket, SOL_SOCKET, SO_KEEPALIVE, &amp;amp;opt, sizeof(1));&lt;br /&gt;
	}&lt;br /&gt;
	return clientSocket;&lt;br /&gt;
}&lt;br /&gt;
// Ajoute les clients au tableau&lt;br /&gt;
void addClientToTab(int clientSocket, int clients[]) {&lt;br /&gt;
	// On vérifie si on à de la place de libre&lt;br /&gt;
	int found = -1;&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_CLIENTS; i++) {&lt;br /&gt;
		// On cherche de la place&lt;br /&gt;
		if (clients[i] == -1) {&lt;br /&gt;
			// On ajoute le client&lt;br /&gt;
			printf(&amp;quot;Ajout du client à l&#039;index %i\n&amp;quot;, i);&lt;br /&gt;
			clients[i] = clientSocket;&lt;br /&gt;
			// Nouvelle connexion, envoie du message de bienvenu&lt;br /&gt;
			send(clientSocket, &amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;, 	strlen(&amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;),&lt;br /&gt;
					MSG_DONTWAIT);&lt;br /&gt;
			found = 0;&lt;br /&gt;
			break;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	if (found == -1) {&lt;br /&gt;
		// On a plus de place de libre&lt;br /&gt;
		puts(&amp;quot;Plus de place, désolé&amp;quot;);&lt;br /&gt;
		close(clientSocket);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// On traite l&#039;input des clients&lt;br /&gt;
void manageClient(int clients[]) {&lt;br /&gt;
	// Création d&#039;un tampon pour stocker les messages des clients dans la heap&lt;br /&gt;
	static char buffer[BUFFER_LEN + 1];&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_CLIENTS; i++) {&lt;br /&gt;
		// On vérifie les messages&lt;br /&gt;
		int clientSocket = clients[i];&lt;br /&gt;
		if (clientSocket == -1) {&lt;br /&gt;
			continue;&lt;br /&gt;
		}&lt;br /&gt;
		int len = recv(clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
		// Booléen pour suivre l&#039;état de la socket&lt;br /&gt;
		int isClosed = 0;&lt;br /&gt;
		if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
			// Une erreur est survenue&lt;br /&gt;
			printf(&amp;quot;Errno [%i] : %s\n&amp;quot;, errno, strerror(errno));&lt;br /&gt;
			isClosed = 1;&lt;br /&gt;
		} else if (len == 0) {&lt;br /&gt;
			// Le client s&#039;est déconnecté (extrémité de la socket fermée)&lt;br /&gt;
			isClosed = 1;&lt;br /&gt;
		} else if (len &amp;gt; 0) {&lt;br /&gt;
			// Ajout du terminateur de chaîne&lt;br /&gt;
			buffer[len] = &#039;\0&#039;;&lt;br /&gt;
			if (strncmp(buffer, EXIT_WORD, 4) == 0) {&lt;br /&gt;
				// Le client veut se déconnecter&lt;br /&gt;
				send(clientSocket, &amp;quot;Bye\n&amp;quot;, strlen(&amp;quot;Bye\n&amp;quot;), 0);&lt;br /&gt;
				isClosed = 1;&lt;br /&gt;
			} else {&lt;br /&gt;
				// On renvoie le texte au client dans un buffer assez grand&lt;br /&gt;
				int len = strlen(&amp;quot;Vous avez dit : &amp;quot;) + strlen(buffer) + 1;&lt;br /&gt;
				char response[len];&lt;br /&gt;
				strcpy(response, &amp;quot;Vous avez dit : &amp;quot;);&lt;br /&gt;
				strcat(response, buffer);&lt;br /&gt;
				// Un seul envoie permet de ne pas surcharger le réseau&lt;br /&gt;
				send(clientSocket, response, strlen(response), 0);&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
		if (isClosed == 1) {&lt;br /&gt;
			// La socket est fermé ou le client veut quitter le serveur !&lt;br /&gt;
			printf(&amp;quot;Fermeture de la connexion avec le client\n&amp;quot;);&lt;br /&gt;
			// Fermeture de la socket&lt;br /&gt;
			close(clientSocket);&lt;br /&gt;
			// On fait de la place dans le tableau&lt;br /&gt;
			clients[i] = -1;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On peut interroger notre serveur avec &#039;&#039;telnet&#039;&#039; (&#039;&#039;yum -y install telnet&#039;&#039;):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# telnet 127.0.0.1 8080&lt;br /&gt;
Trying 127.0.0.1...&lt;br /&gt;
Connected to 127.0.0.1.&lt;br /&gt;
Escape character is &#039;^]&#039;.&lt;br /&gt;
Entrez &#039;exit&#039; pour quitter&lt;br /&gt;
coucou&lt;br /&gt;
Vous avez dit : coucou&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== Serveur UDP ==&lt;br /&gt;
Si on essaie de reproduire le même type de serveur mais cette fois-ci avec une socket en UDP, cela devient beaucoup plus simple. En effet, en UDP, plus besoin de tenir un tableau avec les descripteurs des sockets des clients car... on est en mode déconnecté (datagramme) ! Entendez par là que les clients envoient un message sans attendre de réponse et sans se soucier de savoir si le message est arrivé au serveur.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;arpa/inet.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Port d&#039;écoute de la socket&lt;br /&gt;
#define PORT 8080&lt;br /&gt;
// Adresse d&#039;écoute (toutes les adresses)&lt;br /&gt;
#define IP INADDR_ANY&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
// Taille du tampon de lecture des messages&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse);&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse);&lt;br /&gt;
void manageClient(int serverSocket);&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
	// Structure contenant l&#039;adresse&lt;br /&gt;
	struct sockaddr_in adresse;&lt;br /&gt;
	initAdresse(&amp;amp;adresse);&lt;br /&gt;
	// Descripteur de la socket du serveur&lt;br /&gt;
	int serverSocket = initSocket(&amp;amp;adresse);&lt;br /&gt;
	while (1) {&lt;br /&gt;
		// Descripteur de la socket du client, on attend une connexion&lt;br /&gt;
		manageClient(serverSocket);&lt;br /&gt;
	}&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
// Initialisation de la structure sockaddr_in&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse) {&lt;br /&gt;
	(*adresse).sin_family = AF_INET;&lt;br /&gt;
	(*adresse).sin_addr.s_addr = IP;&lt;br /&gt;
	(*adresse).sin_port = htons( PORT);&lt;br /&gt;
}&lt;br /&gt;
// Démarrage de la socket serveur&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse) {&lt;br /&gt;
	// Descripteur de socket&lt;br /&gt;
	int fdsocket;&lt;br /&gt;
	// Nombre d&#039;options&lt;br /&gt;
	int opt = 1;&lt;br /&gt;
	// Création de la socket en UDP&lt;br /&gt;
	if ((fdsocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Création de la socket\n&amp;quot;);&lt;br /&gt;
	// Paramètrage de la socket&lt;br /&gt;
	if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,&lt;br /&gt;
			sizeof(opt)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Paramètrage de la socket\n&amp;quot;);&lt;br /&gt;
	// Attachement de la socket sur le port et l&#039;adresse IP&lt;br /&gt;
	if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Attachement de la socket sur le port %i\n&amp;quot;, PORT);&lt;br /&gt;
	return fdsocket;&lt;br /&gt;
}&lt;br /&gt;
// On traite l&#039;input des clients&lt;br /&gt;
void manageClient(int serverSocket) {&lt;br /&gt;
	// Création d&#039;un tampon pour stocker les messages des clients dans la heap&lt;br /&gt;
	static char buffer[BUFFER_LEN + 1];&lt;br /&gt;
	// Structure contenant l&#039;adresse du client&lt;br /&gt;
	struct sockaddr_in clientAdresse;&lt;br /&gt;
	unsigned int addrLen = sizeof(clientAdresse);&lt;br /&gt;
	// On vérifie si on a reçu un message&lt;br /&gt;
	int len = recvfrom(serverSocket, buffer, BUFFER_LEN, MSG_DONTWAIT,&lt;br /&gt;
			(struct sockaddr*) &amp;amp;clientAdresse, &amp;amp;addrLen);&lt;br /&gt;
	if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
		printf(&amp;quot;Problème de socket %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	} else if (len &amp;gt; 0) {&lt;br /&gt;
		printf(&amp;quot;Message reçu de %s:%i\n&amp;quot;, inet_ntoa(clientAdresse.sin_addr),&lt;br /&gt;
				ntohs(clientAdresse.sin_port));&lt;br /&gt;
		// Ajout du terminateur de chaîne&lt;br /&gt;
		buffer[len] = &#039;\0&#039;;&lt;br /&gt;
		// On renvoie le texte au client dans un buffer assez grand&lt;br /&gt;
		len = strlen(&amp;quot;Vous avez dit : &amp;quot;) + strlen(buffer) + 1;&lt;br /&gt;
		char response[len];&lt;br /&gt;
		strcpy(response, &amp;quot;Vous avez dit : &amp;quot;);&lt;br /&gt;
		strcat(response, buffer);&lt;br /&gt;
		// Un seul envoie permet de ne pas surcharger le réseau&lt;br /&gt;
		sendto(serverSocket, response, strlen(response), MSG_DONTWAIT,&lt;br /&gt;
				(struct sockaddr*) &amp;amp;clientAdresse, addrLen);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
On peut interroger notre serveur avec &#039;&#039;netcat&#039;&#039; (&#039;&#039;yum -y install nc&#039;&#039;):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# nc -u 127.0.0.1 8080&lt;br /&gt;
coucou&lt;br /&gt;
Vous avez dit : coucou&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Notification évènementielle avec epoll =&lt;br /&gt;
Depuis la version 2.5 du noyau Linux il est possible d&#039;utiliser une série de fonctions, appartenant à l&#039;espace utilisateur (user-space), permettant de suivre l&#039;état d&#039;un descripteur de fichier.&lt;br /&gt;
La différence avec read, recv ou encore recvfrom c&#039;est que l&#039;on va pouvoir surveiller plusieurs descripteurs de fichiers en même temps, laissant le CPU libre pour effectuer autre chose !&lt;br /&gt;
&lt;br /&gt;
== Utilisation ==&lt;br /&gt;
Tout d&#039;abord il faut créer un descripteur de fichier de type &#039;&#039;epoll&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int epoll_create1(int flags);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* flags &amp;amp;rarr; EPOLL_CLOEXEC pour éviter les situations de compétition en environnement parallélisé;&lt;br /&gt;
* le code retour correspond au descripteur &#039;&#039;epoll&#039;&#039; ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée).&lt;br /&gt;
&lt;br /&gt;
Une fois le descripteur &#039;&#039;epoll&#039;&#039; créé, il faut lui associer un événement:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* epfd &amp;amp;rarr; le descripteur &#039;&#039;epoll&#039;&#039; à modifier;&lt;br /&gt;
* op &amp;amp;rarr; une opération parmi:&lt;br /&gt;
** EPOLL_CTL_ADD &amp;amp;rarr; pour ajouter un événement sur un descripteur de fichier;&lt;br /&gt;
** EPOLL_CTL_MOD &amp;amp;rarr; pour modifier un événement sur un descripteur de fichier;&lt;br /&gt;
** EPOLL_CTL_DEL &amp;amp;rarr; pour supprimer un événement sur un descripteur de fichier;&lt;br /&gt;
* fd &amp;amp;rarr; un descripteur de fichier à surveiller;&lt;br /&gt;
* event &amp;amp;rarr; la structure contenant l&#039;événement à surveiller (&#039;&#039;EPOLLIN&#039;&#039;, &#039;&#039;EPOLLOUT&#039;&#039;, EPOLLRDHUP&#039;&#039;, ...);&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; en cas de succès ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée).&lt;br /&gt;
&lt;br /&gt;
Voici la structure &#039;&#039;epoll_event&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
struct epoll_event {&lt;br /&gt;
	uint32_t events;    /* Epoll events */&lt;br /&gt;
	epoll_data_t data;  /* User data variable */&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
typedef union epoll_data {&lt;br /&gt;
	void    *ptr;&lt;br /&gt;
	int      fd;&lt;br /&gt;
	uint32_t u32;&lt;br /&gt;
	uint64_t u64;&lt;br /&gt;
} epoll_data_t;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Enfin, il ne reste plus qu&#039;à &#039;&#039;attendre&#039;&#039; des événements:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* epfd &amp;amp;rarr; le descripteur &#039;&#039;epoll&#039;&#039; à modifier;&lt;br /&gt;
* events &amp;amp;rarr; un tableau de &#039;&#039;epoll_event contenant les événements;&lt;br /&gt;
* maxevents &amp;amp;rarr; le nombre maximum d&#039;événements à traiter (selon la taille du tableau);&lt;br /&gt;
* timeout &amp;amp;rarr; le temps, en millisecondes que la fonction attend:&lt;br /&gt;
** &#039;&#039;-1&#039;&#039; &amp;amp;rarr; la fonction devient bloquante;&lt;br /&gt;
** &#039;&#039;0&#039;&#039; &amp;amp;rarr; retourne immédiatement;&lt;br /&gt;
** &#039;&#039;&amp;gt; 0&#039;&#039; &amp;amp;rarr; attend le nombre de millisecondes spécifiées;&lt;br /&gt;
* le code retour correspond au nombre d&#039;événements ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée).&lt;br /&gt;
&lt;br /&gt;
== Améliorations architecturales ==&lt;br /&gt;
=== Gestion des utilisateurs===&lt;br /&gt;
Tout d&#039;abord, le fait de laisser &#039;&#039;epoll&#039;&#039; gérer les descripteurs de socket nous permet de supprimer le tableau &#039;&#039;clients&#039;&#039; ainsi que la variable &#039;&#039;NB_CLIENTS&#039;&#039;. En effet, la référence vers le descripteur est maintenue par &#039;&#039;epoll&#039;&#039; et nous pouvons gérer maintenant un nombre &#039;&#039;illimité&#039;&#039; de clients.&lt;br /&gt;
&lt;br /&gt;
=== Performances ===&lt;br /&gt;
La boucle &#039;&#039;while&#039;&#039; de notre serveur consomme énormément de CPU, environ 100% de CPU par client ! On aurais pu utiliser un temporisateur avec &#039;&#039;nanosleep&#039;&#039; pour baisser la consommation CPU entre 5% et 10%...&lt;br /&gt;
Le faîte de basculer sur une fonction bloquante comme &#039;&#039;epoll_wait&#039;&#039; qui utilise des signaux (interruptions) permet de réduire considérablement la consommation CPU. Gardez à l&#039;esprit qu&#039;il n&#039;y a rien de mieux que les interruptions, la preuve, lorsque l&#039;on fait un &#039;&#039;top&#039;&#039;, notre programme à complétement disparu de la liste !&lt;br /&gt;
&lt;br /&gt;
== Modification du serveur ==&lt;br /&gt;
Modifions le code du serveur TCP pour y intégrer &#039;&#039;epoll&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;arpa/inet.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Port d&#039;écoute de la socket&lt;br /&gt;
#define PORT 8080&lt;br /&gt;
// Adresse d&#039;écoute (toutes les adresses)&lt;br /&gt;
#define IP INADDR_ANY&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
// Nombre de connexions clients&lt;br /&gt;
#define NB_EVENTS 2&lt;br /&gt;
// Taille du tampon de lecture des messages&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Commande pour arrêter le serveur&lt;br /&gt;
#define EXIT_WORD &amp;quot;exit&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse);&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse);&lt;br /&gt;
int waitForClient(int * serverSocket);&lt;br /&gt;
void addClientToTab(int * epfd, int * clientSocket);&lt;br /&gt;
void manageClient(int * epfd, int * clientSocket);&lt;br /&gt;
void addEpollEvent(int * epfd, int * socket);&lt;br /&gt;
void delEpollEvent(int * epfd, int * socket);&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
	// Création du descripteur EPOLL&lt;br /&gt;
	int epfd = epoll_create1(0);&lt;br /&gt;
	// Structure contenant les événements EPOLL&lt;br /&gt;
	struct epoll_event events[NB_EVENTS];&lt;br /&gt;
	// Structure contenant l&#039;adresse&lt;br /&gt;
	struct sockaddr_in adresse;&lt;br /&gt;
	initAdresse(&amp;amp;adresse);&lt;br /&gt;
	// Descripteur de la socket du serveur&lt;br /&gt;
	int serverSocket = initSocket(&amp;amp;adresse);&lt;br /&gt;
	addEpollEvent(&amp;amp;epfd, &amp;amp;serverSocket);&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	while (1) {&lt;br /&gt;
		int nfds = epoll_wait(epfd, events, NB_EVENTS, -1);&lt;br /&gt;
		for (int i = 0; i &amp;lt; nfds; i++) {&lt;br /&gt;
			if (events[i].data.fd == serverSocket) {&lt;br /&gt;
				if ((clientSocket = waitForClient(&amp;amp;serverSocket)) != -1) {&lt;br /&gt;
					// On ajoute le nouveau client au tableau des descripteurs&lt;br /&gt;
					addClientToTab(&amp;amp;epfd, &amp;amp;clientSocket);&lt;br /&gt;
				}&lt;br /&gt;
			} else if (events[i].events &amp;amp; EPOLLIN) {&lt;br /&gt;
				// On a reçu quelque chose&lt;br /&gt;
				manageClient(&amp;amp;epfd, &amp;amp;events[i].data.fd);&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	close(serverSocket);&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
// Initialisation de la structure sockaddr_in&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse) {&lt;br /&gt;
	(*adresse).sin_family = AF_INET;&lt;br /&gt;
	(*adresse).sin_addr.s_addr = IP;&lt;br /&gt;
	(*adresse).sin_port = htons( PORT);&lt;br /&gt;
}&lt;br /&gt;
// Démarrage de la socket serveur&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse) {&lt;br /&gt;
	// Descripteur de socket&lt;br /&gt;
	int fdsocket;&lt;br /&gt;
	// Nombre d&#039;options&lt;br /&gt;
	int opt = 1;&lt;br /&gt;
	// Création de la socket en TCP&lt;br /&gt;
	if ((fdsocket = socket(AF_INET, SOCK_STREAM, 0)) == -1) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Création de la socket\n&amp;quot;);&lt;br /&gt;
	// Paramètrage de la socket&lt;br /&gt;
	if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,&lt;br /&gt;
			sizeof(opt)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Paramètrage de la socket\n&amp;quot;);&lt;br /&gt;
	// Attachement de la socket sur le port et l&#039;adresse IP&lt;br /&gt;
	if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Attachement de la socket sur le port %i\n&amp;quot;, PORT);&lt;br /&gt;
	// Passage en écoute de la socket&lt;br /&gt;
	if (listen(fdsocket, BACKLOG) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la mise en écoute: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	// Passage en mode non bloquant&lt;br /&gt;
	fcntl(fdsocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
	printf(&amp;quot;Mise en écoute de la socket\n&amp;quot;);&lt;br /&gt;
	return fdsocket;&lt;br /&gt;
}&lt;br /&gt;
// Attente de connexion d&#039;un client&lt;br /&gt;
int waitForClient(int * serverSocket) {&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	// Structure contenant l&#039;adresse du client&lt;br /&gt;
	struct sockaddr_in clientAdresse;&lt;br /&gt;
	int addrLen = sizeof(clientAdresse);&lt;br /&gt;
	if ((clientSocket = accept(*serverSocket, (struct sockaddr*) &amp;amp;clientAdresse,&lt;br /&gt;
			(socklen_t*) &amp;amp;addrLen)) != -1) {&lt;br /&gt;
		// Convertion de l&#039;IP en texte&lt;br /&gt;
		char ip[INET_ADDRSTRLEN];&lt;br /&gt;
		inet_ntop(AF_INET, &amp;amp;(clientAdresse.sin_addr), ip, INET_ADDRSTRLEN);&lt;br /&gt;
		printf(&amp;quot;Connexion de %s:%i\n&amp;quot;, ip, clientAdresse.sin_port);&lt;br /&gt;
		// Passage en mode non bloquant&lt;br /&gt;
		int opt = 1;&lt;br /&gt;
		setsockopt(clientSocket, SOL_SOCKET, SO_KEEPALIVE, &amp;amp;opt, sizeof(1));&lt;br /&gt;
	}&lt;br /&gt;
	return clientSocket;&lt;br /&gt;
}&lt;br /&gt;
// Ajoute les clients au tableau&lt;br /&gt;
void addClientToTab(int * epfd, int * clientSocket) {&lt;br /&gt;
	addEpollEvent(epfd, clientSocket);&lt;br /&gt;
	// Nouvelle connexion, envoie du message de bienvenu&lt;br /&gt;
	send(*clientSocket, &amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;,&lt;br /&gt;
			strlen(&amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;), MSG_DONTWAIT);&lt;br /&gt;
}&lt;br /&gt;
// On traite l&#039;input des clients&lt;br /&gt;
void manageClient(int * epfd, int * clientSocket) {&lt;br /&gt;
	// Création d&#039;un tampon pour stocker les messages des clients dans la heap&lt;br /&gt;
	static char buffer[BUFFER_LEN + 1];&lt;br /&gt;
	int len = recv(*clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
	// Booléen pour suivre l&#039;état de la socket&lt;br /&gt;
	int isClosed = 0;&lt;br /&gt;
	if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
		// Une erreur est survenue&lt;br /&gt;
		printf(&amp;quot;Errno [%i] : %s\n&amp;quot;, errno, strerror(errno));&lt;br /&gt;
		isClosed = 1;&lt;br /&gt;
	} else if (len == 0) {&lt;br /&gt;
		// Le client s&#039;est déconnecté (extrémité de la socket fermée)&lt;br /&gt;
		isClosed = 1;&lt;br /&gt;
	} else if (len &amp;gt; 0) {&lt;br /&gt;
		// Ajout du terminateur de chaîne&lt;br /&gt;
		buffer[len] = &#039;\0&#039;;&lt;br /&gt;
		if (strncmp(buffer, EXIT_WORD, 4) == 0) {&lt;br /&gt;
			// Le client veut se déconnecter&lt;br /&gt;
			send(*clientSocket, &amp;quot;Bye\n&amp;quot;, strlen(&amp;quot;Bye\n&amp;quot;), 0);&lt;br /&gt;
			isClosed = 1;&lt;br /&gt;
		} else {&lt;br /&gt;
			// On renvoie le texte au client dans un buffer assez grand&lt;br /&gt;
			int len = strlen(&amp;quot;Vous avez dit : &amp;quot;) + strlen(buffer) + 1;&lt;br /&gt;
			char response[len];&lt;br /&gt;
			strcpy(response, &amp;quot;Vous avez dit : &amp;quot;);&lt;br /&gt;
			strcat(response, buffer);&lt;br /&gt;
			// Un seul envoie permet de ne pas surcharger le réseau&lt;br /&gt;
			send(*clientSocket, response, strlen(response), 0);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	if (isClosed == 1) {&lt;br /&gt;
		// On enlève la socket des événements EPOLL&lt;br /&gt;
		delEpollEvent(epfd, clientSocket);&lt;br /&gt;
		// La socket est fermé ou le client veut quitter le serveur !&lt;br /&gt;
		printf(&amp;quot;Fermeture de la connexion avec le client\n&amp;quot;);&lt;br /&gt;
		// Fermeture de la socket&lt;br /&gt;
		close(*clientSocket);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// Ajoute un descripteur de socket aux événements EPOLL&lt;br /&gt;
void addEpollEvent(int * epfd, int * socket) {&lt;br /&gt;
	printf(&amp;quot;Ajout de la socket %i dans les événements EPOLL\n&amp;quot;, *socket);&lt;br /&gt;
	// Structure contenant les événements EPOLL&lt;br /&gt;
	struct epoll_event event;&lt;br /&gt;
	// On enregistre la socket du serveur comme descripteur EPOLL&lt;br /&gt;
	event.data.fd = *socket;&lt;br /&gt;
	// On surveille les message entrant, la fermeture de la socket&lt;br /&gt;
	event.events = EPOLLIN | EPOLLRDHUP;&lt;br /&gt;
	epoll_ctl(*epfd, EPOLL_CTL_ADD, *socket, &amp;amp;event);&lt;br /&gt;
}&lt;br /&gt;
// Efface un descripteur de socket des événements EPOLL&lt;br /&gt;
void delEpollEvent(int * epfd, int * socket) {&lt;br /&gt;
	printf(&amp;quot;Suppression de la socket %i des événements EPOLL\n&amp;quot;, *socket);&lt;br /&gt;
	// Structure contenant les événements EPOLL&lt;br /&gt;
	struct epoll_event event;&lt;br /&gt;
	// On enregistre la socket du serveur comme descripteur EPOLL&lt;br /&gt;
	event.data.fd = *socket;&lt;br /&gt;
	// On surveille les message entrant, la fermeture de la socket&lt;br /&gt;
	event.events = EPOLLIN | EPOLLRDHUP;&lt;br /&gt;
	epoll_ctl(*epfd, EPOLL_CTL_DEL, *socket, &amp;amp;event);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Voici ce que l&#039;on peut observer dans la console:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Création de la socket&lt;br /&gt;
Paramètrage de la socket&lt;br /&gt;
Attachement de la socket sur le port 8080&lt;br /&gt;
Mise en écoute de la socket&lt;br /&gt;
Ajout de la socket 4 dans les événements EPOLL&lt;br /&gt;
Connexion de 127.0.0.1:17607&lt;br /&gt;
Ajout de la socket 5 dans les événements EPOLL&lt;br /&gt;
Suppression de la socket 5 des événements EPOLL&lt;br /&gt;
Fermeture de la connexion avec le client&lt;br /&gt;
Connexion de 127.0.0.1:18119&lt;br /&gt;
Ajout de la socket 5 dans les événements EPOLL&lt;br /&gt;
Connexion de 127.0.0.1:18631&lt;br /&gt;
Ajout de la socket 6 dans les événements EPOLL&lt;br /&gt;
Suppression de la socket 5 des événements EPOLL&lt;br /&gt;
Fermeture de la connexion avec le client&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Magsss</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=C_socket&amp;diff=3286</id>
		<title>C socket</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=C_socket&amp;diff=3286"/>
		<updated>2018-11-01T18:29:27Z</updated>

		<summary type="html">&lt;p&gt;Magsss : /* Utilisation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
||&lt;br /&gt;
Les sockets permettent de connecter deux programmes qui s&#039;exécutent sur deux machines différentes. Cette connexion se fait à travers le réseau grâce à l&#039;utilisation d&#039;un port (TCP ou UDP) et d&#039;une adresse IP. &lt;br /&gt;
&lt;br /&gt;
Il y a deux types de sockets, une qui est démarrée par la partie serveur en &#039;&#039;écoute&#039;&#039; et l&#039;autre démarrée par la partie cliente qui se connecte à la première.&lt;br /&gt;
&lt;br /&gt;
Ci-contre une image résumant les différentes étapes pour arriver à l&#039;envoi de données.&lt;br /&gt;
||&lt;br /&gt;
[[Fichier:Socket workflow.png|centré|200px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Création  =&lt;br /&gt;
== Côté serveur ==&lt;br /&gt;
Tout d&#039;abord il faut créer l&#039;objet socket:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt; &lt;br /&gt;
&lt;br /&gt;
int socket(int domain, int type, int protocol)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*domain &amp;amp;rarr; integer, communication domain e.g., AF_INET (IPv4 protocol) , AF_INET6 (IPv6 protocol)&lt;br /&gt;
*type &amp;amp;rarr; type de communication&lt;br /&gt;
**SOCK_STREAM: TCP&lt;br /&gt;
**SOCK_DGRAM: UDP&lt;br /&gt;
**SOCK_RAW: socket à l&#039;état brut (bas niveau)&lt;br /&gt;
*protocol &amp;amp;rarr; valeur du champ &#039;&#039;protocol&#039;&#039; de l&#039;en-tête de niveau 3 (généralement &#039;&#039;0&#039;&#039;)&lt;br /&gt;
*la valeur de retour est le fichier descripteur de la socket ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Une fois le descripteur de socket créé, il est possible de le configurer:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt; &lt;br /&gt;
&lt;br /&gt;
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* level &amp;amp;rarr; niveau d&#039;application de l&#039;option (SOL_SOCKET, TCP, UDP, ...);&lt;br /&gt;
* optname &amp;amp;rarr; le nom de l&#039;option (&#039;&#039;SO_REUSEADDR&#039;&#039;, &#039;&#039;SO_REUSEPORT&#039;&#039;, &#039;&#039;SO_KEEPALIVE&#039;&#039;, ...)&lt;br /&gt;
* optval, optlen  &amp;amp;rarr; utilisé pour accéder aux options de la socket;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
Il existe plusieurs options de socket, les plus utilisées étant :&lt;br /&gt;
* &#039;&#039;SO_REUSEADDR&#039;&#039; &amp;amp;rarr; permet de réutiliser l&#039;adresse de la socket tout de suite même si cette dernière est dans l&#039;état &#039;&#039;wait&#039;&#039;;&lt;br /&gt;
* &#039;&#039;SO_REUSEPORT&#039;&#039; &amp;amp;rarr; permet de réutiliser le port de la socket tout de suite même si cette dernière est dans l&#039;état &#039;&#039;wait&#039;&#039;;&lt;br /&gt;
* &#039;&#039;SO_KEEPALIVE&#039;&#039; &amp;amp;rarr; envoie des informations périodiquement pour tester si l&#039;extrémité du tunnel est toujours présente;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Il faut maintenant attacher la socket à un port Internet et une adresse IP:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* addr &amp;amp;rarr; une structure symbolisant l&#039;adresse &lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
struct sockaddr_in {&lt;br /&gt;
    short            sin_family;   // e.g: AF_INET&lt;br /&gt;
    unsigned short   sin_port;     // e.g: htons(3490)&lt;br /&gt;
    struct in_addr   sin_addr;     // détaillé ci-dessous&lt;br /&gt;
    char             sin_zero[8];  // &#039;0&#039; habituellement&lt;br /&gt;
};&lt;br /&gt;
struct in_addr {&lt;br /&gt;
    unsigned long s_addr;  // initialiser avec inet_aton()&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On peut maintenant passer la socket en état d&#039;écoute, elle est prête à recevoir des connexions:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int listen(int sockfd, int backlog)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* backlog &amp;amp;rarr; taille maximum de la file d&#039;attente de la socket après laquelle le système répond avec &#039;&#039;ECONNREFUSED&#039;&#039;;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On peut maintenant attendre une connexion:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* addr &amp;amp;rarr; l&#039;adresse du client;&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de la structure addr;&lt;br /&gt;
* le code retour varie entre un entier positif qui correspond au descripteur de la socket cliente et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
== Côté client ==&lt;br /&gt;
Après avoir créé la socket avec &#039;&#039;socket&#039;&#039; on peut se connecter à la partie serveur:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* addr &amp;amp;rarr; une structure symbolisant l&#039;adresse (cf. ci-dessus)&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
= Lecture / écriture =&lt;br /&gt;
== Fonctions génériques ==&lt;br /&gt;
Tout d&#039;abord, on peut dire que la socket est un fichier et se manipule donc comme un fichier !&lt;br /&gt;
===Lecture===&lt;br /&gt;
Pour lire dans une socket on peut utiliser &#039;&#039;read&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t read(int fd, void *buf, size_t count);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères où mettre le message reçu;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à recevoir;&lt;br /&gt;
* le code retour correspond au nombre de caractères reçus ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
===Écriture===&lt;br /&gt;
Pour écrire dans une socket ou peut utiliser &#039;&#039;write&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t write(int fd, const void *buf, size_t count);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères contenant le message à envoyer;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à envoyer;&lt;br /&gt;
* le code retour correspond au nombre de caractères envoyés ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
===Modification===&lt;br /&gt;
Pour modifier un descripteur fichier de socket on utilise &#039;&#039;fcntl&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int fcntl(int fd, int cmd, ... /* int args */ );&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* cmd &amp;amp;rarr; la commande de modification (descripteur, drapeaux, ...);&lt;br /&gt;
* args &amp;amp;rarr; les arguments correspondant à la commande&lt;br /&gt;
&lt;br /&gt;
Cette commande va nous permettre de modifier le drapeau du descripteur de la socket (&#039;&#039;F_SETFL&#039;&#039;) pour le rendre non bloquant (&#039;&#039;O_NONBLOCK&#039;&#039;) !&lt;br /&gt;
&lt;br /&gt;
== Fonctions spécifiques ==&lt;br /&gt;
Il existe des fonctions spécifiques pour manipuler les sockets. Ces fonctions ressemblent aux précédentes mais acceptent des drapeaux en plus pour pouvoir modifier le descripteur &#039;&#039;à la volée&#039;&#039;.&lt;br /&gt;
===Lecture===&lt;br /&gt;
Pour lire dans une socket on peut utiliser &#039;&#039;recv&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t recv(int fd, void *buf, size_t count, int flags);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères où mettre le message reçu;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à recevoir;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;O_NONBLOCK&#039;&#039;);&lt;br /&gt;
* le code retour correspond au nombre de caractères reçus ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
===Écriture===&lt;br /&gt;
Pour écrire dans une socket ou peut utiliser &#039;&#039;send&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t send(int fd, const void *buf, size_t count, int flags);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères contenant le message à envoyer;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à envoyer;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;MSG_CONFIRM&#039;&#039;, &#039;&#039;MSG_DONTWAIT &#039;&#039;, ...);&lt;br /&gt;
* le code retour correspond au nombre de caractères envoyés ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
= Cas d&#039;utilisation =&lt;br /&gt;
== Création ==&lt;br /&gt;
Pour commencer, il faut créer le descripteur de fichier:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int fdsocket;&lt;br /&gt;
if ((fdsocket = socket(AF_INET, SOCK_STREAM, 0)) == -1) {&lt;br /&gt;
	printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Paramétrage des options ==&lt;br /&gt;
Il faut maintenant configurer la réutilisation de l&#039;adresse et du port&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int opt = 1;&lt;br /&gt;
if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,	sizeof(opt)) != 0) {&lt;br /&gt;
	printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Attachement ==&lt;br /&gt;
On peut maintenant attacher la socket à un port et une adresse:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
struct sockaddr_in adresse;&lt;br /&gt;
&lt;br /&gt;
adresse.sin_family = AF_INET;&lt;br /&gt;
// Ecoute sur toutes les adresses (INADDR_ANY &amp;lt;=&amp;gt; 0.0.0.0)&lt;br /&gt;
adresse.sin_addr.s_addr = INADDR_ANY;&lt;br /&gt;
// Conversion du port en valeur réseaux (Host TO Network Short)&lt;br /&gt;
adresse.sin_port = htons(8080);&lt;br /&gt;
&lt;br /&gt;
if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
	printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mise en écoute ==&lt;br /&gt;
La socket est prête à passer à l&#039;écoute des connexions des clients:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
&lt;br /&gt;
if (listen(fdsocket, BACKLOG) != 0) {&lt;br /&gt;
	printf(&amp;quot;Echéc de la mise en écoute: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Acceptation des connexions ==&lt;br /&gt;
Il est possible d&#039;accepter les nouvelles connexions:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int clientSocket;&lt;br /&gt;
// Structure contenant l&#039;adresse du client&lt;br /&gt;
struct sockaddr_in clientAdresse;&lt;br /&gt;
int addrLen = sizeof(clientAdresse);&lt;br /&gt;
if ((clientSocket = accept(*serverSocket, (struct sockaddr*) &amp;amp;clientAdresse, (socklen_t*) &amp;amp;addrLen)) != -1) {&lt;br /&gt;
	// Convertion de l&#039;IP en texte&lt;br /&gt;
	char ip[INET_ADDRSTRLEN];&lt;br /&gt;
	inet_ntop(AF_INET, &amp;amp;(clientAdresse.sin_addr), ip, INET_ADDRSTRLEN);&lt;br /&gt;
	printf(&amp;quot;Connexion de %s:%i\n&amp;quot;, ip, clientAdresse.sin_port);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Il est possible d&#039;utiliser &#039;&#039;fcntl&#039;&#039; pour passer le descripteur de la socket en non bloquant, avant l&#039;appel à la fonction &#039;&#039;accept&#039;&#039;. Si le descripteur reste bloquant, la fonction &#039;&#039;accept&#039;&#039; ne rendra pas la main tant qu&#039;un client ne se sera connecté, ayant pour résultat le blocage complet du programme !&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Passage en mode non bloquant&lt;br /&gt;
fcntl(fdsocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
On peut remarquer, au passage, l&#039;utilisation de &#039;&#039;inet_ntop&#039;&#039; pour convertir l&#039;adresse du client du format binaire au format text.&lt;br /&gt;
&lt;br /&gt;
== Lecture / Écriture ==&lt;br /&gt;
=== Mode connecté (TCP) ===&lt;br /&gt;
On peut maintenant utiliser le descripteur de la socket du client pour lire et écrire.&lt;br /&gt;
* avec les fonctions génériques:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Descripteur de la socket du client&lt;br /&gt;
int clientSocket;&lt;br /&gt;
char buffer[BUFFER_LEN];&lt;br /&gt;
// Passage en mode non bloquant, sinon read attend&lt;br /&gt;
fcntl(clientSocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
int len = read(clientSocket, buffer, BUFFER_LEN);&lt;br /&gt;
&lt;br /&gt;
send(clientSocket, &amp;quot;Coucou\n&amp;quot;, strlen(&amp;quot;Coucou\n&amp;quot;));&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* avec les fonctions spécifiques:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Descripteur de la socket du client&lt;br /&gt;
int clientSocket;&lt;br /&gt;
char buffer[BUFFER_LEN];&lt;br /&gt;
&lt;br /&gt;
int len = recv(clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
&lt;br /&gt;
send(clientSocket, &amp;quot;Coucou\n&amp;quot;, strlen(&amp;quot;Coucou\n&amp;quot;), MSG_DONTWAIT);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Analysons le retour de la fonction &#039;&#039;read&#039;&#039; ou &#039;&#039;recv&#039;&#039; lorsque l&#039;on utilise un descripteur non bloquant d&#039;après la [http://man7.org/linux/man-pages/man2/recvmsg.2.html documentation] :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
NAME &lt;br /&gt;
       recv, recvfrom, recvmsg - receive a message from a socket&lt;br /&gt;
[...]&lt;br /&gt;
MSG_DONTWAIT (since Linux 2.2)&lt;br /&gt;
    Enables nonblocking operation; if the operation would block, the call fails with the error EAGAIN or EWOULDBLOCK (this can also be enabled using the O_NONBLOCK flag with the F_SETFL fcntl(2)). &lt;br /&gt;
[...]&lt;br /&gt;
RETURN VALUE&lt;br /&gt;
[...]&lt;br /&gt;
These calls return the number of bytes received, or -1 if an error&lt;br /&gt;
       occurred.  In the event of an error, errno is set to indicate the&lt;br /&gt;
       error.&lt;br /&gt;
When a stream socket peer has performed an orderly shutdown, the&lt;br /&gt;
       return value will be 0 (the traditional &amp;quot;end-of-file&amp;quot; return).&lt;br /&gt;
[...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Le retour est :&lt;br /&gt;
* lorsque le descripteur est vide (aucune donnée de la part du client) &#039;&#039;-1&#039;&#039; et &#039;&#039;errno&#039;&#039; sera positionné à la valeur &#039;&#039;EAGAIN&#039;&#039;, il ne faut donc pas fermer la socket si le retour est &#039;&#039;-1&#039;&#039; ! Il faut d&#039;abord vérifier que &#039;&#039;errno&#039;&#039; &#039;&#039;&#039;n&#039;a pas&#039;&#039;&#039; la valeur &#039;&#039;EAGAIN&#039;&#039;;&lt;br /&gt;
* lorsque le retour est &#039;&#039;0&#039;&#039;, cela signifie que l&#039;extrémité est fermée, il faut donc fermer le descripteur local;&lt;br /&gt;
* lorsque la valeur est supérieure à &#039;&#039;0&#039;&#039; elle correspond au nombre d&#039;octets reçus.&lt;br /&gt;
&lt;br /&gt;
On peut donc en déduire le morceau de code suivant pour gérer le retour de &#039;&#039;read&#039;&#039; ou &#039;&#039;recv&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int len = recv(clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
&lt;br /&gt;
if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
	// Une erreur est survenue&lt;br /&gt;
} else if (len == 0) {&lt;br /&gt;
	// Le client s&#039;est déconnecté (extrémité de la socket fermée)			&lt;br /&gt;
} else if (len &amp;gt; 0) {&lt;br /&gt;
	// Le client à envoyé des données	&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Mode datagramme (UDP) ===&lt;br /&gt;
===Lecture===&lt;br /&gt;
Pour lire dans une socket on peut utiliser &#039;&#039;recvfrom&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t recvfrom(int fd, void *buf, size_t count, int flags, const struct sockaddr *addr, socklen_t addrlen);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères où mettre le message reçu;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à recevoir;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;O_NONBLOCK&#039;&#039;);&lt;br /&gt;
*  addr &amp;amp;rarr; une structure symbolisant l&#039;adresse du client (cf. ci-dessus)&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour correspond au nombre de caractères reçus ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
===Écriture===&lt;br /&gt;
Pour écrire dans une socket ou peut utiliser &#039;&#039;sendto&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t sendto(int fd, const void *buf, size_t count, const struct sockaddr *addr, socklen_t addrlen);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères contenant le message à envoyer;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à envoyer;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;MSG_DONTWAIT &#039;&#039;, ...);&lt;br /&gt;
* addr &amp;amp;rarr; une structure symbolisant l&#039;adresse du client (cf. ci-dessus)&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour correspond au nombre de caractères envoyer ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
= Exemples =&lt;br /&gt;
== Serveur TCP ==&lt;br /&gt;
Voici un exemple de serveur &#039;&#039;echo&#039;&#039; qui renvoie le message au client. Les connexions utilisent TCP et un simple client comme telnet suffit pour l&#039;utiliser.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;arpa/inet.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Port d&#039;écoute de la socket&lt;br /&gt;
#define PORT 8080&lt;br /&gt;
// Adresse d&#039;écoute (toutes les adresses)&lt;br /&gt;
#define IP INADDR_ANY&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
// Nombre de connexions clients&lt;br /&gt;
#define NB_CLIENTS 2&lt;br /&gt;
// Taille du tampon de lecture des messages&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Commande pour arrêter le serveur&lt;br /&gt;
#define EXIT_WORD &amp;quot;exit&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse);&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse);&lt;br /&gt;
int waitForClient(int * serverSocket);&lt;br /&gt;
void addClientToTab(int clientSocket, int clients[]);&lt;br /&gt;
void manageClient(int clients[]);&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
	// Création et initialisation du tableau contenant les descripteurs des sockets clients&lt;br /&gt;
	int clients[NB_CLIENTS];&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_CLIENTS; i++) {&lt;br /&gt;
		clients[i] = -1;&lt;br /&gt;
	}&lt;br /&gt;
	// Structure contenant l&#039;adresse&lt;br /&gt;
	struct sockaddr_in adresse;&lt;br /&gt;
	initAdresse(&amp;amp;adresse);&lt;br /&gt;
	// Descripteur de la socket du serveur&lt;br /&gt;
	int serverSocket = initSocket(&amp;amp;adresse);&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	while (1) {&lt;br /&gt;
		// Descripteur de la socket du client, on attend une connexion&lt;br /&gt;
		if ((clientSocket = waitForClient(&amp;amp;serverSocket)) != -1) {&lt;br /&gt;
			// On ajoute le nouveau client au tableau des descripteurs&lt;br /&gt;
			addClientToTab(clientSocket, clients);&lt;br /&gt;
		}&lt;br /&gt;
		manageClient(clients);&lt;br /&gt;
	}&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
// Initialisation de la structure sockaddr_in&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse) {&lt;br /&gt;
	(*adresse).sin_family = AF_INET;&lt;br /&gt;
	(*adresse).sin_addr.s_addr = IP;&lt;br /&gt;
	(*adresse).sin_port = htons( PORT);&lt;br /&gt;
}&lt;br /&gt;
// Démarrage de la socket serveur&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse) {&lt;br /&gt;
	// Descripteur de socket&lt;br /&gt;
	int fdsocket;&lt;br /&gt;
	// Nombre d&#039;options&lt;br /&gt;
	int opt = 1;&lt;br /&gt;
	// Création de la socket en TCP&lt;br /&gt;
	if ((fdsocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Création de la socket\n&amp;quot;);&lt;br /&gt;
	// Paramètrage de la socket&lt;br /&gt;
	if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,&lt;br /&gt;
			sizeof(opt)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Paramètrage de la socket\n&amp;quot;);&lt;br /&gt;
	// Attachement de la socket sur le port et l&#039;adresse IP&lt;br /&gt;
	if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Attachement de la socket sur le port %i\n&amp;quot;, PORT);&lt;br /&gt;
	// Passage en écoute de la socket&lt;br /&gt;
	if (listen(fdsocket, BACKLOG) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la mise en écoute: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	// Passage en mode non bloquant&lt;br /&gt;
	fcntl(fdsocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
	printf(&amp;quot;Mise en écoute de la socket\n&amp;quot;);&lt;br /&gt;
	return fdsocket;&lt;br /&gt;
}&lt;br /&gt;
// Attente de connexion d&#039;un client&lt;br /&gt;
int waitForClient(int * serverSocket) {&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	// Structure contenant l&#039;adresse du client&lt;br /&gt;
	struct sockaddr_in clientAdresse;&lt;br /&gt;
	int addrLen = sizeof(clientAdresse);&lt;br /&gt;
	if ((clientSocket = accept(*serverSocket, (struct sockaddr*) &amp;amp;clientAdresse,&lt;br /&gt;
			(socklen_t*) &amp;amp;addrLen)) != -1) {&lt;br /&gt;
		// Convertion de l&#039;IP en texte&lt;br /&gt;
		char ip[INET_ADDRSTRLEN];&lt;br /&gt;
		inet_ntop(AF_INET, &amp;amp;(clientAdresse.sin_addr), ip, INET_ADDRSTRLEN);&lt;br /&gt;
		printf(&amp;quot;Connexion de %s:%i\n&amp;quot;, ip, clientAdresse.sin_port);&lt;br /&gt;
		// Passage en mode non bloquant&lt;br /&gt;
		int opt = 1;&lt;br /&gt;
		setsockopt(clientSocket, SOL_SOCKET, SO_KEEPALIVE, &amp;amp;opt, sizeof(1));&lt;br /&gt;
	}&lt;br /&gt;
	return clientSocket;&lt;br /&gt;
}&lt;br /&gt;
// Ajoute les clients au tableau&lt;br /&gt;
void addClientToTab(int clientSocket, int clients[]) {&lt;br /&gt;
	// On vérifie si on à de la place de libre&lt;br /&gt;
	int found = -1;&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_CLIENTS; i++) {&lt;br /&gt;
		// On cherche de la place&lt;br /&gt;
		if (clients[i] == -1) {&lt;br /&gt;
			// On ajoute le client&lt;br /&gt;
			printf(&amp;quot;Ajout du client à l&#039;index %i\n&amp;quot;, i);&lt;br /&gt;
			clients[i] = clientSocket;&lt;br /&gt;
			// Nouvelle connexion, envoie du message de bienvenu&lt;br /&gt;
			send(clientSocket, &amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;, 	strlen(&amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;),&lt;br /&gt;
					MSG_DONTWAIT);&lt;br /&gt;
			found = 0;&lt;br /&gt;
			break;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	if (found == -1) {&lt;br /&gt;
		// On a plus de place de libre&lt;br /&gt;
		puts(&amp;quot;Plus de place, désolé&amp;quot;);&lt;br /&gt;
		close(clientSocket);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// On traite l&#039;input des clients&lt;br /&gt;
void manageClient(int clients[]) {&lt;br /&gt;
	// Création d&#039;un tampon pour stocker les messages des clients dans la heap&lt;br /&gt;
	static char buffer[BUFFER_LEN + 1];&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_CLIENTS; i++) {&lt;br /&gt;
		// On vérifie les messages&lt;br /&gt;
		int clientSocket = clients[i];&lt;br /&gt;
		if (clientSocket == -1) {&lt;br /&gt;
			continue;&lt;br /&gt;
		}&lt;br /&gt;
		int len = recv(clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
		// Booléen pour suivre l&#039;état de la socket&lt;br /&gt;
		int isClosed = 0;&lt;br /&gt;
		if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
			// Une erreur est survenue&lt;br /&gt;
			printf(&amp;quot;Errno [%i] : %s\n&amp;quot;, errno, strerror(errno));&lt;br /&gt;
			isClosed = 1;&lt;br /&gt;
		} else if (len == 0) {&lt;br /&gt;
			// Le client s&#039;est déconnecté (extrémité de la socket fermée)&lt;br /&gt;
			isClosed = 1;&lt;br /&gt;
		} else if (len &amp;gt; 0) {&lt;br /&gt;
			// Ajout du terminateur de chaîne&lt;br /&gt;
			buffer[len] = &#039;\0&#039;;&lt;br /&gt;
			if (strncmp(buffer, EXIT_WORD, 4) == 0) {&lt;br /&gt;
				// Le client veut se déconnecter&lt;br /&gt;
				send(clientSocket, &amp;quot;Bye\n&amp;quot;, strlen(&amp;quot;Bye\n&amp;quot;), 0);&lt;br /&gt;
				isClosed = 1;&lt;br /&gt;
			} else {&lt;br /&gt;
				// On renvoie le texte au client dans un buffer assez grand&lt;br /&gt;
				int len = strlen(&amp;quot;Vous avez dit : &amp;quot;) + strlen(buffer) + 1;&lt;br /&gt;
				char response[len];&lt;br /&gt;
				strcpy(response, &amp;quot;Vous avez dit : &amp;quot;);&lt;br /&gt;
				strcat(response, buffer);&lt;br /&gt;
				// Un seul envoie permet de ne pas surcharger le réseau&lt;br /&gt;
				send(clientSocket, response, strlen(response), 0);&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
		if (isClosed == 1) {&lt;br /&gt;
			// La socket est fermé ou le client veut quitter le serveur !&lt;br /&gt;
			printf(&amp;quot;Fermeture de la connexion avec le client\n&amp;quot;);&lt;br /&gt;
			// Fermeture de la socket&lt;br /&gt;
			close(clientSocket);&lt;br /&gt;
			// On fait de la place dans le tableau&lt;br /&gt;
			clients[i] = -1;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On peut interroger notre serveur avec &#039;&#039;telnet&#039;&#039; (&#039;&#039;yum -y install telnet&#039;&#039;):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# telnet 127.0.0.1 8080&lt;br /&gt;
Trying 127.0.0.1...&lt;br /&gt;
Connected to 127.0.0.1.&lt;br /&gt;
Escape character is &#039;^]&#039;.&lt;br /&gt;
Entrez &#039;exit&#039; pour quitter&lt;br /&gt;
coucou&lt;br /&gt;
Vous avez dit : coucou&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== Serveur UDP ==&lt;br /&gt;
Si on essaie de reproduire le même type de serveur mais cette fois-ci avec une socket en UDP, cela devient beaucoup plus simple. En effet, en UDP, plus besoin de tenir un tableau avec les descripteurs des sockets des clients car... on est en mode déconnecté (datagramme) ! Entendez par là que les clients envoient un message sans attendre de réponse et sans se soucier de savoir si le message est arrivé au serveur.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;arpa/inet.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Port d&#039;écoute de la socket&lt;br /&gt;
#define PORT 8080&lt;br /&gt;
// Adresse d&#039;écoute (toutes les adresses)&lt;br /&gt;
#define IP INADDR_ANY&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
// Taille du tampon de lecture des messages&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse);&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse);&lt;br /&gt;
void manageClient(int serverSocket);&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
	// Structure contenant l&#039;adresse&lt;br /&gt;
	struct sockaddr_in adresse;&lt;br /&gt;
	initAdresse(&amp;amp;adresse);&lt;br /&gt;
	// Descripteur de la socket du serveur&lt;br /&gt;
	int serverSocket = initSocket(&amp;amp;adresse);&lt;br /&gt;
	while (1) {&lt;br /&gt;
		// Descripteur de la socket du client, on attend une connexion&lt;br /&gt;
		manageClient(serverSocket);&lt;br /&gt;
	}&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
// Initialisation de la structure sockaddr_in&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse) {&lt;br /&gt;
	(*adresse).sin_family = AF_INET;&lt;br /&gt;
	(*adresse).sin_addr.s_addr = IP;&lt;br /&gt;
	(*adresse).sin_port = htons( PORT);&lt;br /&gt;
}&lt;br /&gt;
// Démarrage de la socket serveur&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse) {&lt;br /&gt;
	// Descripteur de socket&lt;br /&gt;
	int fdsocket;&lt;br /&gt;
	// Nombre d&#039;options&lt;br /&gt;
	int opt = 1;&lt;br /&gt;
	// Création de la socket en UDP&lt;br /&gt;
	if ((fdsocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Création de la socket\n&amp;quot;);&lt;br /&gt;
	// Paramètrage de la socket&lt;br /&gt;
	if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,&lt;br /&gt;
			sizeof(opt)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Paramètrage de la socket\n&amp;quot;);&lt;br /&gt;
	// Attachement de la socket sur le port et l&#039;adresse IP&lt;br /&gt;
	if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Attachement de la socket sur le port %i\n&amp;quot;, PORT);&lt;br /&gt;
	return fdsocket;&lt;br /&gt;
}&lt;br /&gt;
// On traite l&#039;input des clients&lt;br /&gt;
void manageClient(int serverSocket) {&lt;br /&gt;
	// Création d&#039;un tampon pour stocker les messages des clients dans la heap&lt;br /&gt;
	static char buffer[BUFFER_LEN + 1];&lt;br /&gt;
	// Structure contenant l&#039;adresse du client&lt;br /&gt;
	struct sockaddr_in clientAdresse;&lt;br /&gt;
	unsigned int addrLen = sizeof(clientAdresse);&lt;br /&gt;
	// On vérifie si on a reçu un message&lt;br /&gt;
	int len = recvfrom(serverSocket, buffer, BUFFER_LEN, MSG_DONTWAIT,&lt;br /&gt;
			(struct sockaddr*) &amp;amp;clientAdresse, &amp;amp;addrLen);&lt;br /&gt;
	if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
		printf(&amp;quot;Problème de socket %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	} else if (len &amp;gt; 0) {&lt;br /&gt;
		printf(&amp;quot;Message reçu de %s:%i\n&amp;quot;, inet_ntoa(clientAdresse.sin_addr),&lt;br /&gt;
				ntohs(clientAdresse.sin_port));&lt;br /&gt;
		// Ajout du terminateur de chaîne&lt;br /&gt;
		buffer[len] = &#039;\0&#039;;&lt;br /&gt;
		// On renvoie le texte au client dans un buffer assez grand&lt;br /&gt;
		len = strlen(&amp;quot;Vous avez dit : &amp;quot;) + strlen(buffer) + 1;&lt;br /&gt;
		char response[len];&lt;br /&gt;
		strcpy(response, &amp;quot;Vous avez dit : &amp;quot;);&lt;br /&gt;
		strcat(response, buffer);&lt;br /&gt;
		// Un seul envoie permet de ne pas surcharger le réseau&lt;br /&gt;
		sendto(serverSocket, response, strlen(response), MSG_DONTWAIT,&lt;br /&gt;
				(struct sockaddr*) &amp;amp;clientAdresse, addrLen);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
On peut interroger notre serveur avec &#039;&#039;netcat&#039;&#039; (&#039;&#039;yum -y install nc&#039;&#039;):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# nc -u 127.0.0.1 8080&lt;br /&gt;
coucou&lt;br /&gt;
Vous avez dit : coucou&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Notification évènementielle avec epoll =&lt;br /&gt;
Depuis la version 2.5 du noyau Linux il est possible d&#039;utiliser une série de fonctions, appartenant à l&#039;espace utilisateur (user-space), permettant de suivre l&#039;état d&#039;un descripteur de fichier.&lt;br /&gt;
La différence avec read, recv ou encore recvfrom c&#039;est que l&#039;on va pouvoir surveiller plusieurs descripteurs de fichiers en même temps, laissant le CPU libre pour effectuer autre chose !&lt;br /&gt;
&lt;br /&gt;
== Utilisation ==&lt;br /&gt;
Tout d&#039;abord il faut créer un descripteur de fichier de type &#039;&#039;epoll&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int epoll_create1(int flags);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* flags &amp;amp;rarr; EPOLL_CLOEXEC pour éviter les situations de compétition en environnement parallélisé;&lt;br /&gt;
* le code retour correspond au descripteur &#039;&#039;epoll&#039;&#039; ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée).&lt;br /&gt;
&lt;br /&gt;
Une fois le descripteur &#039;&#039;epoll&#039;&#039; créé, il faut lui associer un événement:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* epfd &amp;amp;rarr; le descripteur &#039;&#039;epoll&#039;&#039; à modifier;&lt;br /&gt;
* op &amp;amp;rarr; une opération parmi:&lt;br /&gt;
** EPOLL_CTL_ADD &amp;amp;rarr; pour ajouter un événement sur un descripteur de fichier;&lt;br /&gt;
** EPOLL_CTL_MOD &amp;amp;rarr; pour modifier un événement sur un descripteur de fichier;&lt;br /&gt;
** EPOLL_CTL_DEL &amp;amp;rarr; pour supprimer un événement sur un descripteur de fichier;&lt;br /&gt;
* fd &amp;amp;rarr; un descripteur de fichier à surveiller;&lt;br /&gt;
* event &amp;amp;rarr; la structure contenant l&#039;événement à surveiller (&#039;&#039;EPOLLIN&#039;&#039;, &#039;&#039;EPOLLOUT&#039;&#039;, EPOLLRDHUP&#039;&#039;, ...);&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; en cas de succès ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée).&lt;br /&gt;
&lt;br /&gt;
Voici la structure &#039;&#039;epoll_event&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
struct epoll_event {&lt;br /&gt;
	uint32_t events;    /* Epoll events */&lt;br /&gt;
	epoll_data_t data;  /* User data variable */&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
typedef union epoll_data {&lt;br /&gt;
	void    *ptr;&lt;br /&gt;
	int      fd;&lt;br /&gt;
	uint32_t u32;&lt;br /&gt;
	uint64_t u64;&lt;br /&gt;
} epoll_data_t;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Enfin, il ne reste plus qu&#039;à &#039;&#039;attendre&#039;&#039; des événements:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* epfd &amp;amp;rarr; le descripteur &#039;&#039;epoll&#039;&#039; à modifier;&lt;br /&gt;
* events &amp;amp;rarr; un tableau de &#039;&#039;epoll_event contenant les événements;&lt;br /&gt;
* maxevents &amp;amp;rarr; le nombre maximum d&#039;événements à traiter (selon la taille du tableau);&lt;br /&gt;
* timeout &amp;amp;rarr; le temps, en millisecondes que la fonction attend:&lt;br /&gt;
** &#039;&#039;-1&#039;&#039; &amp;amp;rarr; la fonction devient bloquante;&lt;br /&gt;
** &#039;&#039;0&#039;&#039; &amp;amp;rarr; retourne immédiatement;&lt;br /&gt;
** &#039;&#039;&amp;gt; 0&#039;&#039; &amp;amp;rarr; attend le nombre de millisecondes spécifiées;&lt;br /&gt;
* le code retour correspond au nombre d&#039;événements ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée).&lt;br /&gt;
&lt;br /&gt;
== Améliorations architecturales ==&lt;br /&gt;
=== Gestion des utilisateurs===&lt;br /&gt;
Tout d&#039;abord, le faite de laisser &#039;&#039;epoll&#039;&#039; gérer les descripteurs de socket nous permet de supprimer le tableau &#039;&#039;clients&#039;&#039; ainsi que la variable &#039;&#039;NB_CLIENTS&#039;&#039;. En effet, la référence vers le descripteur est maintenu par &#039;&#039;epoll&#039;&#039; et nous pouvons gérer maintenant un nombre &#039;&#039;illimité&#039;&#039; de clients.&lt;br /&gt;
=== Performances ===&lt;br /&gt;
La boucle &#039;&#039;while&#039;&#039; de notre serveur consomme énormément de CPU, environ 100% de CPU par client ! On aurais pu utiliser un temporisateur avec &#039;&#039;nanosleep&#039;&#039; pour baisser la consommation CPU entre 5% et 10%...&lt;br /&gt;
Le faîte de basculer sur une fonction bloquante comme &#039;&#039;epoll_wait&#039;&#039; qui utilise des signaux (interruptions) permet de réduire considérablement la consommation CPU. Gardez à l&#039;esprit qu&#039;il n&#039;y a rien de mieux que les interruptions, la preuve, lorsque l&#039;on fait un &#039;&#039;top&#039;&#039;, notre programme à complétement disparu de la liste !&lt;br /&gt;
&lt;br /&gt;
== Modification du serveur ==&lt;br /&gt;
Modifions le code du serveur TCP pour y intégrer &#039;&#039;epoll&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;arpa/inet.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Port d&#039;écoute de la socket&lt;br /&gt;
#define PORT 8080&lt;br /&gt;
// Adresse d&#039;écoute (toutes les adresses)&lt;br /&gt;
#define IP INADDR_ANY&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
// Nombre de connexions clients&lt;br /&gt;
#define NB_EVENTS 2&lt;br /&gt;
// Taille du tampon de lecture des messages&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Commande pour arrêter le serveur&lt;br /&gt;
#define EXIT_WORD &amp;quot;exit&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse);&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse);&lt;br /&gt;
int waitForClient(int * serverSocket);&lt;br /&gt;
void addClientToTab(int * epfd, int * clientSocket);&lt;br /&gt;
void manageClient(int * epfd, int * clientSocket);&lt;br /&gt;
void addEpollEvent(int * epfd, int * socket);&lt;br /&gt;
void delEpollEvent(int * epfd, int * socket);&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
	// Création du descripteur EPOLL&lt;br /&gt;
	int epfd = epoll_create1(0);&lt;br /&gt;
	// Structure contenant les événements EPOLL&lt;br /&gt;
	struct epoll_event events[NB_EVENTS];&lt;br /&gt;
	// Structure contenant l&#039;adresse&lt;br /&gt;
	struct sockaddr_in adresse;&lt;br /&gt;
	initAdresse(&amp;amp;adresse);&lt;br /&gt;
	// Descripteur de la socket du serveur&lt;br /&gt;
	int serverSocket = initSocket(&amp;amp;adresse);&lt;br /&gt;
	addEpollEvent(&amp;amp;epfd, &amp;amp;serverSocket);&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	while (1) {&lt;br /&gt;
		int nfds = epoll_wait(epfd, events, NB_EVENTS, -1);&lt;br /&gt;
		for (int i = 0; i &amp;lt; nfds; i++) {&lt;br /&gt;
			if (events[i].data.fd == serverSocket) {&lt;br /&gt;
				if ((clientSocket = waitForClient(&amp;amp;serverSocket)) != -1) {&lt;br /&gt;
					// On ajoute le nouveau client au tableau des descripteurs&lt;br /&gt;
					addClientToTab(&amp;amp;epfd, &amp;amp;clientSocket);&lt;br /&gt;
				}&lt;br /&gt;
			} else if (events[i].events &amp;amp; EPOLLIN) {&lt;br /&gt;
				// On a reçu quelque chose&lt;br /&gt;
				manageClient(&amp;amp;epfd, &amp;amp;events[i].data.fd);&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	close(serverSocket);&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
// Initialisation de la structure sockaddr_in&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse) {&lt;br /&gt;
	(*adresse).sin_family = AF_INET;&lt;br /&gt;
	(*adresse).sin_addr.s_addr = IP;&lt;br /&gt;
	(*adresse).sin_port = htons( PORT);&lt;br /&gt;
}&lt;br /&gt;
// Démarrage de la socket serveur&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse) {&lt;br /&gt;
	// Descripteur de socket&lt;br /&gt;
	int fdsocket;&lt;br /&gt;
	// Nombre d&#039;options&lt;br /&gt;
	int opt = 1;&lt;br /&gt;
	// Création de la socket en TCP&lt;br /&gt;
	if ((fdsocket = socket(AF_INET, SOCK_STREAM, 0)) == -1) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Création de la socket\n&amp;quot;);&lt;br /&gt;
	// Paramètrage de la socket&lt;br /&gt;
	if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,&lt;br /&gt;
			sizeof(opt)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Paramètrage de la socket\n&amp;quot;);&lt;br /&gt;
	// Attachement de la socket sur le port et l&#039;adresse IP&lt;br /&gt;
	if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Attachement de la socket sur le port %i\n&amp;quot;, PORT);&lt;br /&gt;
	// Passage en écoute de la socket&lt;br /&gt;
	if (listen(fdsocket, BACKLOG) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la mise en écoute: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	// Passage en mode non bloquant&lt;br /&gt;
	fcntl(fdsocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
	printf(&amp;quot;Mise en écoute de la socket\n&amp;quot;);&lt;br /&gt;
	return fdsocket;&lt;br /&gt;
}&lt;br /&gt;
// Attente de connexion d&#039;un client&lt;br /&gt;
int waitForClient(int * serverSocket) {&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	// Structure contenant l&#039;adresse du client&lt;br /&gt;
	struct sockaddr_in clientAdresse;&lt;br /&gt;
	int addrLen = sizeof(clientAdresse);&lt;br /&gt;
	if ((clientSocket = accept(*serverSocket, (struct sockaddr*) &amp;amp;clientAdresse,&lt;br /&gt;
			(socklen_t*) &amp;amp;addrLen)) != -1) {&lt;br /&gt;
		// Convertion de l&#039;IP en texte&lt;br /&gt;
		char ip[INET_ADDRSTRLEN];&lt;br /&gt;
		inet_ntop(AF_INET, &amp;amp;(clientAdresse.sin_addr), ip, INET_ADDRSTRLEN);&lt;br /&gt;
		printf(&amp;quot;Connexion de %s:%i\n&amp;quot;, ip, clientAdresse.sin_port);&lt;br /&gt;
		// Passage en mode non bloquant&lt;br /&gt;
		int opt = 1;&lt;br /&gt;
		setsockopt(clientSocket, SOL_SOCKET, SO_KEEPALIVE, &amp;amp;opt, sizeof(1));&lt;br /&gt;
	}&lt;br /&gt;
	return clientSocket;&lt;br /&gt;
}&lt;br /&gt;
// Ajoute les clients au tableau&lt;br /&gt;
void addClientToTab(int * epfd, int * clientSocket) {&lt;br /&gt;
	addEpollEvent(epfd, clientSocket);&lt;br /&gt;
	// Nouvelle connexion, envoie du message de bienvenu&lt;br /&gt;
	send(*clientSocket, &amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;,&lt;br /&gt;
			strlen(&amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;), MSG_DONTWAIT);&lt;br /&gt;
}&lt;br /&gt;
// On traite l&#039;input des clients&lt;br /&gt;
void manageClient(int * epfd, int * clientSocket) {&lt;br /&gt;
	// Création d&#039;un tampon pour stocker les messages des clients dans la heap&lt;br /&gt;
	static char buffer[BUFFER_LEN + 1];&lt;br /&gt;
	int len = recv(*clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
	// Booléen pour suivre l&#039;état de la socket&lt;br /&gt;
	int isClosed = 0;&lt;br /&gt;
	if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
		// Une erreur est survenue&lt;br /&gt;
		printf(&amp;quot;Errno [%i] : %s\n&amp;quot;, errno, strerror(errno));&lt;br /&gt;
		isClosed = 1;&lt;br /&gt;
	} else if (len == 0) {&lt;br /&gt;
		// Le client s&#039;est déconnecté (extrémité de la socket fermée)&lt;br /&gt;
		isClosed = 1;&lt;br /&gt;
	} else if (len &amp;gt; 0) {&lt;br /&gt;
		// Ajout du terminateur de chaîne&lt;br /&gt;
		buffer[len] = &#039;\0&#039;;&lt;br /&gt;
		if (strncmp(buffer, EXIT_WORD, 4) == 0) {&lt;br /&gt;
			// Le client veut se déconnecter&lt;br /&gt;
			send(*clientSocket, &amp;quot;Bye\n&amp;quot;, strlen(&amp;quot;Bye\n&amp;quot;), 0);&lt;br /&gt;
			isClosed = 1;&lt;br /&gt;
		} else {&lt;br /&gt;
			// On renvoie le texte au client dans un buffer assez grand&lt;br /&gt;
			int len = strlen(&amp;quot;Vous avez dit : &amp;quot;) + strlen(buffer) + 1;&lt;br /&gt;
			char response[len];&lt;br /&gt;
			strcpy(response, &amp;quot;Vous avez dit : &amp;quot;);&lt;br /&gt;
			strcat(response, buffer);&lt;br /&gt;
			// Un seul envoie permet de ne pas surcharger le réseau&lt;br /&gt;
			send(*clientSocket, response, strlen(response), 0);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	if (isClosed == 1) {&lt;br /&gt;
		// On enlève la socket des événements EPOLL&lt;br /&gt;
		delEpollEvent(epfd, clientSocket);&lt;br /&gt;
		// La socket est fermé ou le client veut quitter le serveur !&lt;br /&gt;
		printf(&amp;quot;Fermeture de la connexion avec le client\n&amp;quot;);&lt;br /&gt;
		// Fermeture de la socket&lt;br /&gt;
		close(*clientSocket);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// Ajoute un descripteur de socket aux événements EPOLL&lt;br /&gt;
void addEpollEvent(int * epfd, int * socket) {&lt;br /&gt;
	printf(&amp;quot;Ajout de la socket %i dans les événements EPOLL\n&amp;quot;, *socket);&lt;br /&gt;
	// Structure contenant les événements EPOLL&lt;br /&gt;
	struct epoll_event event;&lt;br /&gt;
	// On enregistre la socket du serveur comme descripteur EPOLL&lt;br /&gt;
	event.data.fd = *socket;&lt;br /&gt;
	// On surveille les message entrant, la fermeture de la socket&lt;br /&gt;
	event.events = EPOLLIN | EPOLLRDHUP;&lt;br /&gt;
	epoll_ctl(*epfd, EPOLL_CTL_ADD, *socket, &amp;amp;event);&lt;br /&gt;
}&lt;br /&gt;
// Efface un descripteur de socket des événements EPOLL&lt;br /&gt;
void delEpollEvent(int * epfd, int * socket) {&lt;br /&gt;
	printf(&amp;quot;Suppression de la socket %i des événements EPOLL\n&amp;quot;, *socket);&lt;br /&gt;
	// Structure contenant les événements EPOLL&lt;br /&gt;
	struct epoll_event event;&lt;br /&gt;
	// On enregistre la socket du serveur comme descripteur EPOLL&lt;br /&gt;
	event.data.fd = *socket;&lt;br /&gt;
	// On surveille les message entrant, la fermeture de la socket&lt;br /&gt;
	event.events = EPOLLIN | EPOLLRDHUP;&lt;br /&gt;
	epoll_ctl(*epfd, EPOLL_CTL_DEL, *socket, &amp;amp;event);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Voici ce que l&#039;on peut observer dans la console:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Création de la socket&lt;br /&gt;
Paramètrage de la socket&lt;br /&gt;
Attachement de la socket sur le port 8080&lt;br /&gt;
Mise en écoute de la socket&lt;br /&gt;
Ajout de la socket 4 dans les événements EPOLL&lt;br /&gt;
Connexion de 127.0.0.1:17607&lt;br /&gt;
Ajout de la socket 5 dans les événements EPOLL&lt;br /&gt;
Suppression de la socket 5 des événements EPOLL&lt;br /&gt;
Fermeture de la connexion avec le client&lt;br /&gt;
Connexion de 127.0.0.1:18119&lt;br /&gt;
Ajout de la socket 5 dans les événements EPOLL&lt;br /&gt;
Connexion de 127.0.0.1:18631&lt;br /&gt;
Ajout de la socket 6 dans les événements EPOLL&lt;br /&gt;
Suppression de la socket 5 des événements EPOLL&lt;br /&gt;
Fermeture de la connexion avec le client&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Magsss</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=C_socket&amp;diff=3285</id>
		<title>C socket</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=C_socket&amp;diff=3285"/>
		<updated>2018-11-01T18:29:06Z</updated>

		<summary type="html">&lt;p&gt;Magsss : /* Utilisation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
||&lt;br /&gt;
Les sockets permettent de connecter deux programmes qui s&#039;exécutent sur deux machines différentes. Cette connexion se fait à travers le réseau grâce à l&#039;utilisation d&#039;un port (TCP ou UDP) et d&#039;une adresse IP. &lt;br /&gt;
&lt;br /&gt;
Il y a deux types de sockets, une qui est démarrée par la partie serveur en &#039;&#039;écoute&#039;&#039; et l&#039;autre démarrée par la partie cliente qui se connecte à la première.&lt;br /&gt;
&lt;br /&gt;
Ci-contre une image résumant les différentes étapes pour arriver à l&#039;envoi de données.&lt;br /&gt;
||&lt;br /&gt;
[[Fichier:Socket workflow.png|centré|200px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Création  =&lt;br /&gt;
== Côté serveur ==&lt;br /&gt;
Tout d&#039;abord il faut créer l&#039;objet socket:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt; &lt;br /&gt;
&lt;br /&gt;
int socket(int domain, int type, int protocol)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*domain &amp;amp;rarr; integer, communication domain e.g., AF_INET (IPv4 protocol) , AF_INET6 (IPv6 protocol)&lt;br /&gt;
*type &amp;amp;rarr; type de communication&lt;br /&gt;
**SOCK_STREAM: TCP&lt;br /&gt;
**SOCK_DGRAM: UDP&lt;br /&gt;
**SOCK_RAW: socket à l&#039;état brut (bas niveau)&lt;br /&gt;
*protocol &amp;amp;rarr; valeur du champ &#039;&#039;protocol&#039;&#039; de l&#039;en-tête de niveau 3 (généralement &#039;&#039;0&#039;&#039;)&lt;br /&gt;
*la valeur de retour est le fichier descripteur de la socket ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Une fois le descripteur de socket créé, il est possible de le configurer:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt; &lt;br /&gt;
&lt;br /&gt;
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* level &amp;amp;rarr; niveau d&#039;application de l&#039;option (SOL_SOCKET, TCP, UDP, ...);&lt;br /&gt;
* optname &amp;amp;rarr; le nom de l&#039;option (&#039;&#039;SO_REUSEADDR&#039;&#039;, &#039;&#039;SO_REUSEPORT&#039;&#039;, &#039;&#039;SO_KEEPALIVE&#039;&#039;, ...)&lt;br /&gt;
* optval, optlen  &amp;amp;rarr; utilisé pour accéder aux options de la socket;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
Il existe plusieurs options de socket, les plus utilisées étant :&lt;br /&gt;
* &#039;&#039;SO_REUSEADDR&#039;&#039; &amp;amp;rarr; permet de réutiliser l&#039;adresse de la socket tout de suite même si cette dernière est dans l&#039;état &#039;&#039;wait&#039;&#039;;&lt;br /&gt;
* &#039;&#039;SO_REUSEPORT&#039;&#039; &amp;amp;rarr; permet de réutiliser le port de la socket tout de suite même si cette dernière est dans l&#039;état &#039;&#039;wait&#039;&#039;;&lt;br /&gt;
* &#039;&#039;SO_KEEPALIVE&#039;&#039; &amp;amp;rarr; envoie des informations périodiquement pour tester si l&#039;extrémité du tunnel est toujours présente;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Il faut maintenant attacher la socket à un port Internet et une adresse IP:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* addr &amp;amp;rarr; une structure symbolisant l&#039;adresse &lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
struct sockaddr_in {&lt;br /&gt;
    short            sin_family;   // e.g: AF_INET&lt;br /&gt;
    unsigned short   sin_port;     // e.g: htons(3490)&lt;br /&gt;
    struct in_addr   sin_addr;     // détaillé ci-dessous&lt;br /&gt;
    char             sin_zero[8];  // &#039;0&#039; habituellement&lt;br /&gt;
};&lt;br /&gt;
struct in_addr {&lt;br /&gt;
    unsigned long s_addr;  // initialiser avec inet_aton()&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On peut maintenant passer la socket en état d&#039;écoute, elle est prête à recevoir des connexions:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int listen(int sockfd, int backlog)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* backlog &amp;amp;rarr; taille maximum de la file d&#039;attente de la socket après laquelle le système répond avec &#039;&#039;ECONNREFUSED&#039;&#039;;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On peut maintenant attendre une connexion:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* addr &amp;amp;rarr; l&#039;adresse du client;&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de la structure addr;&lt;br /&gt;
* le code retour varie entre un entier positif qui correspond au descripteur de la socket cliente et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
== Côté client ==&lt;br /&gt;
Après avoir créé la socket avec &#039;&#039;socket&#039;&#039; on peut se connecter à la partie serveur:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* addr &amp;amp;rarr; une structure symbolisant l&#039;adresse (cf. ci-dessus)&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
= Lecture / écriture =&lt;br /&gt;
== Fonctions génériques ==&lt;br /&gt;
Tout d&#039;abord, on peut dire que la socket est un fichier et se manipule donc comme un fichier !&lt;br /&gt;
===Lecture===&lt;br /&gt;
Pour lire dans une socket on peut utiliser &#039;&#039;read&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t read(int fd, void *buf, size_t count);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères où mettre le message reçu;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à recevoir;&lt;br /&gt;
* le code retour correspond au nombre de caractères reçus ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
===Écriture===&lt;br /&gt;
Pour écrire dans une socket ou peut utiliser &#039;&#039;write&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t write(int fd, const void *buf, size_t count);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères contenant le message à envoyer;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à envoyer;&lt;br /&gt;
* le code retour correspond au nombre de caractères envoyés ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
===Modification===&lt;br /&gt;
Pour modifier un descripteur fichier de socket on utilise &#039;&#039;fcntl&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int fcntl(int fd, int cmd, ... /* int args */ );&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* cmd &amp;amp;rarr; la commande de modification (descripteur, drapeaux, ...);&lt;br /&gt;
* args &amp;amp;rarr; les arguments correspondant à la commande&lt;br /&gt;
&lt;br /&gt;
Cette commande va nous permettre de modifier le drapeau du descripteur de la socket (&#039;&#039;F_SETFL&#039;&#039;) pour le rendre non bloquant (&#039;&#039;O_NONBLOCK&#039;&#039;) !&lt;br /&gt;
&lt;br /&gt;
== Fonctions spécifiques ==&lt;br /&gt;
Il existe des fonctions spécifiques pour manipuler les sockets. Ces fonctions ressemblent aux précédentes mais acceptent des drapeaux en plus pour pouvoir modifier le descripteur &#039;&#039;à la volée&#039;&#039;.&lt;br /&gt;
===Lecture===&lt;br /&gt;
Pour lire dans une socket on peut utiliser &#039;&#039;recv&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t recv(int fd, void *buf, size_t count, int flags);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères où mettre le message reçu;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à recevoir;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;O_NONBLOCK&#039;&#039;);&lt;br /&gt;
* le code retour correspond au nombre de caractères reçus ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
===Écriture===&lt;br /&gt;
Pour écrire dans une socket ou peut utiliser &#039;&#039;send&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t send(int fd, const void *buf, size_t count, int flags);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères contenant le message à envoyer;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à envoyer;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;MSG_CONFIRM&#039;&#039;, &#039;&#039;MSG_DONTWAIT &#039;&#039;, ...);&lt;br /&gt;
* le code retour correspond au nombre de caractères envoyés ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
= Cas d&#039;utilisation =&lt;br /&gt;
== Création ==&lt;br /&gt;
Pour commencer, il faut créer le descripteur de fichier:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int fdsocket;&lt;br /&gt;
if ((fdsocket = socket(AF_INET, SOCK_STREAM, 0)) == -1) {&lt;br /&gt;
	printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Paramétrage des options ==&lt;br /&gt;
Il faut maintenant configurer la réutilisation de l&#039;adresse et du port&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int opt = 1;&lt;br /&gt;
if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,	sizeof(opt)) != 0) {&lt;br /&gt;
	printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Attachement ==&lt;br /&gt;
On peut maintenant attacher la socket à un port et une adresse:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
struct sockaddr_in adresse;&lt;br /&gt;
&lt;br /&gt;
adresse.sin_family = AF_INET;&lt;br /&gt;
// Ecoute sur toutes les adresses (INADDR_ANY &amp;lt;=&amp;gt; 0.0.0.0)&lt;br /&gt;
adresse.sin_addr.s_addr = INADDR_ANY;&lt;br /&gt;
// Conversion du port en valeur réseaux (Host TO Network Short)&lt;br /&gt;
adresse.sin_port = htons(8080);&lt;br /&gt;
&lt;br /&gt;
if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
	printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mise en écoute ==&lt;br /&gt;
La socket est prête à passer à l&#039;écoute des connexions des clients:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
&lt;br /&gt;
if (listen(fdsocket, BACKLOG) != 0) {&lt;br /&gt;
	printf(&amp;quot;Echéc de la mise en écoute: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Acceptation des connexions ==&lt;br /&gt;
Il est possible d&#039;accepter les nouvelles connexions:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int clientSocket;&lt;br /&gt;
// Structure contenant l&#039;adresse du client&lt;br /&gt;
struct sockaddr_in clientAdresse;&lt;br /&gt;
int addrLen = sizeof(clientAdresse);&lt;br /&gt;
if ((clientSocket = accept(*serverSocket, (struct sockaddr*) &amp;amp;clientAdresse, (socklen_t*) &amp;amp;addrLen)) != -1) {&lt;br /&gt;
	// Convertion de l&#039;IP en texte&lt;br /&gt;
	char ip[INET_ADDRSTRLEN];&lt;br /&gt;
	inet_ntop(AF_INET, &amp;amp;(clientAdresse.sin_addr), ip, INET_ADDRSTRLEN);&lt;br /&gt;
	printf(&amp;quot;Connexion de %s:%i\n&amp;quot;, ip, clientAdresse.sin_port);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Il est possible d&#039;utiliser &#039;&#039;fcntl&#039;&#039; pour passer le descripteur de la socket en non bloquant, avant l&#039;appel à la fonction &#039;&#039;accept&#039;&#039;. Si le descripteur reste bloquant, la fonction &#039;&#039;accept&#039;&#039; ne rendra pas la main tant qu&#039;un client ne se sera connecté, ayant pour résultat le blocage complet du programme !&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Passage en mode non bloquant&lt;br /&gt;
fcntl(fdsocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
On peut remarquer, au passage, l&#039;utilisation de &#039;&#039;inet_ntop&#039;&#039; pour convertir l&#039;adresse du client du format binaire au format text.&lt;br /&gt;
&lt;br /&gt;
== Lecture / Écriture ==&lt;br /&gt;
=== Mode connecté (TCP) ===&lt;br /&gt;
On peut maintenant utiliser le descripteur de la socket du client pour lire et écrire.&lt;br /&gt;
* avec les fonctions génériques:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Descripteur de la socket du client&lt;br /&gt;
int clientSocket;&lt;br /&gt;
char buffer[BUFFER_LEN];&lt;br /&gt;
// Passage en mode non bloquant, sinon read attend&lt;br /&gt;
fcntl(clientSocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
int len = read(clientSocket, buffer, BUFFER_LEN);&lt;br /&gt;
&lt;br /&gt;
send(clientSocket, &amp;quot;Coucou\n&amp;quot;, strlen(&amp;quot;Coucou\n&amp;quot;));&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* avec les fonctions spécifiques:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Descripteur de la socket du client&lt;br /&gt;
int clientSocket;&lt;br /&gt;
char buffer[BUFFER_LEN];&lt;br /&gt;
&lt;br /&gt;
int len = recv(clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
&lt;br /&gt;
send(clientSocket, &amp;quot;Coucou\n&amp;quot;, strlen(&amp;quot;Coucou\n&amp;quot;), MSG_DONTWAIT);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Analysons le retour de la fonction &#039;&#039;read&#039;&#039; ou &#039;&#039;recv&#039;&#039; lorsque l&#039;on utilise un descripteur non bloquant d&#039;après la [http://man7.org/linux/man-pages/man2/recvmsg.2.html documentation] :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
NAME &lt;br /&gt;
       recv, recvfrom, recvmsg - receive a message from a socket&lt;br /&gt;
[...]&lt;br /&gt;
MSG_DONTWAIT (since Linux 2.2)&lt;br /&gt;
    Enables nonblocking operation; if the operation would block, the call fails with the error EAGAIN or EWOULDBLOCK (this can also be enabled using the O_NONBLOCK flag with the F_SETFL fcntl(2)). &lt;br /&gt;
[...]&lt;br /&gt;
RETURN VALUE&lt;br /&gt;
[...]&lt;br /&gt;
These calls return the number of bytes received, or -1 if an error&lt;br /&gt;
       occurred.  In the event of an error, errno is set to indicate the&lt;br /&gt;
       error.&lt;br /&gt;
When a stream socket peer has performed an orderly shutdown, the&lt;br /&gt;
       return value will be 0 (the traditional &amp;quot;end-of-file&amp;quot; return).&lt;br /&gt;
[...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Le retour est :&lt;br /&gt;
* lorsque le descripteur est vide (aucune donnée de la part du client) &#039;&#039;-1&#039;&#039; et &#039;&#039;errno&#039;&#039; sera positionné à la valeur &#039;&#039;EAGAIN&#039;&#039;, il ne faut donc pas fermer la socket si le retour est &#039;&#039;-1&#039;&#039; ! Il faut d&#039;abord vérifier que &#039;&#039;errno&#039;&#039; &#039;&#039;&#039;n&#039;a pas&#039;&#039;&#039; la valeur &#039;&#039;EAGAIN&#039;&#039;;&lt;br /&gt;
* lorsque le retour est &#039;&#039;0&#039;&#039;, cela signifie que l&#039;extrémité est fermée, il faut donc fermer le descripteur local;&lt;br /&gt;
* lorsque la valeur est supérieure à &#039;&#039;0&#039;&#039; elle correspond au nombre d&#039;octets reçus.&lt;br /&gt;
&lt;br /&gt;
On peut donc en déduire le morceau de code suivant pour gérer le retour de &#039;&#039;read&#039;&#039; ou &#039;&#039;recv&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int len = recv(clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
&lt;br /&gt;
if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
	// Une erreur est survenue&lt;br /&gt;
} else if (len == 0) {&lt;br /&gt;
	// Le client s&#039;est déconnecté (extrémité de la socket fermée)			&lt;br /&gt;
} else if (len &amp;gt; 0) {&lt;br /&gt;
	// Le client à envoyé des données	&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Mode datagramme (UDP) ===&lt;br /&gt;
===Lecture===&lt;br /&gt;
Pour lire dans une socket on peut utiliser &#039;&#039;recvfrom&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t recvfrom(int fd, void *buf, size_t count, int flags, const struct sockaddr *addr, socklen_t addrlen);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères où mettre le message reçu;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à recevoir;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;O_NONBLOCK&#039;&#039;);&lt;br /&gt;
*  addr &amp;amp;rarr; une structure symbolisant l&#039;adresse du client (cf. ci-dessus)&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour correspond au nombre de caractères reçus ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
===Écriture===&lt;br /&gt;
Pour écrire dans une socket ou peut utiliser &#039;&#039;sendto&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t sendto(int fd, const void *buf, size_t count, const struct sockaddr *addr, socklen_t addrlen);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères contenant le message à envoyer;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à envoyer;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;MSG_DONTWAIT &#039;&#039;, ...);&lt;br /&gt;
* addr &amp;amp;rarr; une structure symbolisant l&#039;adresse du client (cf. ci-dessus)&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour correspond au nombre de caractères envoyer ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
= Exemples =&lt;br /&gt;
== Serveur TCP ==&lt;br /&gt;
Voici un exemple de serveur &#039;&#039;echo&#039;&#039; qui renvoie le message au client. Les connexions utilisent TCP et un simple client comme telnet suffit pour l&#039;utiliser.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;arpa/inet.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Port d&#039;écoute de la socket&lt;br /&gt;
#define PORT 8080&lt;br /&gt;
// Adresse d&#039;écoute (toutes les adresses)&lt;br /&gt;
#define IP INADDR_ANY&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
// Nombre de connexions clients&lt;br /&gt;
#define NB_CLIENTS 2&lt;br /&gt;
// Taille du tampon de lecture des messages&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Commande pour arrêter le serveur&lt;br /&gt;
#define EXIT_WORD &amp;quot;exit&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse);&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse);&lt;br /&gt;
int waitForClient(int * serverSocket);&lt;br /&gt;
void addClientToTab(int clientSocket, int clients[]);&lt;br /&gt;
void manageClient(int clients[]);&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
	// Création et initialisation du tableau contenant les descripteurs des sockets clients&lt;br /&gt;
	int clients[NB_CLIENTS];&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_CLIENTS; i++) {&lt;br /&gt;
		clients[i] = -1;&lt;br /&gt;
	}&lt;br /&gt;
	// Structure contenant l&#039;adresse&lt;br /&gt;
	struct sockaddr_in adresse;&lt;br /&gt;
	initAdresse(&amp;amp;adresse);&lt;br /&gt;
	// Descripteur de la socket du serveur&lt;br /&gt;
	int serverSocket = initSocket(&amp;amp;adresse);&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	while (1) {&lt;br /&gt;
		// Descripteur de la socket du client, on attend une connexion&lt;br /&gt;
		if ((clientSocket = waitForClient(&amp;amp;serverSocket)) != -1) {&lt;br /&gt;
			// On ajoute le nouveau client au tableau des descripteurs&lt;br /&gt;
			addClientToTab(clientSocket, clients);&lt;br /&gt;
		}&lt;br /&gt;
		manageClient(clients);&lt;br /&gt;
	}&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
// Initialisation de la structure sockaddr_in&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse) {&lt;br /&gt;
	(*adresse).sin_family = AF_INET;&lt;br /&gt;
	(*adresse).sin_addr.s_addr = IP;&lt;br /&gt;
	(*adresse).sin_port = htons( PORT);&lt;br /&gt;
}&lt;br /&gt;
// Démarrage de la socket serveur&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse) {&lt;br /&gt;
	// Descripteur de socket&lt;br /&gt;
	int fdsocket;&lt;br /&gt;
	// Nombre d&#039;options&lt;br /&gt;
	int opt = 1;&lt;br /&gt;
	// Création de la socket en TCP&lt;br /&gt;
	if ((fdsocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Création de la socket\n&amp;quot;);&lt;br /&gt;
	// Paramètrage de la socket&lt;br /&gt;
	if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,&lt;br /&gt;
			sizeof(opt)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Paramètrage de la socket\n&amp;quot;);&lt;br /&gt;
	// Attachement de la socket sur le port et l&#039;adresse IP&lt;br /&gt;
	if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Attachement de la socket sur le port %i\n&amp;quot;, PORT);&lt;br /&gt;
	// Passage en écoute de la socket&lt;br /&gt;
	if (listen(fdsocket, BACKLOG) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la mise en écoute: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	// Passage en mode non bloquant&lt;br /&gt;
	fcntl(fdsocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
	printf(&amp;quot;Mise en écoute de la socket\n&amp;quot;);&lt;br /&gt;
	return fdsocket;&lt;br /&gt;
}&lt;br /&gt;
// Attente de connexion d&#039;un client&lt;br /&gt;
int waitForClient(int * serverSocket) {&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	// Structure contenant l&#039;adresse du client&lt;br /&gt;
	struct sockaddr_in clientAdresse;&lt;br /&gt;
	int addrLen = sizeof(clientAdresse);&lt;br /&gt;
	if ((clientSocket = accept(*serverSocket, (struct sockaddr*) &amp;amp;clientAdresse,&lt;br /&gt;
			(socklen_t*) &amp;amp;addrLen)) != -1) {&lt;br /&gt;
		// Convertion de l&#039;IP en texte&lt;br /&gt;
		char ip[INET_ADDRSTRLEN];&lt;br /&gt;
		inet_ntop(AF_INET, &amp;amp;(clientAdresse.sin_addr), ip, INET_ADDRSTRLEN);&lt;br /&gt;
		printf(&amp;quot;Connexion de %s:%i\n&amp;quot;, ip, clientAdresse.sin_port);&lt;br /&gt;
		// Passage en mode non bloquant&lt;br /&gt;
		int opt = 1;&lt;br /&gt;
		setsockopt(clientSocket, SOL_SOCKET, SO_KEEPALIVE, &amp;amp;opt, sizeof(1));&lt;br /&gt;
	}&lt;br /&gt;
	return clientSocket;&lt;br /&gt;
}&lt;br /&gt;
// Ajoute les clients au tableau&lt;br /&gt;
void addClientToTab(int clientSocket, int clients[]) {&lt;br /&gt;
	// On vérifie si on à de la place de libre&lt;br /&gt;
	int found = -1;&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_CLIENTS; i++) {&lt;br /&gt;
		// On cherche de la place&lt;br /&gt;
		if (clients[i] == -1) {&lt;br /&gt;
			// On ajoute le client&lt;br /&gt;
			printf(&amp;quot;Ajout du client à l&#039;index %i\n&amp;quot;, i);&lt;br /&gt;
			clients[i] = clientSocket;&lt;br /&gt;
			// Nouvelle connexion, envoie du message de bienvenu&lt;br /&gt;
			send(clientSocket, &amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;, 	strlen(&amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;),&lt;br /&gt;
					MSG_DONTWAIT);&lt;br /&gt;
			found = 0;&lt;br /&gt;
			break;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	if (found == -1) {&lt;br /&gt;
		// On a plus de place de libre&lt;br /&gt;
		puts(&amp;quot;Plus de place, désolé&amp;quot;);&lt;br /&gt;
		close(clientSocket);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// On traite l&#039;input des clients&lt;br /&gt;
void manageClient(int clients[]) {&lt;br /&gt;
	// Création d&#039;un tampon pour stocker les messages des clients dans la heap&lt;br /&gt;
	static char buffer[BUFFER_LEN + 1];&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_CLIENTS; i++) {&lt;br /&gt;
		// On vérifie les messages&lt;br /&gt;
		int clientSocket = clients[i];&lt;br /&gt;
		if (clientSocket == -1) {&lt;br /&gt;
			continue;&lt;br /&gt;
		}&lt;br /&gt;
		int len = recv(clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
		// Booléen pour suivre l&#039;état de la socket&lt;br /&gt;
		int isClosed = 0;&lt;br /&gt;
		if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
			// Une erreur est survenue&lt;br /&gt;
			printf(&amp;quot;Errno [%i] : %s\n&amp;quot;, errno, strerror(errno));&lt;br /&gt;
			isClosed = 1;&lt;br /&gt;
		} else if (len == 0) {&lt;br /&gt;
			// Le client s&#039;est déconnecté (extrémité de la socket fermée)&lt;br /&gt;
			isClosed = 1;&lt;br /&gt;
		} else if (len &amp;gt; 0) {&lt;br /&gt;
			// Ajout du terminateur de chaîne&lt;br /&gt;
			buffer[len] = &#039;\0&#039;;&lt;br /&gt;
			if (strncmp(buffer, EXIT_WORD, 4) == 0) {&lt;br /&gt;
				// Le client veut se déconnecter&lt;br /&gt;
				send(clientSocket, &amp;quot;Bye\n&amp;quot;, strlen(&amp;quot;Bye\n&amp;quot;), 0);&lt;br /&gt;
				isClosed = 1;&lt;br /&gt;
			} else {&lt;br /&gt;
				// On renvoie le texte au client dans un buffer assez grand&lt;br /&gt;
				int len = strlen(&amp;quot;Vous avez dit : &amp;quot;) + strlen(buffer) + 1;&lt;br /&gt;
				char response[len];&lt;br /&gt;
				strcpy(response, &amp;quot;Vous avez dit : &amp;quot;);&lt;br /&gt;
				strcat(response, buffer);&lt;br /&gt;
				// Un seul envoie permet de ne pas surcharger le réseau&lt;br /&gt;
				send(clientSocket, response, strlen(response), 0);&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
		if (isClosed == 1) {&lt;br /&gt;
			// La socket est fermé ou le client veut quitter le serveur !&lt;br /&gt;
			printf(&amp;quot;Fermeture de la connexion avec le client\n&amp;quot;);&lt;br /&gt;
			// Fermeture de la socket&lt;br /&gt;
			close(clientSocket);&lt;br /&gt;
			// On fait de la place dans le tableau&lt;br /&gt;
			clients[i] = -1;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On peut interroger notre serveur avec &#039;&#039;telnet&#039;&#039; (&#039;&#039;yum -y install telnet&#039;&#039;):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# telnet 127.0.0.1 8080&lt;br /&gt;
Trying 127.0.0.1...&lt;br /&gt;
Connected to 127.0.0.1.&lt;br /&gt;
Escape character is &#039;^]&#039;.&lt;br /&gt;
Entrez &#039;exit&#039; pour quitter&lt;br /&gt;
coucou&lt;br /&gt;
Vous avez dit : coucou&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== Serveur UDP ==&lt;br /&gt;
Si on essaie de reproduire le même type de serveur mais cette fois-ci avec une socket en UDP, cela devient beaucoup plus simple. En effet, en UDP, plus besoin de tenir un tableau avec les descripteurs des sockets des clients car... on est en mode déconnecté (datagramme) ! Entendez par là que les clients envoient un message sans attendre de réponse et sans se soucier de savoir si le message est arrivé au serveur.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;arpa/inet.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Port d&#039;écoute de la socket&lt;br /&gt;
#define PORT 8080&lt;br /&gt;
// Adresse d&#039;écoute (toutes les adresses)&lt;br /&gt;
#define IP INADDR_ANY&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
// Taille du tampon de lecture des messages&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse);&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse);&lt;br /&gt;
void manageClient(int serverSocket);&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
	// Structure contenant l&#039;adresse&lt;br /&gt;
	struct sockaddr_in adresse;&lt;br /&gt;
	initAdresse(&amp;amp;adresse);&lt;br /&gt;
	// Descripteur de la socket du serveur&lt;br /&gt;
	int serverSocket = initSocket(&amp;amp;adresse);&lt;br /&gt;
	while (1) {&lt;br /&gt;
		// Descripteur de la socket du client, on attend une connexion&lt;br /&gt;
		manageClient(serverSocket);&lt;br /&gt;
	}&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
// Initialisation de la structure sockaddr_in&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse) {&lt;br /&gt;
	(*adresse).sin_family = AF_INET;&lt;br /&gt;
	(*adresse).sin_addr.s_addr = IP;&lt;br /&gt;
	(*adresse).sin_port = htons( PORT);&lt;br /&gt;
}&lt;br /&gt;
// Démarrage de la socket serveur&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse) {&lt;br /&gt;
	// Descripteur de socket&lt;br /&gt;
	int fdsocket;&lt;br /&gt;
	// Nombre d&#039;options&lt;br /&gt;
	int opt = 1;&lt;br /&gt;
	// Création de la socket en UDP&lt;br /&gt;
	if ((fdsocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Création de la socket\n&amp;quot;);&lt;br /&gt;
	// Paramètrage de la socket&lt;br /&gt;
	if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,&lt;br /&gt;
			sizeof(opt)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Paramètrage de la socket\n&amp;quot;);&lt;br /&gt;
	// Attachement de la socket sur le port et l&#039;adresse IP&lt;br /&gt;
	if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Attachement de la socket sur le port %i\n&amp;quot;, PORT);&lt;br /&gt;
	return fdsocket;&lt;br /&gt;
}&lt;br /&gt;
// On traite l&#039;input des clients&lt;br /&gt;
void manageClient(int serverSocket) {&lt;br /&gt;
	// Création d&#039;un tampon pour stocker les messages des clients dans la heap&lt;br /&gt;
	static char buffer[BUFFER_LEN + 1];&lt;br /&gt;
	// Structure contenant l&#039;adresse du client&lt;br /&gt;
	struct sockaddr_in clientAdresse;&lt;br /&gt;
	unsigned int addrLen = sizeof(clientAdresse);&lt;br /&gt;
	// On vérifie si on a reçu un message&lt;br /&gt;
	int len = recvfrom(serverSocket, buffer, BUFFER_LEN, MSG_DONTWAIT,&lt;br /&gt;
			(struct sockaddr*) &amp;amp;clientAdresse, &amp;amp;addrLen);&lt;br /&gt;
	if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
		printf(&amp;quot;Problème de socket %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	} else if (len &amp;gt; 0) {&lt;br /&gt;
		printf(&amp;quot;Message reçu de %s:%i\n&amp;quot;, inet_ntoa(clientAdresse.sin_addr),&lt;br /&gt;
				ntohs(clientAdresse.sin_port));&lt;br /&gt;
		// Ajout du terminateur de chaîne&lt;br /&gt;
		buffer[len] = &#039;\0&#039;;&lt;br /&gt;
		// On renvoie le texte au client dans un buffer assez grand&lt;br /&gt;
		len = strlen(&amp;quot;Vous avez dit : &amp;quot;) + strlen(buffer) + 1;&lt;br /&gt;
		char response[len];&lt;br /&gt;
		strcpy(response, &amp;quot;Vous avez dit : &amp;quot;);&lt;br /&gt;
		strcat(response, buffer);&lt;br /&gt;
		// Un seul envoie permet de ne pas surcharger le réseau&lt;br /&gt;
		sendto(serverSocket, response, strlen(response), MSG_DONTWAIT,&lt;br /&gt;
				(struct sockaddr*) &amp;amp;clientAdresse, addrLen);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
On peut interroger notre serveur avec &#039;&#039;netcat&#039;&#039; (&#039;&#039;yum -y install nc&#039;&#039;):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# nc -u 127.0.0.1 8080&lt;br /&gt;
coucou&lt;br /&gt;
Vous avez dit : coucou&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Notification évènementielle avec epoll =&lt;br /&gt;
Depuis la version 2.5 du noyau Linux il est possible d&#039;utiliser une série de fonctions, appartenant à l&#039;espace utilisateur (user-space), permettant de suivre l&#039;état d&#039;un descripteur de fichier.&lt;br /&gt;
La différence avec read, recv ou encore recvfrom c&#039;est que l&#039;on va pouvoir surveiller plusieurs descripteurs de fichiers en même temps, laissant le CPU libre pour effectuer autre chose !&lt;br /&gt;
&lt;br /&gt;
== Utilisation ==&lt;br /&gt;
Tout d&#039;abord il faut créer un descripteur de fichier de type &#039;&#039;epoll&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int epoll_create1(int flags);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* flags &amp;amp;rarr; EPOLL_CLOEXEC pour éviter les situations de compétition en environnement parallélisé;&lt;br /&gt;
* le code retour correspond au descripteur &#039;&#039;epoll&#039;&#039; ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée).&lt;br /&gt;
&lt;br /&gt;
Une fois le descripteur &#039;&#039;epoll&#039;&#039; créé, il faut lui associer un événement:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* epfd &amp;amp;rarr; le descripteur &#039;&#039;epoll&#039;&#039; à modifier;&lt;br /&gt;
* op &amp;amp;rarr; une opération parmis:&lt;br /&gt;
** EPOLL_CTL_ADD &amp;amp;rarr; pour ajouter un événement sur un descripteur de fichier;&lt;br /&gt;
** EPOLL_CTL_MOD &amp;amp;rarr; pour modifier un événement sur un descripteur de fichier;&lt;br /&gt;
** EPOLL_CTL_DEL &amp;amp;rarr; pour supprimer un événement sur un descripteur de fichier;&lt;br /&gt;
* fd &amp;amp;rarr; un descripteur de fichier à surveiller;&lt;br /&gt;
* event &amp;amp;rarr; la structure contenant l&#039;événement à surveiller (&#039;&#039;EPOLLIN&#039;&#039;, &#039;&#039;EPOLLOUT&#039;&#039;, EPOLLRDHUP&#039;&#039;, ...);&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; en cas de succès ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée).&lt;br /&gt;
&lt;br /&gt;
Voici la structure &#039;&#039;epoll_event&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
struct epoll_event {&lt;br /&gt;
	uint32_t events;    /* Epoll events */&lt;br /&gt;
	epoll_data_t data;  /* User data variable */&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
typedef union epoll_data {&lt;br /&gt;
	void    *ptr;&lt;br /&gt;
	int      fd;&lt;br /&gt;
	uint32_t u32;&lt;br /&gt;
	uint64_t u64;&lt;br /&gt;
} epoll_data_t;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Enfin, il ne reste plus qu&#039;à &#039;&#039;attendre&#039;&#039; des événements:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* epfd &amp;amp;rarr; le descripteur &#039;&#039;epoll&#039;&#039; à modifier;&lt;br /&gt;
* events &amp;amp;rarr; un tableau de &#039;&#039;epoll_event contenant les événements;&lt;br /&gt;
* maxevents &amp;amp;rarr; le nombre maximum d&#039;événements à traiter (selon la taille du tableau);&lt;br /&gt;
* timeout &amp;amp;rarr; le temps, en millisecondes que la fonction attend:&lt;br /&gt;
** &#039;&#039;-1&#039;&#039; &amp;amp;rarr; la fonction devient bloquante;&lt;br /&gt;
** &#039;&#039;0&#039;&#039; &amp;amp;rarr; retourne immédiatement;&lt;br /&gt;
** &#039;&#039;&amp;gt; 0&#039;&#039; &amp;amp;rarr; attend le nombre de millisecondes spécifiées;&lt;br /&gt;
* le code retour correspond au nombre d&#039;événements ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée).&lt;br /&gt;
&lt;br /&gt;
== Améliorations architecturales ==&lt;br /&gt;
=== Gestion des utilisateurs===&lt;br /&gt;
Tout d&#039;abord, le faite de laisser &#039;&#039;epoll&#039;&#039; gérer les descripteurs de socket nous permet de supprimer le tableau &#039;&#039;clients&#039;&#039; ainsi que la variable &#039;&#039;NB_CLIENTS&#039;&#039;. En effet, la référence vers le descripteur est maintenu par &#039;&#039;epoll&#039;&#039; et nous pouvons gérer maintenant un nombre &#039;&#039;illimité&#039;&#039; de clients.&lt;br /&gt;
=== Performances ===&lt;br /&gt;
La boucle &#039;&#039;while&#039;&#039; de notre serveur consomme énormément de CPU, environ 100% de CPU par client ! On aurais pu utiliser un temporisateur avec &#039;&#039;nanosleep&#039;&#039; pour baisser la consommation CPU entre 5% et 10%...&lt;br /&gt;
Le faîte de basculer sur une fonction bloquante comme &#039;&#039;epoll_wait&#039;&#039; qui utilise des signaux (interruptions) permet de réduire considérablement la consommation CPU. Gardez à l&#039;esprit qu&#039;il n&#039;y a rien de mieux que les interruptions, la preuve, lorsque l&#039;on fait un &#039;&#039;top&#039;&#039;, notre programme à complétement disparu de la liste !&lt;br /&gt;
&lt;br /&gt;
== Modification du serveur ==&lt;br /&gt;
Modifions le code du serveur TCP pour y intégrer &#039;&#039;epoll&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;arpa/inet.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Port d&#039;écoute de la socket&lt;br /&gt;
#define PORT 8080&lt;br /&gt;
// Adresse d&#039;écoute (toutes les adresses)&lt;br /&gt;
#define IP INADDR_ANY&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
// Nombre de connexions clients&lt;br /&gt;
#define NB_EVENTS 2&lt;br /&gt;
// Taille du tampon de lecture des messages&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Commande pour arrêter le serveur&lt;br /&gt;
#define EXIT_WORD &amp;quot;exit&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse);&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse);&lt;br /&gt;
int waitForClient(int * serverSocket);&lt;br /&gt;
void addClientToTab(int * epfd, int * clientSocket);&lt;br /&gt;
void manageClient(int * epfd, int * clientSocket);&lt;br /&gt;
void addEpollEvent(int * epfd, int * socket);&lt;br /&gt;
void delEpollEvent(int * epfd, int * socket);&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
	// Création du descripteur EPOLL&lt;br /&gt;
	int epfd = epoll_create1(0);&lt;br /&gt;
	// Structure contenant les événements EPOLL&lt;br /&gt;
	struct epoll_event events[NB_EVENTS];&lt;br /&gt;
	// Structure contenant l&#039;adresse&lt;br /&gt;
	struct sockaddr_in adresse;&lt;br /&gt;
	initAdresse(&amp;amp;adresse);&lt;br /&gt;
	// Descripteur de la socket du serveur&lt;br /&gt;
	int serverSocket = initSocket(&amp;amp;adresse);&lt;br /&gt;
	addEpollEvent(&amp;amp;epfd, &amp;amp;serverSocket);&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	while (1) {&lt;br /&gt;
		int nfds = epoll_wait(epfd, events, NB_EVENTS, -1);&lt;br /&gt;
		for (int i = 0; i &amp;lt; nfds; i++) {&lt;br /&gt;
			if (events[i].data.fd == serverSocket) {&lt;br /&gt;
				if ((clientSocket = waitForClient(&amp;amp;serverSocket)) != -1) {&lt;br /&gt;
					// On ajoute le nouveau client au tableau des descripteurs&lt;br /&gt;
					addClientToTab(&amp;amp;epfd, &amp;amp;clientSocket);&lt;br /&gt;
				}&lt;br /&gt;
			} else if (events[i].events &amp;amp; EPOLLIN) {&lt;br /&gt;
				// On a reçu quelque chose&lt;br /&gt;
				manageClient(&amp;amp;epfd, &amp;amp;events[i].data.fd);&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	close(serverSocket);&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
// Initialisation de la structure sockaddr_in&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse) {&lt;br /&gt;
	(*adresse).sin_family = AF_INET;&lt;br /&gt;
	(*adresse).sin_addr.s_addr = IP;&lt;br /&gt;
	(*adresse).sin_port = htons( PORT);&lt;br /&gt;
}&lt;br /&gt;
// Démarrage de la socket serveur&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse) {&lt;br /&gt;
	// Descripteur de socket&lt;br /&gt;
	int fdsocket;&lt;br /&gt;
	// Nombre d&#039;options&lt;br /&gt;
	int opt = 1;&lt;br /&gt;
	// Création de la socket en TCP&lt;br /&gt;
	if ((fdsocket = socket(AF_INET, SOCK_STREAM, 0)) == -1) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Création de la socket\n&amp;quot;);&lt;br /&gt;
	// Paramètrage de la socket&lt;br /&gt;
	if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,&lt;br /&gt;
			sizeof(opt)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Paramètrage de la socket\n&amp;quot;);&lt;br /&gt;
	// Attachement de la socket sur le port et l&#039;adresse IP&lt;br /&gt;
	if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Attachement de la socket sur le port %i\n&amp;quot;, PORT);&lt;br /&gt;
	// Passage en écoute de la socket&lt;br /&gt;
	if (listen(fdsocket, BACKLOG) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la mise en écoute: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	// Passage en mode non bloquant&lt;br /&gt;
	fcntl(fdsocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
	printf(&amp;quot;Mise en écoute de la socket\n&amp;quot;);&lt;br /&gt;
	return fdsocket;&lt;br /&gt;
}&lt;br /&gt;
// Attente de connexion d&#039;un client&lt;br /&gt;
int waitForClient(int * serverSocket) {&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	// Structure contenant l&#039;adresse du client&lt;br /&gt;
	struct sockaddr_in clientAdresse;&lt;br /&gt;
	int addrLen = sizeof(clientAdresse);&lt;br /&gt;
	if ((clientSocket = accept(*serverSocket, (struct sockaddr*) &amp;amp;clientAdresse,&lt;br /&gt;
			(socklen_t*) &amp;amp;addrLen)) != -1) {&lt;br /&gt;
		// Convertion de l&#039;IP en texte&lt;br /&gt;
		char ip[INET_ADDRSTRLEN];&lt;br /&gt;
		inet_ntop(AF_INET, &amp;amp;(clientAdresse.sin_addr), ip, INET_ADDRSTRLEN);&lt;br /&gt;
		printf(&amp;quot;Connexion de %s:%i\n&amp;quot;, ip, clientAdresse.sin_port);&lt;br /&gt;
		// Passage en mode non bloquant&lt;br /&gt;
		int opt = 1;&lt;br /&gt;
		setsockopt(clientSocket, SOL_SOCKET, SO_KEEPALIVE, &amp;amp;opt, sizeof(1));&lt;br /&gt;
	}&lt;br /&gt;
	return clientSocket;&lt;br /&gt;
}&lt;br /&gt;
// Ajoute les clients au tableau&lt;br /&gt;
void addClientToTab(int * epfd, int * clientSocket) {&lt;br /&gt;
	addEpollEvent(epfd, clientSocket);&lt;br /&gt;
	// Nouvelle connexion, envoie du message de bienvenu&lt;br /&gt;
	send(*clientSocket, &amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;,&lt;br /&gt;
			strlen(&amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;), MSG_DONTWAIT);&lt;br /&gt;
}&lt;br /&gt;
// On traite l&#039;input des clients&lt;br /&gt;
void manageClient(int * epfd, int * clientSocket) {&lt;br /&gt;
	// Création d&#039;un tampon pour stocker les messages des clients dans la heap&lt;br /&gt;
	static char buffer[BUFFER_LEN + 1];&lt;br /&gt;
	int len = recv(*clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
	// Booléen pour suivre l&#039;état de la socket&lt;br /&gt;
	int isClosed = 0;&lt;br /&gt;
	if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
		// Une erreur est survenue&lt;br /&gt;
		printf(&amp;quot;Errno [%i] : %s\n&amp;quot;, errno, strerror(errno));&lt;br /&gt;
		isClosed = 1;&lt;br /&gt;
	} else if (len == 0) {&lt;br /&gt;
		// Le client s&#039;est déconnecté (extrémité de la socket fermée)&lt;br /&gt;
		isClosed = 1;&lt;br /&gt;
	} else if (len &amp;gt; 0) {&lt;br /&gt;
		// Ajout du terminateur de chaîne&lt;br /&gt;
		buffer[len] = &#039;\0&#039;;&lt;br /&gt;
		if (strncmp(buffer, EXIT_WORD, 4) == 0) {&lt;br /&gt;
			// Le client veut se déconnecter&lt;br /&gt;
			send(*clientSocket, &amp;quot;Bye\n&amp;quot;, strlen(&amp;quot;Bye\n&amp;quot;), 0);&lt;br /&gt;
			isClosed = 1;&lt;br /&gt;
		} else {&lt;br /&gt;
			// On renvoie le texte au client dans un buffer assez grand&lt;br /&gt;
			int len = strlen(&amp;quot;Vous avez dit : &amp;quot;) + strlen(buffer) + 1;&lt;br /&gt;
			char response[len];&lt;br /&gt;
			strcpy(response, &amp;quot;Vous avez dit : &amp;quot;);&lt;br /&gt;
			strcat(response, buffer);&lt;br /&gt;
			// Un seul envoie permet de ne pas surcharger le réseau&lt;br /&gt;
			send(*clientSocket, response, strlen(response), 0);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	if (isClosed == 1) {&lt;br /&gt;
		// On enlève la socket des événements EPOLL&lt;br /&gt;
		delEpollEvent(epfd, clientSocket);&lt;br /&gt;
		// La socket est fermé ou le client veut quitter le serveur !&lt;br /&gt;
		printf(&amp;quot;Fermeture de la connexion avec le client\n&amp;quot;);&lt;br /&gt;
		// Fermeture de la socket&lt;br /&gt;
		close(*clientSocket);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// Ajoute un descripteur de socket aux événements EPOLL&lt;br /&gt;
void addEpollEvent(int * epfd, int * socket) {&lt;br /&gt;
	printf(&amp;quot;Ajout de la socket %i dans les événements EPOLL\n&amp;quot;, *socket);&lt;br /&gt;
	// Structure contenant les événements EPOLL&lt;br /&gt;
	struct epoll_event event;&lt;br /&gt;
	// On enregistre la socket du serveur comme descripteur EPOLL&lt;br /&gt;
	event.data.fd = *socket;&lt;br /&gt;
	// On surveille les message entrant, la fermeture de la socket&lt;br /&gt;
	event.events = EPOLLIN | EPOLLRDHUP;&lt;br /&gt;
	epoll_ctl(*epfd, EPOLL_CTL_ADD, *socket, &amp;amp;event);&lt;br /&gt;
}&lt;br /&gt;
// Efface un descripteur de socket des événements EPOLL&lt;br /&gt;
void delEpollEvent(int * epfd, int * socket) {&lt;br /&gt;
	printf(&amp;quot;Suppression de la socket %i des événements EPOLL\n&amp;quot;, *socket);&lt;br /&gt;
	// Structure contenant les événements EPOLL&lt;br /&gt;
	struct epoll_event event;&lt;br /&gt;
	// On enregistre la socket du serveur comme descripteur EPOLL&lt;br /&gt;
	event.data.fd = *socket;&lt;br /&gt;
	// On surveille les message entrant, la fermeture de la socket&lt;br /&gt;
	event.events = EPOLLIN | EPOLLRDHUP;&lt;br /&gt;
	epoll_ctl(*epfd, EPOLL_CTL_DEL, *socket, &amp;amp;event);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Voici ce que l&#039;on peut observer dans la console:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Création de la socket&lt;br /&gt;
Paramètrage de la socket&lt;br /&gt;
Attachement de la socket sur le port 8080&lt;br /&gt;
Mise en écoute de la socket&lt;br /&gt;
Ajout de la socket 4 dans les événements EPOLL&lt;br /&gt;
Connexion de 127.0.0.1:17607&lt;br /&gt;
Ajout de la socket 5 dans les événements EPOLL&lt;br /&gt;
Suppression de la socket 5 des événements EPOLL&lt;br /&gt;
Fermeture de la connexion avec le client&lt;br /&gt;
Connexion de 127.0.0.1:18119&lt;br /&gt;
Ajout de la socket 5 dans les événements EPOLL&lt;br /&gt;
Connexion de 127.0.0.1:18631&lt;br /&gt;
Ajout de la socket 6 dans les événements EPOLL&lt;br /&gt;
Suppression de la socket 5 des événements EPOLL&lt;br /&gt;
Fermeture de la connexion avec le client&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Magsss</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=C_socket&amp;diff=3284</id>
		<title>C socket</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=C_socket&amp;diff=3284"/>
		<updated>2018-11-01T18:28:22Z</updated>

		<summary type="html">&lt;p&gt;Magsss : /* Serveur UDP */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
||&lt;br /&gt;
Les sockets permettent de connecter deux programmes qui s&#039;exécutent sur deux machines différentes. Cette connexion se fait à travers le réseau grâce à l&#039;utilisation d&#039;un port (TCP ou UDP) et d&#039;une adresse IP. &lt;br /&gt;
&lt;br /&gt;
Il y a deux types de sockets, une qui est démarrée par la partie serveur en &#039;&#039;écoute&#039;&#039; et l&#039;autre démarrée par la partie cliente qui se connecte à la première.&lt;br /&gt;
&lt;br /&gt;
Ci-contre une image résumant les différentes étapes pour arriver à l&#039;envoi de données.&lt;br /&gt;
||&lt;br /&gt;
[[Fichier:Socket workflow.png|centré|200px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Création  =&lt;br /&gt;
== Côté serveur ==&lt;br /&gt;
Tout d&#039;abord il faut créer l&#039;objet socket:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt; &lt;br /&gt;
&lt;br /&gt;
int socket(int domain, int type, int protocol)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*domain &amp;amp;rarr; integer, communication domain e.g., AF_INET (IPv4 protocol) , AF_INET6 (IPv6 protocol)&lt;br /&gt;
*type &amp;amp;rarr; type de communication&lt;br /&gt;
**SOCK_STREAM: TCP&lt;br /&gt;
**SOCK_DGRAM: UDP&lt;br /&gt;
**SOCK_RAW: socket à l&#039;état brut (bas niveau)&lt;br /&gt;
*protocol &amp;amp;rarr; valeur du champ &#039;&#039;protocol&#039;&#039; de l&#039;en-tête de niveau 3 (généralement &#039;&#039;0&#039;&#039;)&lt;br /&gt;
*la valeur de retour est le fichier descripteur de la socket ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Une fois le descripteur de socket créé, il est possible de le configurer:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt; &lt;br /&gt;
&lt;br /&gt;
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* level &amp;amp;rarr; niveau d&#039;application de l&#039;option (SOL_SOCKET, TCP, UDP, ...);&lt;br /&gt;
* optname &amp;amp;rarr; le nom de l&#039;option (&#039;&#039;SO_REUSEADDR&#039;&#039;, &#039;&#039;SO_REUSEPORT&#039;&#039;, &#039;&#039;SO_KEEPALIVE&#039;&#039;, ...)&lt;br /&gt;
* optval, optlen  &amp;amp;rarr; utilisé pour accéder aux options de la socket;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
Il existe plusieurs options de socket, les plus utilisées étant :&lt;br /&gt;
* &#039;&#039;SO_REUSEADDR&#039;&#039; &amp;amp;rarr; permet de réutiliser l&#039;adresse de la socket tout de suite même si cette dernière est dans l&#039;état &#039;&#039;wait&#039;&#039;;&lt;br /&gt;
* &#039;&#039;SO_REUSEPORT&#039;&#039; &amp;amp;rarr; permet de réutiliser le port de la socket tout de suite même si cette dernière est dans l&#039;état &#039;&#039;wait&#039;&#039;;&lt;br /&gt;
* &#039;&#039;SO_KEEPALIVE&#039;&#039; &amp;amp;rarr; envoie des informations périodiquement pour tester si l&#039;extrémité du tunnel est toujours présente;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Il faut maintenant attacher la socket à un port Internet et une adresse IP:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* addr &amp;amp;rarr; une structure symbolisant l&#039;adresse &lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
struct sockaddr_in {&lt;br /&gt;
    short            sin_family;   // e.g: AF_INET&lt;br /&gt;
    unsigned short   sin_port;     // e.g: htons(3490)&lt;br /&gt;
    struct in_addr   sin_addr;     // détaillé ci-dessous&lt;br /&gt;
    char             sin_zero[8];  // &#039;0&#039; habituellement&lt;br /&gt;
};&lt;br /&gt;
struct in_addr {&lt;br /&gt;
    unsigned long s_addr;  // initialiser avec inet_aton()&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On peut maintenant passer la socket en état d&#039;écoute, elle est prête à recevoir des connexions:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int listen(int sockfd, int backlog)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* backlog &amp;amp;rarr; taille maximum de la file d&#039;attente de la socket après laquelle le système répond avec &#039;&#039;ECONNREFUSED&#039;&#039;;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On peut maintenant attendre une connexion:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* addr &amp;amp;rarr; l&#039;adresse du client;&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de la structure addr;&lt;br /&gt;
* le code retour varie entre un entier positif qui correspond au descripteur de la socket cliente et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
== Côté client ==&lt;br /&gt;
Après avoir créé la socket avec &#039;&#039;socket&#039;&#039; on peut se connecter à la partie serveur:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* addr &amp;amp;rarr; une structure symbolisant l&#039;adresse (cf. ci-dessus)&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
= Lecture / écriture =&lt;br /&gt;
== Fonctions génériques ==&lt;br /&gt;
Tout d&#039;abord, on peut dire que la socket est un fichier et se manipule donc comme un fichier !&lt;br /&gt;
===Lecture===&lt;br /&gt;
Pour lire dans une socket on peut utiliser &#039;&#039;read&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t read(int fd, void *buf, size_t count);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères où mettre le message reçu;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à recevoir;&lt;br /&gt;
* le code retour correspond au nombre de caractères reçus ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
===Écriture===&lt;br /&gt;
Pour écrire dans une socket ou peut utiliser &#039;&#039;write&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t write(int fd, const void *buf, size_t count);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères contenant le message à envoyer;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à envoyer;&lt;br /&gt;
* le code retour correspond au nombre de caractères envoyés ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
===Modification===&lt;br /&gt;
Pour modifier un descripteur fichier de socket on utilise &#039;&#039;fcntl&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int fcntl(int fd, int cmd, ... /* int args */ );&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* cmd &amp;amp;rarr; la commande de modification (descripteur, drapeaux, ...);&lt;br /&gt;
* args &amp;amp;rarr; les arguments correspondant à la commande&lt;br /&gt;
&lt;br /&gt;
Cette commande va nous permettre de modifier le drapeau du descripteur de la socket (&#039;&#039;F_SETFL&#039;&#039;) pour le rendre non bloquant (&#039;&#039;O_NONBLOCK&#039;&#039;) !&lt;br /&gt;
&lt;br /&gt;
== Fonctions spécifiques ==&lt;br /&gt;
Il existe des fonctions spécifiques pour manipuler les sockets. Ces fonctions ressemblent aux précédentes mais acceptent des drapeaux en plus pour pouvoir modifier le descripteur &#039;&#039;à la volée&#039;&#039;.&lt;br /&gt;
===Lecture===&lt;br /&gt;
Pour lire dans une socket on peut utiliser &#039;&#039;recv&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t recv(int fd, void *buf, size_t count, int flags);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères où mettre le message reçu;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à recevoir;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;O_NONBLOCK&#039;&#039;);&lt;br /&gt;
* le code retour correspond au nombre de caractères reçus ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
===Écriture===&lt;br /&gt;
Pour écrire dans une socket ou peut utiliser &#039;&#039;send&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t send(int fd, const void *buf, size_t count, int flags);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères contenant le message à envoyer;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à envoyer;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;MSG_CONFIRM&#039;&#039;, &#039;&#039;MSG_DONTWAIT &#039;&#039;, ...);&lt;br /&gt;
* le code retour correspond au nombre de caractères envoyés ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
= Cas d&#039;utilisation =&lt;br /&gt;
== Création ==&lt;br /&gt;
Pour commencer, il faut créer le descripteur de fichier:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int fdsocket;&lt;br /&gt;
if ((fdsocket = socket(AF_INET, SOCK_STREAM, 0)) == -1) {&lt;br /&gt;
	printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Paramétrage des options ==&lt;br /&gt;
Il faut maintenant configurer la réutilisation de l&#039;adresse et du port&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int opt = 1;&lt;br /&gt;
if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,	sizeof(opt)) != 0) {&lt;br /&gt;
	printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Attachement ==&lt;br /&gt;
On peut maintenant attacher la socket à un port et une adresse:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
struct sockaddr_in adresse;&lt;br /&gt;
&lt;br /&gt;
adresse.sin_family = AF_INET;&lt;br /&gt;
// Ecoute sur toutes les adresses (INADDR_ANY &amp;lt;=&amp;gt; 0.0.0.0)&lt;br /&gt;
adresse.sin_addr.s_addr = INADDR_ANY;&lt;br /&gt;
// Conversion du port en valeur réseaux (Host TO Network Short)&lt;br /&gt;
adresse.sin_port = htons(8080);&lt;br /&gt;
&lt;br /&gt;
if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
	printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mise en écoute ==&lt;br /&gt;
La socket est prête à passer à l&#039;écoute des connexions des clients:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
&lt;br /&gt;
if (listen(fdsocket, BACKLOG) != 0) {&lt;br /&gt;
	printf(&amp;quot;Echéc de la mise en écoute: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Acceptation des connexions ==&lt;br /&gt;
Il est possible d&#039;accepter les nouvelles connexions:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int clientSocket;&lt;br /&gt;
// Structure contenant l&#039;adresse du client&lt;br /&gt;
struct sockaddr_in clientAdresse;&lt;br /&gt;
int addrLen = sizeof(clientAdresse);&lt;br /&gt;
if ((clientSocket = accept(*serverSocket, (struct sockaddr*) &amp;amp;clientAdresse, (socklen_t*) &amp;amp;addrLen)) != -1) {&lt;br /&gt;
	// Convertion de l&#039;IP en texte&lt;br /&gt;
	char ip[INET_ADDRSTRLEN];&lt;br /&gt;
	inet_ntop(AF_INET, &amp;amp;(clientAdresse.sin_addr), ip, INET_ADDRSTRLEN);&lt;br /&gt;
	printf(&amp;quot;Connexion de %s:%i\n&amp;quot;, ip, clientAdresse.sin_port);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Il est possible d&#039;utiliser &#039;&#039;fcntl&#039;&#039; pour passer le descripteur de la socket en non bloquant, avant l&#039;appel à la fonction &#039;&#039;accept&#039;&#039;. Si le descripteur reste bloquant, la fonction &#039;&#039;accept&#039;&#039; ne rendra pas la main tant qu&#039;un client ne se sera connecté, ayant pour résultat le blocage complet du programme !&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Passage en mode non bloquant&lt;br /&gt;
fcntl(fdsocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
On peut remarquer, au passage, l&#039;utilisation de &#039;&#039;inet_ntop&#039;&#039; pour convertir l&#039;adresse du client du format binaire au format text.&lt;br /&gt;
&lt;br /&gt;
== Lecture / Écriture ==&lt;br /&gt;
=== Mode connecté (TCP) ===&lt;br /&gt;
On peut maintenant utiliser le descripteur de la socket du client pour lire et écrire.&lt;br /&gt;
* avec les fonctions génériques:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Descripteur de la socket du client&lt;br /&gt;
int clientSocket;&lt;br /&gt;
char buffer[BUFFER_LEN];&lt;br /&gt;
// Passage en mode non bloquant, sinon read attend&lt;br /&gt;
fcntl(clientSocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
int len = read(clientSocket, buffer, BUFFER_LEN);&lt;br /&gt;
&lt;br /&gt;
send(clientSocket, &amp;quot;Coucou\n&amp;quot;, strlen(&amp;quot;Coucou\n&amp;quot;));&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* avec les fonctions spécifiques:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Descripteur de la socket du client&lt;br /&gt;
int clientSocket;&lt;br /&gt;
char buffer[BUFFER_LEN];&lt;br /&gt;
&lt;br /&gt;
int len = recv(clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
&lt;br /&gt;
send(clientSocket, &amp;quot;Coucou\n&amp;quot;, strlen(&amp;quot;Coucou\n&amp;quot;), MSG_DONTWAIT);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Analysons le retour de la fonction &#039;&#039;read&#039;&#039; ou &#039;&#039;recv&#039;&#039; lorsque l&#039;on utilise un descripteur non bloquant d&#039;après la [http://man7.org/linux/man-pages/man2/recvmsg.2.html documentation] :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
NAME &lt;br /&gt;
       recv, recvfrom, recvmsg - receive a message from a socket&lt;br /&gt;
[...]&lt;br /&gt;
MSG_DONTWAIT (since Linux 2.2)&lt;br /&gt;
    Enables nonblocking operation; if the operation would block, the call fails with the error EAGAIN or EWOULDBLOCK (this can also be enabled using the O_NONBLOCK flag with the F_SETFL fcntl(2)). &lt;br /&gt;
[...]&lt;br /&gt;
RETURN VALUE&lt;br /&gt;
[...]&lt;br /&gt;
These calls return the number of bytes received, or -1 if an error&lt;br /&gt;
       occurred.  In the event of an error, errno is set to indicate the&lt;br /&gt;
       error.&lt;br /&gt;
When a stream socket peer has performed an orderly shutdown, the&lt;br /&gt;
       return value will be 0 (the traditional &amp;quot;end-of-file&amp;quot; return).&lt;br /&gt;
[...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Le retour est :&lt;br /&gt;
* lorsque le descripteur est vide (aucune donnée de la part du client) &#039;&#039;-1&#039;&#039; et &#039;&#039;errno&#039;&#039; sera positionné à la valeur &#039;&#039;EAGAIN&#039;&#039;, il ne faut donc pas fermer la socket si le retour est &#039;&#039;-1&#039;&#039; ! Il faut d&#039;abord vérifier que &#039;&#039;errno&#039;&#039; &#039;&#039;&#039;n&#039;a pas&#039;&#039;&#039; la valeur &#039;&#039;EAGAIN&#039;&#039;;&lt;br /&gt;
* lorsque le retour est &#039;&#039;0&#039;&#039;, cela signifie que l&#039;extrémité est fermée, il faut donc fermer le descripteur local;&lt;br /&gt;
* lorsque la valeur est supérieure à &#039;&#039;0&#039;&#039; elle correspond au nombre d&#039;octets reçus.&lt;br /&gt;
&lt;br /&gt;
On peut donc en déduire le morceau de code suivant pour gérer le retour de &#039;&#039;read&#039;&#039; ou &#039;&#039;recv&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int len = recv(clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
&lt;br /&gt;
if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
	// Une erreur est survenue&lt;br /&gt;
} else if (len == 0) {&lt;br /&gt;
	// Le client s&#039;est déconnecté (extrémité de la socket fermée)			&lt;br /&gt;
} else if (len &amp;gt; 0) {&lt;br /&gt;
	// Le client à envoyé des données	&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Mode datagramme (UDP) ===&lt;br /&gt;
===Lecture===&lt;br /&gt;
Pour lire dans une socket on peut utiliser &#039;&#039;recvfrom&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t recvfrom(int fd, void *buf, size_t count, int flags, const struct sockaddr *addr, socklen_t addrlen);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères où mettre le message reçu;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à recevoir;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;O_NONBLOCK&#039;&#039;);&lt;br /&gt;
*  addr &amp;amp;rarr; une structure symbolisant l&#039;adresse du client (cf. ci-dessus)&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour correspond au nombre de caractères reçus ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
===Écriture===&lt;br /&gt;
Pour écrire dans une socket ou peut utiliser &#039;&#039;sendto&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t sendto(int fd, const void *buf, size_t count, const struct sockaddr *addr, socklen_t addrlen);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères contenant le message à envoyer;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à envoyer;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;MSG_DONTWAIT &#039;&#039;, ...);&lt;br /&gt;
* addr &amp;amp;rarr; une structure symbolisant l&#039;adresse du client (cf. ci-dessus)&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour correspond au nombre de caractères envoyer ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
= Exemples =&lt;br /&gt;
== Serveur TCP ==&lt;br /&gt;
Voici un exemple de serveur &#039;&#039;echo&#039;&#039; qui renvoie le message au client. Les connexions utilisent TCP et un simple client comme telnet suffit pour l&#039;utiliser.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;arpa/inet.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Port d&#039;écoute de la socket&lt;br /&gt;
#define PORT 8080&lt;br /&gt;
// Adresse d&#039;écoute (toutes les adresses)&lt;br /&gt;
#define IP INADDR_ANY&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
// Nombre de connexions clients&lt;br /&gt;
#define NB_CLIENTS 2&lt;br /&gt;
// Taille du tampon de lecture des messages&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Commande pour arrêter le serveur&lt;br /&gt;
#define EXIT_WORD &amp;quot;exit&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse);&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse);&lt;br /&gt;
int waitForClient(int * serverSocket);&lt;br /&gt;
void addClientToTab(int clientSocket, int clients[]);&lt;br /&gt;
void manageClient(int clients[]);&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
	// Création et initialisation du tableau contenant les descripteurs des sockets clients&lt;br /&gt;
	int clients[NB_CLIENTS];&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_CLIENTS; i++) {&lt;br /&gt;
		clients[i] = -1;&lt;br /&gt;
	}&lt;br /&gt;
	// Structure contenant l&#039;adresse&lt;br /&gt;
	struct sockaddr_in adresse;&lt;br /&gt;
	initAdresse(&amp;amp;adresse);&lt;br /&gt;
	// Descripteur de la socket du serveur&lt;br /&gt;
	int serverSocket = initSocket(&amp;amp;adresse);&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	while (1) {&lt;br /&gt;
		// Descripteur de la socket du client, on attend une connexion&lt;br /&gt;
		if ((clientSocket = waitForClient(&amp;amp;serverSocket)) != -1) {&lt;br /&gt;
			// On ajoute le nouveau client au tableau des descripteurs&lt;br /&gt;
			addClientToTab(clientSocket, clients);&lt;br /&gt;
		}&lt;br /&gt;
		manageClient(clients);&lt;br /&gt;
	}&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
// Initialisation de la structure sockaddr_in&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse) {&lt;br /&gt;
	(*adresse).sin_family = AF_INET;&lt;br /&gt;
	(*adresse).sin_addr.s_addr = IP;&lt;br /&gt;
	(*adresse).sin_port = htons( PORT);&lt;br /&gt;
}&lt;br /&gt;
// Démarrage de la socket serveur&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse) {&lt;br /&gt;
	// Descripteur de socket&lt;br /&gt;
	int fdsocket;&lt;br /&gt;
	// Nombre d&#039;options&lt;br /&gt;
	int opt = 1;&lt;br /&gt;
	// Création de la socket en TCP&lt;br /&gt;
	if ((fdsocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Création de la socket\n&amp;quot;);&lt;br /&gt;
	// Paramètrage de la socket&lt;br /&gt;
	if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,&lt;br /&gt;
			sizeof(opt)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Paramètrage de la socket\n&amp;quot;);&lt;br /&gt;
	// Attachement de la socket sur le port et l&#039;adresse IP&lt;br /&gt;
	if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Attachement de la socket sur le port %i\n&amp;quot;, PORT);&lt;br /&gt;
	// Passage en écoute de la socket&lt;br /&gt;
	if (listen(fdsocket, BACKLOG) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la mise en écoute: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	// Passage en mode non bloquant&lt;br /&gt;
	fcntl(fdsocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
	printf(&amp;quot;Mise en écoute de la socket\n&amp;quot;);&lt;br /&gt;
	return fdsocket;&lt;br /&gt;
}&lt;br /&gt;
// Attente de connexion d&#039;un client&lt;br /&gt;
int waitForClient(int * serverSocket) {&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	// Structure contenant l&#039;adresse du client&lt;br /&gt;
	struct sockaddr_in clientAdresse;&lt;br /&gt;
	int addrLen = sizeof(clientAdresse);&lt;br /&gt;
	if ((clientSocket = accept(*serverSocket, (struct sockaddr*) &amp;amp;clientAdresse,&lt;br /&gt;
			(socklen_t*) &amp;amp;addrLen)) != -1) {&lt;br /&gt;
		// Convertion de l&#039;IP en texte&lt;br /&gt;
		char ip[INET_ADDRSTRLEN];&lt;br /&gt;
		inet_ntop(AF_INET, &amp;amp;(clientAdresse.sin_addr), ip, INET_ADDRSTRLEN);&lt;br /&gt;
		printf(&amp;quot;Connexion de %s:%i\n&amp;quot;, ip, clientAdresse.sin_port);&lt;br /&gt;
		// Passage en mode non bloquant&lt;br /&gt;
		int opt = 1;&lt;br /&gt;
		setsockopt(clientSocket, SOL_SOCKET, SO_KEEPALIVE, &amp;amp;opt, sizeof(1));&lt;br /&gt;
	}&lt;br /&gt;
	return clientSocket;&lt;br /&gt;
}&lt;br /&gt;
// Ajoute les clients au tableau&lt;br /&gt;
void addClientToTab(int clientSocket, int clients[]) {&lt;br /&gt;
	// On vérifie si on à de la place de libre&lt;br /&gt;
	int found = -1;&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_CLIENTS; i++) {&lt;br /&gt;
		// On cherche de la place&lt;br /&gt;
		if (clients[i] == -1) {&lt;br /&gt;
			// On ajoute le client&lt;br /&gt;
			printf(&amp;quot;Ajout du client à l&#039;index %i\n&amp;quot;, i);&lt;br /&gt;
			clients[i] = clientSocket;&lt;br /&gt;
			// Nouvelle connexion, envoie du message de bienvenu&lt;br /&gt;
			send(clientSocket, &amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;, 	strlen(&amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;),&lt;br /&gt;
					MSG_DONTWAIT);&lt;br /&gt;
			found = 0;&lt;br /&gt;
			break;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	if (found == -1) {&lt;br /&gt;
		// On a plus de place de libre&lt;br /&gt;
		puts(&amp;quot;Plus de place, désolé&amp;quot;);&lt;br /&gt;
		close(clientSocket);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// On traite l&#039;input des clients&lt;br /&gt;
void manageClient(int clients[]) {&lt;br /&gt;
	// Création d&#039;un tampon pour stocker les messages des clients dans la heap&lt;br /&gt;
	static char buffer[BUFFER_LEN + 1];&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_CLIENTS; i++) {&lt;br /&gt;
		// On vérifie les messages&lt;br /&gt;
		int clientSocket = clients[i];&lt;br /&gt;
		if (clientSocket == -1) {&lt;br /&gt;
			continue;&lt;br /&gt;
		}&lt;br /&gt;
		int len = recv(clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
		// Booléen pour suivre l&#039;état de la socket&lt;br /&gt;
		int isClosed = 0;&lt;br /&gt;
		if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
			// Une erreur est survenue&lt;br /&gt;
			printf(&amp;quot;Errno [%i] : %s\n&amp;quot;, errno, strerror(errno));&lt;br /&gt;
			isClosed = 1;&lt;br /&gt;
		} else if (len == 0) {&lt;br /&gt;
			// Le client s&#039;est déconnecté (extrémité de la socket fermée)&lt;br /&gt;
			isClosed = 1;&lt;br /&gt;
		} else if (len &amp;gt; 0) {&lt;br /&gt;
			// Ajout du terminateur de chaîne&lt;br /&gt;
			buffer[len] = &#039;\0&#039;;&lt;br /&gt;
			if (strncmp(buffer, EXIT_WORD, 4) == 0) {&lt;br /&gt;
				// Le client veut se déconnecter&lt;br /&gt;
				send(clientSocket, &amp;quot;Bye\n&amp;quot;, strlen(&amp;quot;Bye\n&amp;quot;), 0);&lt;br /&gt;
				isClosed = 1;&lt;br /&gt;
			} else {&lt;br /&gt;
				// On renvoie le texte au client dans un buffer assez grand&lt;br /&gt;
				int len = strlen(&amp;quot;Vous avez dit : &amp;quot;) + strlen(buffer) + 1;&lt;br /&gt;
				char response[len];&lt;br /&gt;
				strcpy(response, &amp;quot;Vous avez dit : &amp;quot;);&lt;br /&gt;
				strcat(response, buffer);&lt;br /&gt;
				// Un seul envoie permet de ne pas surcharger le réseau&lt;br /&gt;
				send(clientSocket, response, strlen(response), 0);&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
		if (isClosed == 1) {&lt;br /&gt;
			// La socket est fermé ou le client veut quitter le serveur !&lt;br /&gt;
			printf(&amp;quot;Fermeture de la connexion avec le client\n&amp;quot;);&lt;br /&gt;
			// Fermeture de la socket&lt;br /&gt;
			close(clientSocket);&lt;br /&gt;
			// On fait de la place dans le tableau&lt;br /&gt;
			clients[i] = -1;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On peut interroger notre serveur avec &#039;&#039;telnet&#039;&#039; (&#039;&#039;yum -y install telnet&#039;&#039;):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# telnet 127.0.0.1 8080&lt;br /&gt;
Trying 127.0.0.1...&lt;br /&gt;
Connected to 127.0.0.1.&lt;br /&gt;
Escape character is &#039;^]&#039;.&lt;br /&gt;
Entrez &#039;exit&#039; pour quitter&lt;br /&gt;
coucou&lt;br /&gt;
Vous avez dit : coucou&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== Serveur UDP ==&lt;br /&gt;
Si on essaie de reproduire le même type de serveur mais cette fois-ci avec une socket en UDP, cela devient beaucoup plus simple. En effet, en UDP, plus besoin de tenir un tableau avec les descripteurs des sockets des clients car... on est en mode déconnecté (datagramme) ! Entendez par là que les clients envoient un message sans attendre de réponse et sans se soucier de savoir si le message est arrivé au serveur.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;arpa/inet.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Port d&#039;écoute de la socket&lt;br /&gt;
#define PORT 8080&lt;br /&gt;
// Adresse d&#039;écoute (toutes les adresses)&lt;br /&gt;
#define IP INADDR_ANY&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
// Taille du tampon de lecture des messages&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse);&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse);&lt;br /&gt;
void manageClient(int serverSocket);&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
	// Structure contenant l&#039;adresse&lt;br /&gt;
	struct sockaddr_in adresse;&lt;br /&gt;
	initAdresse(&amp;amp;adresse);&lt;br /&gt;
	// Descripteur de la socket du serveur&lt;br /&gt;
	int serverSocket = initSocket(&amp;amp;adresse);&lt;br /&gt;
	while (1) {&lt;br /&gt;
		// Descripteur de la socket du client, on attend une connexion&lt;br /&gt;
		manageClient(serverSocket);&lt;br /&gt;
	}&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
// Initialisation de la structure sockaddr_in&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse) {&lt;br /&gt;
	(*adresse).sin_family = AF_INET;&lt;br /&gt;
	(*adresse).sin_addr.s_addr = IP;&lt;br /&gt;
	(*adresse).sin_port = htons( PORT);&lt;br /&gt;
}&lt;br /&gt;
// Démarrage de la socket serveur&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse) {&lt;br /&gt;
	// Descripteur de socket&lt;br /&gt;
	int fdsocket;&lt;br /&gt;
	// Nombre d&#039;options&lt;br /&gt;
	int opt = 1;&lt;br /&gt;
	// Création de la socket en UDP&lt;br /&gt;
	if ((fdsocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Création de la socket\n&amp;quot;);&lt;br /&gt;
	// Paramètrage de la socket&lt;br /&gt;
	if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,&lt;br /&gt;
			sizeof(opt)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Paramètrage de la socket\n&amp;quot;);&lt;br /&gt;
	// Attachement de la socket sur le port et l&#039;adresse IP&lt;br /&gt;
	if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Attachement de la socket sur le port %i\n&amp;quot;, PORT);&lt;br /&gt;
	return fdsocket;&lt;br /&gt;
}&lt;br /&gt;
// On traite l&#039;input des clients&lt;br /&gt;
void manageClient(int serverSocket) {&lt;br /&gt;
	// Création d&#039;un tampon pour stocker les messages des clients dans la heap&lt;br /&gt;
	static char buffer[BUFFER_LEN + 1];&lt;br /&gt;
	// Structure contenant l&#039;adresse du client&lt;br /&gt;
	struct sockaddr_in clientAdresse;&lt;br /&gt;
	unsigned int addrLen = sizeof(clientAdresse);&lt;br /&gt;
	// On vérifie si on a reçu un message&lt;br /&gt;
	int len = recvfrom(serverSocket, buffer, BUFFER_LEN, MSG_DONTWAIT,&lt;br /&gt;
			(struct sockaddr*) &amp;amp;clientAdresse, &amp;amp;addrLen);&lt;br /&gt;
	if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
		printf(&amp;quot;Problème de socket %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	} else if (len &amp;gt; 0) {&lt;br /&gt;
		printf(&amp;quot;Message reçu de %s:%i\n&amp;quot;, inet_ntoa(clientAdresse.sin_addr),&lt;br /&gt;
				ntohs(clientAdresse.sin_port));&lt;br /&gt;
		// Ajout du terminateur de chaîne&lt;br /&gt;
		buffer[len] = &#039;\0&#039;;&lt;br /&gt;
		// On renvoie le texte au client dans un buffer assez grand&lt;br /&gt;
		len = strlen(&amp;quot;Vous avez dit : &amp;quot;) + strlen(buffer) + 1;&lt;br /&gt;
		char response[len];&lt;br /&gt;
		strcpy(response, &amp;quot;Vous avez dit : &amp;quot;);&lt;br /&gt;
		strcat(response, buffer);&lt;br /&gt;
		// Un seul envoie permet de ne pas surcharger le réseau&lt;br /&gt;
		sendto(serverSocket, response, strlen(response), MSG_DONTWAIT,&lt;br /&gt;
				(struct sockaddr*) &amp;amp;clientAdresse, addrLen);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
On peut interroger notre serveur avec &#039;&#039;netcat&#039;&#039; (&#039;&#039;yum -y install nc&#039;&#039;):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# nc -u 127.0.0.1 8080&lt;br /&gt;
coucou&lt;br /&gt;
Vous avez dit : coucou&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Notification évènementielle avec epoll =&lt;br /&gt;
Depuis la version 2.5 du noyau Linux il est possible d&#039;utiliser une série de fonctions, appartenant à l&#039;espace utilisateur (user-space), permettant de suivre l&#039;état d&#039;un descripteur de fichier.&lt;br /&gt;
La différence avec read, recv ou encore recvfrom c&#039;est que l&#039;on va pouvoir surveiller plusieurs descripteurs de fichiers en même temps, laissant le CPU libre pour effectuer autre chose !&lt;br /&gt;
&lt;br /&gt;
== Utilisation ==&lt;br /&gt;
Tout d&#039;abord il faut créer un descripteur de fichier de type &#039;&#039;epoll&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int epoll_create1(int flags);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* flags &amp;amp;rarr; EPOLL_CLOEXEC pour éviter les situation de compétition en environnement parallélisé;&lt;br /&gt;
* le code retour correspond au descripteur &#039;&#039;epoll&#039;&#039; ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée).&lt;br /&gt;
&lt;br /&gt;
Une fois le descripteur &#039;&#039;epoll&#039;&#039; créé, il faut lui associer un événement:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* epfd &amp;amp;rarr; le descripteur &#039;&#039;epoll&#039;&#039; à modifier;&lt;br /&gt;
* op &amp;amp;rarr; une opération parmis:&lt;br /&gt;
** EPOLL_CTL_ADD &amp;amp;rarr; pour ajouter un événement sur un descripteur de fichier;&lt;br /&gt;
** EPOLL_CTL_MOD &amp;amp;rarr; pour modifier un événement sur un descripteur de fichier;&lt;br /&gt;
** EPOLL_CTL_DEL &amp;amp;rarr; pour supprimer un événement sur un descripteur de fichier;&lt;br /&gt;
* fd &amp;amp;rarr; un descripteur de fichier à surveiller;&lt;br /&gt;
* event &amp;amp;rarr; la structure contenant l&#039;événement à surveiller (&#039;&#039;EPOLLIN&#039;&#039;, &#039;&#039;EPOLLOUT&#039;&#039;, EPOLLRDHUP&#039;&#039;, ...);&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; en cas de succès ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée).&lt;br /&gt;
&lt;br /&gt;
Voici la structure &#039;&#039;epoll_event&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
struct epoll_event {&lt;br /&gt;
	uint32_t events;    /* Epoll events */&lt;br /&gt;
	epoll_data_t data;  /* User data variable */&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
typedef union epoll_data {&lt;br /&gt;
	void    *ptr;&lt;br /&gt;
	int      fd;&lt;br /&gt;
	uint32_t u32;&lt;br /&gt;
	uint64_t u64;&lt;br /&gt;
} epoll_data_t;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Enfin, il ne reste plus qu&#039;à &#039;&#039;attendre&#039;&#039; des événements:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* epfd &amp;amp;rarr; le descripteur &#039;&#039;epoll&#039;&#039; à modifier;&lt;br /&gt;
* events &amp;amp;rarr; un tableau de &#039;&#039;epoll_event contenant les événements;&lt;br /&gt;
* maxevents &amp;amp;rarr; le nombre maximum d&#039;événements à traiter (selon la taille du tableau);&lt;br /&gt;
* timeout &amp;amp;rarr; le temps, en millisecondes que la fonction attend:&lt;br /&gt;
** &#039;&#039;-1&#039;&#039; &amp;amp;rarr; la fonction devient bloquante;&lt;br /&gt;
** &#039;&#039;0&#039;&#039; &amp;amp;rarr; retourne immédiatement;&lt;br /&gt;
** &#039;&#039;&amp;gt; 0&#039;&#039; &amp;amp;rarr; attend le nombre de millisecondes spécifiées;&lt;br /&gt;
* le code retour correspond au nombre d&#039;événements ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée).&lt;br /&gt;
&lt;br /&gt;
== Améliorations architecturales ==&lt;br /&gt;
=== Gestion des utilisateurs===&lt;br /&gt;
Tout d&#039;abord, le faite de laisser &#039;&#039;epoll&#039;&#039; gérer les descripteurs de socket nous permet de supprimer le tableau &#039;&#039;clients&#039;&#039; ainsi que la variable &#039;&#039;NB_CLIENTS&#039;&#039;. En effet, la référence vers le descripteur est maintenu par &#039;&#039;epoll&#039;&#039; et nous pouvons gérer maintenant un nombre &#039;&#039;illimité&#039;&#039; de clients.&lt;br /&gt;
=== Performances ===&lt;br /&gt;
La boucle &#039;&#039;while&#039;&#039; de notre serveur consomme énormément de CPU, environ 100% de CPU par client ! On aurais pu utiliser un temporisateur avec &#039;&#039;nanosleep&#039;&#039; pour baisser la consommation CPU entre 5% et 10%...&lt;br /&gt;
Le faîte de basculer sur une fonction bloquante comme &#039;&#039;epoll_wait&#039;&#039; qui utilise des signaux (interruptions) permet de réduire considérablement la consommation CPU. Gardez à l&#039;esprit qu&#039;il n&#039;y a rien de mieux que les interruptions, la preuve, lorsque l&#039;on fait un &#039;&#039;top&#039;&#039;, notre programme à complétement disparu de la liste !&lt;br /&gt;
&lt;br /&gt;
== Modification du serveur ==&lt;br /&gt;
Modifions le code du serveur TCP pour y intégrer &#039;&#039;epoll&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;arpa/inet.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Port d&#039;écoute de la socket&lt;br /&gt;
#define PORT 8080&lt;br /&gt;
// Adresse d&#039;écoute (toutes les adresses)&lt;br /&gt;
#define IP INADDR_ANY&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
// Nombre de connexions clients&lt;br /&gt;
#define NB_EVENTS 2&lt;br /&gt;
// Taille du tampon de lecture des messages&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Commande pour arrêter le serveur&lt;br /&gt;
#define EXIT_WORD &amp;quot;exit&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse);&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse);&lt;br /&gt;
int waitForClient(int * serverSocket);&lt;br /&gt;
void addClientToTab(int * epfd, int * clientSocket);&lt;br /&gt;
void manageClient(int * epfd, int * clientSocket);&lt;br /&gt;
void addEpollEvent(int * epfd, int * socket);&lt;br /&gt;
void delEpollEvent(int * epfd, int * socket);&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
	// Création du descripteur EPOLL&lt;br /&gt;
	int epfd = epoll_create1(0);&lt;br /&gt;
	// Structure contenant les événements EPOLL&lt;br /&gt;
	struct epoll_event events[NB_EVENTS];&lt;br /&gt;
	// Structure contenant l&#039;adresse&lt;br /&gt;
	struct sockaddr_in adresse;&lt;br /&gt;
	initAdresse(&amp;amp;adresse);&lt;br /&gt;
	// Descripteur de la socket du serveur&lt;br /&gt;
	int serverSocket = initSocket(&amp;amp;adresse);&lt;br /&gt;
	addEpollEvent(&amp;amp;epfd, &amp;amp;serverSocket);&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	while (1) {&lt;br /&gt;
		int nfds = epoll_wait(epfd, events, NB_EVENTS, -1);&lt;br /&gt;
		for (int i = 0; i &amp;lt; nfds; i++) {&lt;br /&gt;
			if (events[i].data.fd == serverSocket) {&lt;br /&gt;
				if ((clientSocket = waitForClient(&amp;amp;serverSocket)) != -1) {&lt;br /&gt;
					// On ajoute le nouveau client au tableau des descripteurs&lt;br /&gt;
					addClientToTab(&amp;amp;epfd, &amp;amp;clientSocket);&lt;br /&gt;
				}&lt;br /&gt;
			} else if (events[i].events &amp;amp; EPOLLIN) {&lt;br /&gt;
				// On a reçu quelque chose&lt;br /&gt;
				manageClient(&amp;amp;epfd, &amp;amp;events[i].data.fd);&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	close(serverSocket);&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
// Initialisation de la structure sockaddr_in&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse) {&lt;br /&gt;
	(*adresse).sin_family = AF_INET;&lt;br /&gt;
	(*adresse).sin_addr.s_addr = IP;&lt;br /&gt;
	(*adresse).sin_port = htons( PORT);&lt;br /&gt;
}&lt;br /&gt;
// Démarrage de la socket serveur&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse) {&lt;br /&gt;
	// Descripteur de socket&lt;br /&gt;
	int fdsocket;&lt;br /&gt;
	// Nombre d&#039;options&lt;br /&gt;
	int opt = 1;&lt;br /&gt;
	// Création de la socket en TCP&lt;br /&gt;
	if ((fdsocket = socket(AF_INET, SOCK_STREAM, 0)) == -1) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Création de la socket\n&amp;quot;);&lt;br /&gt;
	// Paramètrage de la socket&lt;br /&gt;
	if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,&lt;br /&gt;
			sizeof(opt)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Paramètrage de la socket\n&amp;quot;);&lt;br /&gt;
	// Attachement de la socket sur le port et l&#039;adresse IP&lt;br /&gt;
	if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Attachement de la socket sur le port %i\n&amp;quot;, PORT);&lt;br /&gt;
	// Passage en écoute de la socket&lt;br /&gt;
	if (listen(fdsocket, BACKLOG) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la mise en écoute: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	// Passage en mode non bloquant&lt;br /&gt;
	fcntl(fdsocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
	printf(&amp;quot;Mise en écoute de la socket\n&amp;quot;);&lt;br /&gt;
	return fdsocket;&lt;br /&gt;
}&lt;br /&gt;
// Attente de connexion d&#039;un client&lt;br /&gt;
int waitForClient(int * serverSocket) {&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	// Structure contenant l&#039;adresse du client&lt;br /&gt;
	struct sockaddr_in clientAdresse;&lt;br /&gt;
	int addrLen = sizeof(clientAdresse);&lt;br /&gt;
	if ((clientSocket = accept(*serverSocket, (struct sockaddr*) &amp;amp;clientAdresse,&lt;br /&gt;
			(socklen_t*) &amp;amp;addrLen)) != -1) {&lt;br /&gt;
		// Convertion de l&#039;IP en texte&lt;br /&gt;
		char ip[INET_ADDRSTRLEN];&lt;br /&gt;
		inet_ntop(AF_INET, &amp;amp;(clientAdresse.sin_addr), ip, INET_ADDRSTRLEN);&lt;br /&gt;
		printf(&amp;quot;Connexion de %s:%i\n&amp;quot;, ip, clientAdresse.sin_port);&lt;br /&gt;
		// Passage en mode non bloquant&lt;br /&gt;
		int opt = 1;&lt;br /&gt;
		setsockopt(clientSocket, SOL_SOCKET, SO_KEEPALIVE, &amp;amp;opt, sizeof(1));&lt;br /&gt;
	}&lt;br /&gt;
	return clientSocket;&lt;br /&gt;
}&lt;br /&gt;
// Ajoute les clients au tableau&lt;br /&gt;
void addClientToTab(int * epfd, int * clientSocket) {&lt;br /&gt;
	addEpollEvent(epfd, clientSocket);&lt;br /&gt;
	// Nouvelle connexion, envoie du message de bienvenu&lt;br /&gt;
	send(*clientSocket, &amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;,&lt;br /&gt;
			strlen(&amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;), MSG_DONTWAIT);&lt;br /&gt;
}&lt;br /&gt;
// On traite l&#039;input des clients&lt;br /&gt;
void manageClient(int * epfd, int * clientSocket) {&lt;br /&gt;
	// Création d&#039;un tampon pour stocker les messages des clients dans la heap&lt;br /&gt;
	static char buffer[BUFFER_LEN + 1];&lt;br /&gt;
	int len = recv(*clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
	// Booléen pour suivre l&#039;état de la socket&lt;br /&gt;
	int isClosed = 0;&lt;br /&gt;
	if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
		// Une erreur est survenue&lt;br /&gt;
		printf(&amp;quot;Errno [%i] : %s\n&amp;quot;, errno, strerror(errno));&lt;br /&gt;
		isClosed = 1;&lt;br /&gt;
	} else if (len == 0) {&lt;br /&gt;
		// Le client s&#039;est déconnecté (extrémité de la socket fermée)&lt;br /&gt;
		isClosed = 1;&lt;br /&gt;
	} else if (len &amp;gt; 0) {&lt;br /&gt;
		// Ajout du terminateur de chaîne&lt;br /&gt;
		buffer[len] = &#039;\0&#039;;&lt;br /&gt;
		if (strncmp(buffer, EXIT_WORD, 4) == 0) {&lt;br /&gt;
			// Le client veut se déconnecter&lt;br /&gt;
			send(*clientSocket, &amp;quot;Bye\n&amp;quot;, strlen(&amp;quot;Bye\n&amp;quot;), 0);&lt;br /&gt;
			isClosed = 1;&lt;br /&gt;
		} else {&lt;br /&gt;
			// On renvoie le texte au client dans un buffer assez grand&lt;br /&gt;
			int len = strlen(&amp;quot;Vous avez dit : &amp;quot;) + strlen(buffer) + 1;&lt;br /&gt;
			char response[len];&lt;br /&gt;
			strcpy(response, &amp;quot;Vous avez dit : &amp;quot;);&lt;br /&gt;
			strcat(response, buffer);&lt;br /&gt;
			// Un seul envoie permet de ne pas surcharger le réseau&lt;br /&gt;
			send(*clientSocket, response, strlen(response), 0);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	if (isClosed == 1) {&lt;br /&gt;
		// On enlève la socket des événements EPOLL&lt;br /&gt;
		delEpollEvent(epfd, clientSocket);&lt;br /&gt;
		// La socket est fermé ou le client veut quitter le serveur !&lt;br /&gt;
		printf(&amp;quot;Fermeture de la connexion avec le client\n&amp;quot;);&lt;br /&gt;
		// Fermeture de la socket&lt;br /&gt;
		close(*clientSocket);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// Ajoute un descripteur de socket aux événements EPOLL&lt;br /&gt;
void addEpollEvent(int * epfd, int * socket) {&lt;br /&gt;
	printf(&amp;quot;Ajout de la socket %i dans les événements EPOLL\n&amp;quot;, *socket);&lt;br /&gt;
	// Structure contenant les événements EPOLL&lt;br /&gt;
	struct epoll_event event;&lt;br /&gt;
	// On enregistre la socket du serveur comme descripteur EPOLL&lt;br /&gt;
	event.data.fd = *socket;&lt;br /&gt;
	// On surveille les message entrant, la fermeture de la socket&lt;br /&gt;
	event.events = EPOLLIN | EPOLLRDHUP;&lt;br /&gt;
	epoll_ctl(*epfd, EPOLL_CTL_ADD, *socket, &amp;amp;event);&lt;br /&gt;
}&lt;br /&gt;
// Efface un descripteur de socket des événements EPOLL&lt;br /&gt;
void delEpollEvent(int * epfd, int * socket) {&lt;br /&gt;
	printf(&amp;quot;Suppression de la socket %i des événements EPOLL\n&amp;quot;, *socket);&lt;br /&gt;
	// Structure contenant les événements EPOLL&lt;br /&gt;
	struct epoll_event event;&lt;br /&gt;
	// On enregistre la socket du serveur comme descripteur EPOLL&lt;br /&gt;
	event.data.fd = *socket;&lt;br /&gt;
	// On surveille les message entrant, la fermeture de la socket&lt;br /&gt;
	event.events = EPOLLIN | EPOLLRDHUP;&lt;br /&gt;
	epoll_ctl(*epfd, EPOLL_CTL_DEL, *socket, &amp;amp;event);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Voici ce que l&#039;on peut observer dans la console:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Création de la socket&lt;br /&gt;
Paramètrage de la socket&lt;br /&gt;
Attachement de la socket sur le port 8080&lt;br /&gt;
Mise en écoute de la socket&lt;br /&gt;
Ajout de la socket 4 dans les événements EPOLL&lt;br /&gt;
Connexion de 127.0.0.1:17607&lt;br /&gt;
Ajout de la socket 5 dans les événements EPOLL&lt;br /&gt;
Suppression de la socket 5 des événements EPOLL&lt;br /&gt;
Fermeture de la connexion avec le client&lt;br /&gt;
Connexion de 127.0.0.1:18119&lt;br /&gt;
Ajout de la socket 5 dans les événements EPOLL&lt;br /&gt;
Connexion de 127.0.0.1:18631&lt;br /&gt;
Ajout de la socket 6 dans les événements EPOLL&lt;br /&gt;
Suppression de la socket 5 des événements EPOLL&lt;br /&gt;
Fermeture de la connexion avec le client&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Magsss</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=C_socket&amp;diff=3283</id>
		<title>C socket</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=C_socket&amp;diff=3283"/>
		<updated>2018-11-01T18:27:13Z</updated>

		<summary type="html">&lt;p&gt;Magsss : /* Lecture */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
||&lt;br /&gt;
Les sockets permettent de connecter deux programmes qui s&#039;exécutent sur deux machines différentes. Cette connexion se fait à travers le réseau grâce à l&#039;utilisation d&#039;un port (TCP ou UDP) et d&#039;une adresse IP. &lt;br /&gt;
&lt;br /&gt;
Il y a deux types de sockets, une qui est démarrée par la partie serveur en &#039;&#039;écoute&#039;&#039; et l&#039;autre démarrée par la partie cliente qui se connecte à la première.&lt;br /&gt;
&lt;br /&gt;
Ci-contre une image résumant les différentes étapes pour arriver à l&#039;envoi de données.&lt;br /&gt;
||&lt;br /&gt;
[[Fichier:Socket workflow.png|centré|200px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Création  =&lt;br /&gt;
== Côté serveur ==&lt;br /&gt;
Tout d&#039;abord il faut créer l&#039;objet socket:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt; &lt;br /&gt;
&lt;br /&gt;
int socket(int domain, int type, int protocol)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*domain &amp;amp;rarr; integer, communication domain e.g., AF_INET (IPv4 protocol) , AF_INET6 (IPv6 protocol)&lt;br /&gt;
*type &amp;amp;rarr; type de communication&lt;br /&gt;
**SOCK_STREAM: TCP&lt;br /&gt;
**SOCK_DGRAM: UDP&lt;br /&gt;
**SOCK_RAW: socket à l&#039;état brut (bas niveau)&lt;br /&gt;
*protocol &amp;amp;rarr; valeur du champ &#039;&#039;protocol&#039;&#039; de l&#039;en-tête de niveau 3 (généralement &#039;&#039;0&#039;&#039;)&lt;br /&gt;
*la valeur de retour est le fichier descripteur de la socket ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Une fois le descripteur de socket créé, il est possible de le configurer:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt; &lt;br /&gt;
&lt;br /&gt;
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* level &amp;amp;rarr; niveau d&#039;application de l&#039;option (SOL_SOCKET, TCP, UDP, ...);&lt;br /&gt;
* optname &amp;amp;rarr; le nom de l&#039;option (&#039;&#039;SO_REUSEADDR&#039;&#039;, &#039;&#039;SO_REUSEPORT&#039;&#039;, &#039;&#039;SO_KEEPALIVE&#039;&#039;, ...)&lt;br /&gt;
* optval, optlen  &amp;amp;rarr; utilisé pour accéder aux options de la socket;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
Il existe plusieurs options de socket, les plus utilisées étant :&lt;br /&gt;
* &#039;&#039;SO_REUSEADDR&#039;&#039; &amp;amp;rarr; permet de réutiliser l&#039;adresse de la socket tout de suite même si cette dernière est dans l&#039;état &#039;&#039;wait&#039;&#039;;&lt;br /&gt;
* &#039;&#039;SO_REUSEPORT&#039;&#039; &amp;amp;rarr; permet de réutiliser le port de la socket tout de suite même si cette dernière est dans l&#039;état &#039;&#039;wait&#039;&#039;;&lt;br /&gt;
* &#039;&#039;SO_KEEPALIVE&#039;&#039; &amp;amp;rarr; envoie des informations périodiquement pour tester si l&#039;extrémité du tunnel est toujours présente;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Il faut maintenant attacher la socket à un port Internet et une adresse IP:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* addr &amp;amp;rarr; une structure symbolisant l&#039;adresse &lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
struct sockaddr_in {&lt;br /&gt;
    short            sin_family;   // e.g: AF_INET&lt;br /&gt;
    unsigned short   sin_port;     // e.g: htons(3490)&lt;br /&gt;
    struct in_addr   sin_addr;     // détaillé ci-dessous&lt;br /&gt;
    char             sin_zero[8];  // &#039;0&#039; habituellement&lt;br /&gt;
};&lt;br /&gt;
struct in_addr {&lt;br /&gt;
    unsigned long s_addr;  // initialiser avec inet_aton()&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On peut maintenant passer la socket en état d&#039;écoute, elle est prête à recevoir des connexions:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int listen(int sockfd, int backlog)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* backlog &amp;amp;rarr; taille maximum de la file d&#039;attente de la socket après laquelle le système répond avec &#039;&#039;ECONNREFUSED&#039;&#039;;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On peut maintenant attendre une connexion:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* addr &amp;amp;rarr; l&#039;adresse du client;&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de la structure addr;&lt;br /&gt;
* le code retour varie entre un entier positif qui correspond au descripteur de la socket cliente et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
== Côté client ==&lt;br /&gt;
Après avoir créé la socket avec &#039;&#039;socket&#039;&#039; on peut se connecter à la partie serveur:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* addr &amp;amp;rarr; une structure symbolisant l&#039;adresse (cf. ci-dessus)&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
= Lecture / écriture =&lt;br /&gt;
== Fonctions génériques ==&lt;br /&gt;
Tout d&#039;abord, on peut dire que la socket est un fichier et se manipule donc comme un fichier !&lt;br /&gt;
===Lecture===&lt;br /&gt;
Pour lire dans une socket on peut utiliser &#039;&#039;read&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t read(int fd, void *buf, size_t count);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères où mettre le message reçu;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à recevoir;&lt;br /&gt;
* le code retour correspond au nombre de caractères reçus ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
===Écriture===&lt;br /&gt;
Pour écrire dans une socket ou peut utiliser &#039;&#039;write&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t write(int fd, const void *buf, size_t count);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères contenant le message à envoyer;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à envoyer;&lt;br /&gt;
* le code retour correspond au nombre de caractères envoyés ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
===Modification===&lt;br /&gt;
Pour modifier un descripteur fichier de socket on utilise &#039;&#039;fcntl&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int fcntl(int fd, int cmd, ... /* int args */ );&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* cmd &amp;amp;rarr; la commande de modification (descripteur, drapeaux, ...);&lt;br /&gt;
* args &amp;amp;rarr; les arguments correspondant à la commande&lt;br /&gt;
&lt;br /&gt;
Cette commande va nous permettre de modifier le drapeau du descripteur de la socket (&#039;&#039;F_SETFL&#039;&#039;) pour le rendre non bloquant (&#039;&#039;O_NONBLOCK&#039;&#039;) !&lt;br /&gt;
&lt;br /&gt;
== Fonctions spécifiques ==&lt;br /&gt;
Il existe des fonctions spécifiques pour manipuler les sockets. Ces fonctions ressemblent aux précédentes mais acceptent des drapeaux en plus pour pouvoir modifier le descripteur &#039;&#039;à la volée&#039;&#039;.&lt;br /&gt;
===Lecture===&lt;br /&gt;
Pour lire dans une socket on peut utiliser &#039;&#039;recv&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t recv(int fd, void *buf, size_t count, int flags);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères où mettre le message reçu;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à recevoir;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;O_NONBLOCK&#039;&#039;);&lt;br /&gt;
* le code retour correspond au nombre de caractères reçus ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
===Écriture===&lt;br /&gt;
Pour écrire dans une socket ou peut utiliser &#039;&#039;send&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t send(int fd, const void *buf, size_t count, int flags);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères contenant le message à envoyer;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à envoyer;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;MSG_CONFIRM&#039;&#039;, &#039;&#039;MSG_DONTWAIT &#039;&#039;, ...);&lt;br /&gt;
* le code retour correspond au nombre de caractères envoyés ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
= Cas d&#039;utilisation =&lt;br /&gt;
== Création ==&lt;br /&gt;
Pour commencer, il faut créer le descripteur de fichier:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int fdsocket;&lt;br /&gt;
if ((fdsocket = socket(AF_INET, SOCK_STREAM, 0)) == -1) {&lt;br /&gt;
	printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Paramétrage des options ==&lt;br /&gt;
Il faut maintenant configurer la réutilisation de l&#039;adresse et du port&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int opt = 1;&lt;br /&gt;
if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,	sizeof(opt)) != 0) {&lt;br /&gt;
	printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Attachement ==&lt;br /&gt;
On peut maintenant attacher la socket à un port et une adresse:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
struct sockaddr_in adresse;&lt;br /&gt;
&lt;br /&gt;
adresse.sin_family = AF_INET;&lt;br /&gt;
// Ecoute sur toutes les adresses (INADDR_ANY &amp;lt;=&amp;gt; 0.0.0.0)&lt;br /&gt;
adresse.sin_addr.s_addr = INADDR_ANY;&lt;br /&gt;
// Conversion du port en valeur réseaux (Host TO Network Short)&lt;br /&gt;
adresse.sin_port = htons(8080);&lt;br /&gt;
&lt;br /&gt;
if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
	printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mise en écoute ==&lt;br /&gt;
La socket est prête à passer à l&#039;écoute des connexions des clients:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
&lt;br /&gt;
if (listen(fdsocket, BACKLOG) != 0) {&lt;br /&gt;
	printf(&amp;quot;Echéc de la mise en écoute: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Acceptation des connexions ==&lt;br /&gt;
Il est possible d&#039;accepter les nouvelles connexions:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int clientSocket;&lt;br /&gt;
// Structure contenant l&#039;adresse du client&lt;br /&gt;
struct sockaddr_in clientAdresse;&lt;br /&gt;
int addrLen = sizeof(clientAdresse);&lt;br /&gt;
if ((clientSocket = accept(*serverSocket, (struct sockaddr*) &amp;amp;clientAdresse, (socklen_t*) &amp;amp;addrLen)) != -1) {&lt;br /&gt;
	// Convertion de l&#039;IP en texte&lt;br /&gt;
	char ip[INET_ADDRSTRLEN];&lt;br /&gt;
	inet_ntop(AF_INET, &amp;amp;(clientAdresse.sin_addr), ip, INET_ADDRSTRLEN);&lt;br /&gt;
	printf(&amp;quot;Connexion de %s:%i\n&amp;quot;, ip, clientAdresse.sin_port);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Il est possible d&#039;utiliser &#039;&#039;fcntl&#039;&#039; pour passer le descripteur de la socket en non bloquant, avant l&#039;appel à la fonction &#039;&#039;accept&#039;&#039;. Si le descripteur reste bloquant, la fonction &#039;&#039;accept&#039;&#039; ne rendra pas la main tant qu&#039;un client ne se sera connecté, ayant pour résultat le blocage complet du programme !&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Passage en mode non bloquant&lt;br /&gt;
fcntl(fdsocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
On peut remarquer, au passage, l&#039;utilisation de &#039;&#039;inet_ntop&#039;&#039; pour convertir l&#039;adresse du client du format binaire au format text.&lt;br /&gt;
&lt;br /&gt;
== Lecture / Écriture ==&lt;br /&gt;
=== Mode connecté (TCP) ===&lt;br /&gt;
On peut maintenant utiliser le descripteur de la socket du client pour lire et écrire.&lt;br /&gt;
* avec les fonctions génériques:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Descripteur de la socket du client&lt;br /&gt;
int clientSocket;&lt;br /&gt;
char buffer[BUFFER_LEN];&lt;br /&gt;
// Passage en mode non bloquant, sinon read attend&lt;br /&gt;
fcntl(clientSocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
int len = read(clientSocket, buffer, BUFFER_LEN);&lt;br /&gt;
&lt;br /&gt;
send(clientSocket, &amp;quot;Coucou\n&amp;quot;, strlen(&amp;quot;Coucou\n&amp;quot;));&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* avec les fonctions spécifiques:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Descripteur de la socket du client&lt;br /&gt;
int clientSocket;&lt;br /&gt;
char buffer[BUFFER_LEN];&lt;br /&gt;
&lt;br /&gt;
int len = recv(clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
&lt;br /&gt;
send(clientSocket, &amp;quot;Coucou\n&amp;quot;, strlen(&amp;quot;Coucou\n&amp;quot;), MSG_DONTWAIT);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Analysons le retour de la fonction &#039;&#039;read&#039;&#039; ou &#039;&#039;recv&#039;&#039; lorsque l&#039;on utilise un descripteur non bloquant d&#039;après la [http://man7.org/linux/man-pages/man2/recvmsg.2.html documentation] :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
NAME &lt;br /&gt;
       recv, recvfrom, recvmsg - receive a message from a socket&lt;br /&gt;
[...]&lt;br /&gt;
MSG_DONTWAIT (since Linux 2.2)&lt;br /&gt;
    Enables nonblocking operation; if the operation would block, the call fails with the error EAGAIN or EWOULDBLOCK (this can also be enabled using the O_NONBLOCK flag with the F_SETFL fcntl(2)). &lt;br /&gt;
[...]&lt;br /&gt;
RETURN VALUE&lt;br /&gt;
[...]&lt;br /&gt;
These calls return the number of bytes received, or -1 if an error&lt;br /&gt;
       occurred.  In the event of an error, errno is set to indicate the&lt;br /&gt;
       error.&lt;br /&gt;
When a stream socket peer has performed an orderly shutdown, the&lt;br /&gt;
       return value will be 0 (the traditional &amp;quot;end-of-file&amp;quot; return).&lt;br /&gt;
[...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Le retour est :&lt;br /&gt;
* lorsque le descripteur est vide (aucune donnée de la part du client) &#039;&#039;-1&#039;&#039; et &#039;&#039;errno&#039;&#039; sera positionné à la valeur &#039;&#039;EAGAIN&#039;&#039;, il ne faut donc pas fermer la socket si le retour est &#039;&#039;-1&#039;&#039; ! Il faut d&#039;abord vérifier que &#039;&#039;errno&#039;&#039; &#039;&#039;&#039;n&#039;a pas&#039;&#039;&#039; la valeur &#039;&#039;EAGAIN&#039;&#039;;&lt;br /&gt;
* lorsque le retour est &#039;&#039;0&#039;&#039;, cela signifie que l&#039;extrémité est fermée, il faut donc fermer le descripteur local;&lt;br /&gt;
* lorsque la valeur est supérieure à &#039;&#039;0&#039;&#039; elle correspond au nombre d&#039;octets reçus.&lt;br /&gt;
&lt;br /&gt;
On peut donc en déduire le morceau de code suivant pour gérer le retour de &#039;&#039;read&#039;&#039; ou &#039;&#039;recv&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int len = recv(clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
&lt;br /&gt;
if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
	// Une erreur est survenue&lt;br /&gt;
} else if (len == 0) {&lt;br /&gt;
	// Le client s&#039;est déconnecté (extrémité de la socket fermée)			&lt;br /&gt;
} else if (len &amp;gt; 0) {&lt;br /&gt;
	// Le client à envoyé des données	&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Mode datagramme (UDP) ===&lt;br /&gt;
===Lecture===&lt;br /&gt;
Pour lire dans une socket on peut utiliser &#039;&#039;recvfrom&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t recvfrom(int fd, void *buf, size_t count, int flags, const struct sockaddr *addr, socklen_t addrlen);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères où mettre le message reçu;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à recevoir;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;O_NONBLOCK&#039;&#039;);&lt;br /&gt;
*  addr &amp;amp;rarr; une structure symbolisant l&#039;adresse du client (cf. ci-dessus)&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour correspond au nombre de caractères reçus ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
===Écriture===&lt;br /&gt;
Pour écrire dans une socket ou peut utiliser &#039;&#039;sendto&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t sendto(int fd, const void *buf, size_t count, const struct sockaddr *addr, socklen_t addrlen);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères contenant le message à envoyer;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à envoyer;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;MSG_DONTWAIT &#039;&#039;, ...);&lt;br /&gt;
* addr &amp;amp;rarr; une structure symbolisant l&#039;adresse du client (cf. ci-dessus)&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour correspond au nombre de caractères envoyer ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
= Exemples =&lt;br /&gt;
== Serveur TCP ==&lt;br /&gt;
Voici un exemple de serveur &#039;&#039;echo&#039;&#039; qui renvoie le message au client. Les connexions utilisent TCP et un simple client comme telnet suffit pour l&#039;utiliser.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;arpa/inet.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Port d&#039;écoute de la socket&lt;br /&gt;
#define PORT 8080&lt;br /&gt;
// Adresse d&#039;écoute (toutes les adresses)&lt;br /&gt;
#define IP INADDR_ANY&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
// Nombre de connexions clients&lt;br /&gt;
#define NB_CLIENTS 2&lt;br /&gt;
// Taille du tampon de lecture des messages&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Commande pour arrêter le serveur&lt;br /&gt;
#define EXIT_WORD &amp;quot;exit&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse);&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse);&lt;br /&gt;
int waitForClient(int * serverSocket);&lt;br /&gt;
void addClientToTab(int clientSocket, int clients[]);&lt;br /&gt;
void manageClient(int clients[]);&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
	// Création et initialisation du tableau contenant les descripteurs des sockets clients&lt;br /&gt;
	int clients[NB_CLIENTS];&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_CLIENTS; i++) {&lt;br /&gt;
		clients[i] = -1;&lt;br /&gt;
	}&lt;br /&gt;
	// Structure contenant l&#039;adresse&lt;br /&gt;
	struct sockaddr_in adresse;&lt;br /&gt;
	initAdresse(&amp;amp;adresse);&lt;br /&gt;
	// Descripteur de la socket du serveur&lt;br /&gt;
	int serverSocket = initSocket(&amp;amp;adresse);&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	while (1) {&lt;br /&gt;
		// Descripteur de la socket du client, on attend une connexion&lt;br /&gt;
		if ((clientSocket = waitForClient(&amp;amp;serverSocket)) != -1) {&lt;br /&gt;
			// On ajoute le nouveau client au tableau des descripteurs&lt;br /&gt;
			addClientToTab(clientSocket, clients);&lt;br /&gt;
		}&lt;br /&gt;
		manageClient(clients);&lt;br /&gt;
	}&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
// Initialisation de la structure sockaddr_in&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse) {&lt;br /&gt;
	(*adresse).sin_family = AF_INET;&lt;br /&gt;
	(*adresse).sin_addr.s_addr = IP;&lt;br /&gt;
	(*adresse).sin_port = htons( PORT);&lt;br /&gt;
}&lt;br /&gt;
// Démarrage de la socket serveur&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse) {&lt;br /&gt;
	// Descripteur de socket&lt;br /&gt;
	int fdsocket;&lt;br /&gt;
	// Nombre d&#039;options&lt;br /&gt;
	int opt = 1;&lt;br /&gt;
	// Création de la socket en TCP&lt;br /&gt;
	if ((fdsocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Création de la socket\n&amp;quot;);&lt;br /&gt;
	// Paramètrage de la socket&lt;br /&gt;
	if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,&lt;br /&gt;
			sizeof(opt)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Paramètrage de la socket\n&amp;quot;);&lt;br /&gt;
	// Attachement de la socket sur le port et l&#039;adresse IP&lt;br /&gt;
	if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Attachement de la socket sur le port %i\n&amp;quot;, PORT);&lt;br /&gt;
	// Passage en écoute de la socket&lt;br /&gt;
	if (listen(fdsocket, BACKLOG) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la mise en écoute: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	// Passage en mode non bloquant&lt;br /&gt;
	fcntl(fdsocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
	printf(&amp;quot;Mise en écoute de la socket\n&amp;quot;);&lt;br /&gt;
	return fdsocket;&lt;br /&gt;
}&lt;br /&gt;
// Attente de connexion d&#039;un client&lt;br /&gt;
int waitForClient(int * serverSocket) {&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	// Structure contenant l&#039;adresse du client&lt;br /&gt;
	struct sockaddr_in clientAdresse;&lt;br /&gt;
	int addrLen = sizeof(clientAdresse);&lt;br /&gt;
	if ((clientSocket = accept(*serverSocket, (struct sockaddr*) &amp;amp;clientAdresse,&lt;br /&gt;
			(socklen_t*) &amp;amp;addrLen)) != -1) {&lt;br /&gt;
		// Convertion de l&#039;IP en texte&lt;br /&gt;
		char ip[INET_ADDRSTRLEN];&lt;br /&gt;
		inet_ntop(AF_INET, &amp;amp;(clientAdresse.sin_addr), ip, INET_ADDRSTRLEN);&lt;br /&gt;
		printf(&amp;quot;Connexion de %s:%i\n&amp;quot;, ip, clientAdresse.sin_port);&lt;br /&gt;
		// Passage en mode non bloquant&lt;br /&gt;
		int opt = 1;&lt;br /&gt;
		setsockopt(clientSocket, SOL_SOCKET, SO_KEEPALIVE, &amp;amp;opt, sizeof(1));&lt;br /&gt;
	}&lt;br /&gt;
	return clientSocket;&lt;br /&gt;
}&lt;br /&gt;
// Ajoute les clients au tableau&lt;br /&gt;
void addClientToTab(int clientSocket, int clients[]) {&lt;br /&gt;
	// On vérifie si on à de la place de libre&lt;br /&gt;
	int found = -1;&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_CLIENTS; i++) {&lt;br /&gt;
		// On cherche de la place&lt;br /&gt;
		if (clients[i] == -1) {&lt;br /&gt;
			// On ajoute le client&lt;br /&gt;
			printf(&amp;quot;Ajout du client à l&#039;index %i\n&amp;quot;, i);&lt;br /&gt;
			clients[i] = clientSocket;&lt;br /&gt;
			// Nouvelle connexion, envoie du message de bienvenu&lt;br /&gt;
			send(clientSocket, &amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;, 	strlen(&amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;),&lt;br /&gt;
					MSG_DONTWAIT);&lt;br /&gt;
			found = 0;&lt;br /&gt;
			break;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	if (found == -1) {&lt;br /&gt;
		// On a plus de place de libre&lt;br /&gt;
		puts(&amp;quot;Plus de place, désolé&amp;quot;);&lt;br /&gt;
		close(clientSocket);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// On traite l&#039;input des clients&lt;br /&gt;
void manageClient(int clients[]) {&lt;br /&gt;
	// Création d&#039;un tampon pour stocker les messages des clients dans la heap&lt;br /&gt;
	static char buffer[BUFFER_LEN + 1];&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_CLIENTS; i++) {&lt;br /&gt;
		// On vérifie les messages&lt;br /&gt;
		int clientSocket = clients[i];&lt;br /&gt;
		if (clientSocket == -1) {&lt;br /&gt;
			continue;&lt;br /&gt;
		}&lt;br /&gt;
		int len = recv(clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
		// Booléen pour suivre l&#039;état de la socket&lt;br /&gt;
		int isClosed = 0;&lt;br /&gt;
		if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
			// Une erreur est survenue&lt;br /&gt;
			printf(&amp;quot;Errno [%i] : %s\n&amp;quot;, errno, strerror(errno));&lt;br /&gt;
			isClosed = 1;&lt;br /&gt;
		} else if (len == 0) {&lt;br /&gt;
			// Le client s&#039;est déconnecté (extrémité de la socket fermée)&lt;br /&gt;
			isClosed = 1;&lt;br /&gt;
		} else if (len &amp;gt; 0) {&lt;br /&gt;
			// Ajout du terminateur de chaîne&lt;br /&gt;
			buffer[len] = &#039;\0&#039;;&lt;br /&gt;
			if (strncmp(buffer, EXIT_WORD, 4) == 0) {&lt;br /&gt;
				// Le client veut se déconnecter&lt;br /&gt;
				send(clientSocket, &amp;quot;Bye\n&amp;quot;, strlen(&amp;quot;Bye\n&amp;quot;), 0);&lt;br /&gt;
				isClosed = 1;&lt;br /&gt;
			} else {&lt;br /&gt;
				// On renvoie le texte au client dans un buffer assez grand&lt;br /&gt;
				int len = strlen(&amp;quot;Vous avez dit : &amp;quot;) + strlen(buffer) + 1;&lt;br /&gt;
				char response[len];&lt;br /&gt;
				strcpy(response, &amp;quot;Vous avez dit : &amp;quot;);&lt;br /&gt;
				strcat(response, buffer);&lt;br /&gt;
				// Un seul envoie permet de ne pas surcharger le réseau&lt;br /&gt;
				send(clientSocket, response, strlen(response), 0);&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
		if (isClosed == 1) {&lt;br /&gt;
			// La socket est fermé ou le client veut quitter le serveur !&lt;br /&gt;
			printf(&amp;quot;Fermeture de la connexion avec le client\n&amp;quot;);&lt;br /&gt;
			// Fermeture de la socket&lt;br /&gt;
			close(clientSocket);&lt;br /&gt;
			// On fait de la place dans le tableau&lt;br /&gt;
			clients[i] = -1;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On peut interroger notre serveur avec &#039;&#039;telnet&#039;&#039; (&#039;&#039;yum -y install telnet&#039;&#039;):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# telnet 127.0.0.1 8080&lt;br /&gt;
Trying 127.0.0.1...&lt;br /&gt;
Connected to 127.0.0.1.&lt;br /&gt;
Escape character is &#039;^]&#039;.&lt;br /&gt;
Entrez &#039;exit&#039; pour quitter&lt;br /&gt;
coucou&lt;br /&gt;
Vous avez dit : coucou&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== Serveur UDP ==&lt;br /&gt;
Si on essaye de reproduire le même type de serveur mais cette fois-ci avec une socket en UDP, cela devient beaucoup plus simple. En effet, en UDP, plus besoin de tenir un tableau avec les descripteurs des sockets des clients car... on est en mode déconnecté (datagramme) ! Entendez par la que les clients envoie un message sans attendre de réponse et sans se soucier de savoir si le message est arrivé au serveur.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;arpa/inet.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Port d&#039;écoute de la socket&lt;br /&gt;
#define PORT 8080&lt;br /&gt;
// Adresse d&#039;écoute (toutes les adresses)&lt;br /&gt;
#define IP INADDR_ANY&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
// Taille du tampon de lecture des messages&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse);&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse);&lt;br /&gt;
void manageClient(int serverSocket);&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
	// Structure contenant l&#039;adresse&lt;br /&gt;
	struct sockaddr_in adresse;&lt;br /&gt;
	initAdresse(&amp;amp;adresse);&lt;br /&gt;
	// Descripteur de la socket du serveur&lt;br /&gt;
	int serverSocket = initSocket(&amp;amp;adresse);&lt;br /&gt;
	while (1) {&lt;br /&gt;
		// Descripteur de la socket du client, on attend une connexion&lt;br /&gt;
		manageClient(serverSocket);&lt;br /&gt;
	}&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
// Initialisation de la structure sockaddr_in&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse) {&lt;br /&gt;
	(*adresse).sin_family = AF_INET;&lt;br /&gt;
	(*adresse).sin_addr.s_addr = IP;&lt;br /&gt;
	(*adresse).sin_port = htons( PORT);&lt;br /&gt;
}&lt;br /&gt;
// Démarrage de la socket serveur&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse) {&lt;br /&gt;
	// Descripteur de socket&lt;br /&gt;
	int fdsocket;&lt;br /&gt;
	// Nombre d&#039;options&lt;br /&gt;
	int opt = 1;&lt;br /&gt;
	// Création de la socket en UDP&lt;br /&gt;
	if ((fdsocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Création de la socket\n&amp;quot;);&lt;br /&gt;
	// Paramètrage de la socket&lt;br /&gt;
	if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,&lt;br /&gt;
			sizeof(opt)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Paramètrage de la socket\n&amp;quot;);&lt;br /&gt;
	// Attachement de la socket sur le port et l&#039;adresse IP&lt;br /&gt;
	if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Attachement de la socket sur le port %i\n&amp;quot;, PORT);&lt;br /&gt;
	return fdsocket;&lt;br /&gt;
}&lt;br /&gt;
// On traite l&#039;input des clients&lt;br /&gt;
void manageClient(int serverSocket) {&lt;br /&gt;
	// Création d&#039;un tampon pour stocker les messages des clients dans la heap&lt;br /&gt;
	static char buffer[BUFFER_LEN + 1];&lt;br /&gt;
	// Structure contenant l&#039;adresse du client&lt;br /&gt;
	struct sockaddr_in clientAdresse;&lt;br /&gt;
	unsigned int addrLen = sizeof(clientAdresse);&lt;br /&gt;
	// On vérifie si on a reçu un message&lt;br /&gt;
	int len = recvfrom(serverSocket, buffer, BUFFER_LEN, MSG_DONTWAIT,&lt;br /&gt;
			(struct sockaddr*) &amp;amp;clientAdresse, &amp;amp;addrLen);&lt;br /&gt;
	if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
		printf(&amp;quot;Problème de socket %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	} else if (len &amp;gt; 0) {&lt;br /&gt;
		printf(&amp;quot;Message reçu de %s:%i\n&amp;quot;, inet_ntoa(clientAdresse.sin_addr),&lt;br /&gt;
				ntohs(clientAdresse.sin_port));&lt;br /&gt;
		// Ajout du terminateur de chaîne&lt;br /&gt;
		buffer[len] = &#039;\0&#039;;&lt;br /&gt;
		// On renvoie le texte au client dans un buffer assez grand&lt;br /&gt;
		len = strlen(&amp;quot;Vous avez dit : &amp;quot;) + strlen(buffer) + 1;&lt;br /&gt;
		char response[len];&lt;br /&gt;
		strcpy(response, &amp;quot;Vous avez dit : &amp;quot;);&lt;br /&gt;
		strcat(response, buffer);&lt;br /&gt;
		// Un seul envoie permet de ne pas surcharger le réseau&lt;br /&gt;
		sendto(serverSocket, response, strlen(response), MSG_DONTWAIT,&lt;br /&gt;
				(struct sockaddr*) &amp;amp;clientAdresse, addrLen);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
On peut interroger notre serveur avec &#039;&#039;netcat&#039;&#039; (&#039;&#039;yum -y install nc&#039;&#039;):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# nc -u 127.0.0.1 8080&lt;br /&gt;
coucou&lt;br /&gt;
Vous avez dit : coucou&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
= Notification évènementielle avec epoll =&lt;br /&gt;
Depuis la version 2.5 du noyau Linux il est possible d&#039;utiliser une série de fonctions, appartenant à l&#039;espace utilisateur (user-space), permettant de suivre l&#039;état d&#039;un descripteur de fichier.&lt;br /&gt;
La différence avec read, recv ou encore recvfrom c&#039;est que l&#039;on va pouvoir surveiller plusieurs descripteurs de fichiers en même temps, laissant le CPU libre pour effectuer autre chose !&lt;br /&gt;
&lt;br /&gt;
== Utilisation ==&lt;br /&gt;
Tout d&#039;abord il faut créer un descripteur de fichier de type &#039;&#039;epoll&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int epoll_create1(int flags);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* flags &amp;amp;rarr; EPOLL_CLOEXEC pour éviter les situation de compétition en environnement parallélisé;&lt;br /&gt;
* le code retour correspond au descripteur &#039;&#039;epoll&#039;&#039; ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée).&lt;br /&gt;
&lt;br /&gt;
Une fois le descripteur &#039;&#039;epoll&#039;&#039; créé, il faut lui associer un événement:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* epfd &amp;amp;rarr; le descripteur &#039;&#039;epoll&#039;&#039; à modifier;&lt;br /&gt;
* op &amp;amp;rarr; une opération parmis:&lt;br /&gt;
** EPOLL_CTL_ADD &amp;amp;rarr; pour ajouter un événement sur un descripteur de fichier;&lt;br /&gt;
** EPOLL_CTL_MOD &amp;amp;rarr; pour modifier un événement sur un descripteur de fichier;&lt;br /&gt;
** EPOLL_CTL_DEL &amp;amp;rarr; pour supprimer un événement sur un descripteur de fichier;&lt;br /&gt;
* fd &amp;amp;rarr; un descripteur de fichier à surveiller;&lt;br /&gt;
* event &amp;amp;rarr; la structure contenant l&#039;événement à surveiller (&#039;&#039;EPOLLIN&#039;&#039;, &#039;&#039;EPOLLOUT&#039;&#039;, EPOLLRDHUP&#039;&#039;, ...);&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; en cas de succès ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée).&lt;br /&gt;
&lt;br /&gt;
Voici la structure &#039;&#039;epoll_event&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
struct epoll_event {&lt;br /&gt;
	uint32_t events;    /* Epoll events */&lt;br /&gt;
	epoll_data_t data;  /* User data variable */&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
typedef union epoll_data {&lt;br /&gt;
	void    *ptr;&lt;br /&gt;
	int      fd;&lt;br /&gt;
	uint32_t u32;&lt;br /&gt;
	uint64_t u64;&lt;br /&gt;
} epoll_data_t;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Enfin, il ne reste plus qu&#039;à &#039;&#039;attendre&#039;&#039; des événements:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* epfd &amp;amp;rarr; le descripteur &#039;&#039;epoll&#039;&#039; à modifier;&lt;br /&gt;
* events &amp;amp;rarr; un tableau de &#039;&#039;epoll_event contenant les événements;&lt;br /&gt;
* maxevents &amp;amp;rarr; le nombre maximum d&#039;événements à traiter (selon la taille du tableau);&lt;br /&gt;
* timeout &amp;amp;rarr; le temps, en millisecondes que la fonction attend:&lt;br /&gt;
** &#039;&#039;-1&#039;&#039; &amp;amp;rarr; la fonction devient bloquante;&lt;br /&gt;
** &#039;&#039;0&#039;&#039; &amp;amp;rarr; retourne immédiatement;&lt;br /&gt;
** &#039;&#039;&amp;gt; 0&#039;&#039; &amp;amp;rarr; attend le nombre de millisecondes spécifiées;&lt;br /&gt;
* le code retour correspond au nombre d&#039;événements ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée).&lt;br /&gt;
&lt;br /&gt;
== Améliorations architecturales ==&lt;br /&gt;
=== Gestion des utilisateurs===&lt;br /&gt;
Tout d&#039;abord, le faite de laisser &#039;&#039;epoll&#039;&#039; gérer les descripteurs de socket nous permet de supprimer le tableau &#039;&#039;clients&#039;&#039; ainsi que la variable &#039;&#039;NB_CLIENTS&#039;&#039;. En effet, la référence vers le descripteur est maintenu par &#039;&#039;epoll&#039;&#039; et nous pouvons gérer maintenant un nombre &#039;&#039;illimité&#039;&#039; de clients.&lt;br /&gt;
=== Performances ===&lt;br /&gt;
La boucle &#039;&#039;while&#039;&#039; de notre serveur consomme énormément de CPU, environ 100% de CPU par client ! On aurais pu utiliser un temporisateur avec &#039;&#039;nanosleep&#039;&#039; pour baisser la consommation CPU entre 5% et 10%...&lt;br /&gt;
Le faîte de basculer sur une fonction bloquante comme &#039;&#039;epoll_wait&#039;&#039; qui utilise des signaux (interruptions) permet de réduire considérablement la consommation CPU. Gardez à l&#039;esprit qu&#039;il n&#039;y a rien de mieux que les interruptions, la preuve, lorsque l&#039;on fait un &#039;&#039;top&#039;&#039;, notre programme à complétement disparu de la liste !&lt;br /&gt;
&lt;br /&gt;
== Modification du serveur ==&lt;br /&gt;
Modifions le code du serveur TCP pour y intégrer &#039;&#039;epoll&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;arpa/inet.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Port d&#039;écoute de la socket&lt;br /&gt;
#define PORT 8080&lt;br /&gt;
// Adresse d&#039;écoute (toutes les adresses)&lt;br /&gt;
#define IP INADDR_ANY&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
// Nombre de connexions clients&lt;br /&gt;
#define NB_EVENTS 2&lt;br /&gt;
// Taille du tampon de lecture des messages&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Commande pour arrêter le serveur&lt;br /&gt;
#define EXIT_WORD &amp;quot;exit&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse);&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse);&lt;br /&gt;
int waitForClient(int * serverSocket);&lt;br /&gt;
void addClientToTab(int * epfd, int * clientSocket);&lt;br /&gt;
void manageClient(int * epfd, int * clientSocket);&lt;br /&gt;
void addEpollEvent(int * epfd, int * socket);&lt;br /&gt;
void delEpollEvent(int * epfd, int * socket);&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
	// Création du descripteur EPOLL&lt;br /&gt;
	int epfd = epoll_create1(0);&lt;br /&gt;
	// Structure contenant les événements EPOLL&lt;br /&gt;
	struct epoll_event events[NB_EVENTS];&lt;br /&gt;
	// Structure contenant l&#039;adresse&lt;br /&gt;
	struct sockaddr_in adresse;&lt;br /&gt;
	initAdresse(&amp;amp;adresse);&lt;br /&gt;
	// Descripteur de la socket du serveur&lt;br /&gt;
	int serverSocket = initSocket(&amp;amp;adresse);&lt;br /&gt;
	addEpollEvent(&amp;amp;epfd, &amp;amp;serverSocket);&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	while (1) {&lt;br /&gt;
		int nfds = epoll_wait(epfd, events, NB_EVENTS, -1);&lt;br /&gt;
		for (int i = 0; i &amp;lt; nfds; i++) {&lt;br /&gt;
			if (events[i].data.fd == serverSocket) {&lt;br /&gt;
				if ((clientSocket = waitForClient(&amp;amp;serverSocket)) != -1) {&lt;br /&gt;
					// On ajoute le nouveau client au tableau des descripteurs&lt;br /&gt;
					addClientToTab(&amp;amp;epfd, &amp;amp;clientSocket);&lt;br /&gt;
				}&lt;br /&gt;
			} else if (events[i].events &amp;amp; EPOLLIN) {&lt;br /&gt;
				// On a reçu quelque chose&lt;br /&gt;
				manageClient(&amp;amp;epfd, &amp;amp;events[i].data.fd);&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	close(serverSocket);&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
// Initialisation de la structure sockaddr_in&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse) {&lt;br /&gt;
	(*adresse).sin_family = AF_INET;&lt;br /&gt;
	(*adresse).sin_addr.s_addr = IP;&lt;br /&gt;
	(*adresse).sin_port = htons( PORT);&lt;br /&gt;
}&lt;br /&gt;
// Démarrage de la socket serveur&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse) {&lt;br /&gt;
	// Descripteur de socket&lt;br /&gt;
	int fdsocket;&lt;br /&gt;
	// Nombre d&#039;options&lt;br /&gt;
	int opt = 1;&lt;br /&gt;
	// Création de la socket en TCP&lt;br /&gt;
	if ((fdsocket = socket(AF_INET, SOCK_STREAM, 0)) == -1) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Création de la socket\n&amp;quot;);&lt;br /&gt;
	// Paramètrage de la socket&lt;br /&gt;
	if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,&lt;br /&gt;
			sizeof(opt)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Paramètrage de la socket\n&amp;quot;);&lt;br /&gt;
	// Attachement de la socket sur le port et l&#039;adresse IP&lt;br /&gt;
	if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Attachement de la socket sur le port %i\n&amp;quot;, PORT);&lt;br /&gt;
	// Passage en écoute de la socket&lt;br /&gt;
	if (listen(fdsocket, BACKLOG) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la mise en écoute: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	// Passage en mode non bloquant&lt;br /&gt;
	fcntl(fdsocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
	printf(&amp;quot;Mise en écoute de la socket\n&amp;quot;);&lt;br /&gt;
	return fdsocket;&lt;br /&gt;
}&lt;br /&gt;
// Attente de connexion d&#039;un client&lt;br /&gt;
int waitForClient(int * serverSocket) {&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	// Structure contenant l&#039;adresse du client&lt;br /&gt;
	struct sockaddr_in clientAdresse;&lt;br /&gt;
	int addrLen = sizeof(clientAdresse);&lt;br /&gt;
	if ((clientSocket = accept(*serverSocket, (struct sockaddr*) &amp;amp;clientAdresse,&lt;br /&gt;
			(socklen_t*) &amp;amp;addrLen)) != -1) {&lt;br /&gt;
		// Convertion de l&#039;IP en texte&lt;br /&gt;
		char ip[INET_ADDRSTRLEN];&lt;br /&gt;
		inet_ntop(AF_INET, &amp;amp;(clientAdresse.sin_addr), ip, INET_ADDRSTRLEN);&lt;br /&gt;
		printf(&amp;quot;Connexion de %s:%i\n&amp;quot;, ip, clientAdresse.sin_port);&lt;br /&gt;
		// Passage en mode non bloquant&lt;br /&gt;
		int opt = 1;&lt;br /&gt;
		setsockopt(clientSocket, SOL_SOCKET, SO_KEEPALIVE, &amp;amp;opt, sizeof(1));&lt;br /&gt;
	}&lt;br /&gt;
	return clientSocket;&lt;br /&gt;
}&lt;br /&gt;
// Ajoute les clients au tableau&lt;br /&gt;
void addClientToTab(int * epfd, int * clientSocket) {&lt;br /&gt;
	addEpollEvent(epfd, clientSocket);&lt;br /&gt;
	// Nouvelle connexion, envoie du message de bienvenu&lt;br /&gt;
	send(*clientSocket, &amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;,&lt;br /&gt;
			strlen(&amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;), MSG_DONTWAIT);&lt;br /&gt;
}&lt;br /&gt;
// On traite l&#039;input des clients&lt;br /&gt;
void manageClient(int * epfd, int * clientSocket) {&lt;br /&gt;
	// Création d&#039;un tampon pour stocker les messages des clients dans la heap&lt;br /&gt;
	static char buffer[BUFFER_LEN + 1];&lt;br /&gt;
	int len = recv(*clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
	// Booléen pour suivre l&#039;état de la socket&lt;br /&gt;
	int isClosed = 0;&lt;br /&gt;
	if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
		// Une erreur est survenue&lt;br /&gt;
		printf(&amp;quot;Errno [%i] : %s\n&amp;quot;, errno, strerror(errno));&lt;br /&gt;
		isClosed = 1;&lt;br /&gt;
	} else if (len == 0) {&lt;br /&gt;
		// Le client s&#039;est déconnecté (extrémité de la socket fermée)&lt;br /&gt;
		isClosed = 1;&lt;br /&gt;
	} else if (len &amp;gt; 0) {&lt;br /&gt;
		// Ajout du terminateur de chaîne&lt;br /&gt;
		buffer[len] = &#039;\0&#039;;&lt;br /&gt;
		if (strncmp(buffer, EXIT_WORD, 4) == 0) {&lt;br /&gt;
			// Le client veut se déconnecter&lt;br /&gt;
			send(*clientSocket, &amp;quot;Bye\n&amp;quot;, strlen(&amp;quot;Bye\n&amp;quot;), 0);&lt;br /&gt;
			isClosed = 1;&lt;br /&gt;
		} else {&lt;br /&gt;
			// On renvoie le texte au client dans un buffer assez grand&lt;br /&gt;
			int len = strlen(&amp;quot;Vous avez dit : &amp;quot;) + strlen(buffer) + 1;&lt;br /&gt;
			char response[len];&lt;br /&gt;
			strcpy(response, &amp;quot;Vous avez dit : &amp;quot;);&lt;br /&gt;
			strcat(response, buffer);&lt;br /&gt;
			// Un seul envoie permet de ne pas surcharger le réseau&lt;br /&gt;
			send(*clientSocket, response, strlen(response), 0);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	if (isClosed == 1) {&lt;br /&gt;
		// On enlève la socket des événements EPOLL&lt;br /&gt;
		delEpollEvent(epfd, clientSocket);&lt;br /&gt;
		// La socket est fermé ou le client veut quitter le serveur !&lt;br /&gt;
		printf(&amp;quot;Fermeture de la connexion avec le client\n&amp;quot;);&lt;br /&gt;
		// Fermeture de la socket&lt;br /&gt;
		close(*clientSocket);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// Ajoute un descripteur de socket aux événements EPOLL&lt;br /&gt;
void addEpollEvent(int * epfd, int * socket) {&lt;br /&gt;
	printf(&amp;quot;Ajout de la socket %i dans les événements EPOLL\n&amp;quot;, *socket);&lt;br /&gt;
	// Structure contenant les événements EPOLL&lt;br /&gt;
	struct epoll_event event;&lt;br /&gt;
	// On enregistre la socket du serveur comme descripteur EPOLL&lt;br /&gt;
	event.data.fd = *socket;&lt;br /&gt;
	// On surveille les message entrant, la fermeture de la socket&lt;br /&gt;
	event.events = EPOLLIN | EPOLLRDHUP;&lt;br /&gt;
	epoll_ctl(*epfd, EPOLL_CTL_ADD, *socket, &amp;amp;event);&lt;br /&gt;
}&lt;br /&gt;
// Efface un descripteur de socket des événements EPOLL&lt;br /&gt;
void delEpollEvent(int * epfd, int * socket) {&lt;br /&gt;
	printf(&amp;quot;Suppression de la socket %i des événements EPOLL\n&amp;quot;, *socket);&lt;br /&gt;
	// Structure contenant les événements EPOLL&lt;br /&gt;
	struct epoll_event event;&lt;br /&gt;
	// On enregistre la socket du serveur comme descripteur EPOLL&lt;br /&gt;
	event.data.fd = *socket;&lt;br /&gt;
	// On surveille les message entrant, la fermeture de la socket&lt;br /&gt;
	event.events = EPOLLIN | EPOLLRDHUP;&lt;br /&gt;
	epoll_ctl(*epfd, EPOLL_CTL_DEL, *socket, &amp;amp;event);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Voici ce que l&#039;on peut observer dans la console:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Création de la socket&lt;br /&gt;
Paramètrage de la socket&lt;br /&gt;
Attachement de la socket sur le port 8080&lt;br /&gt;
Mise en écoute de la socket&lt;br /&gt;
Ajout de la socket 4 dans les événements EPOLL&lt;br /&gt;
Connexion de 127.0.0.1:17607&lt;br /&gt;
Ajout de la socket 5 dans les événements EPOLL&lt;br /&gt;
Suppression de la socket 5 des événements EPOLL&lt;br /&gt;
Fermeture de la connexion avec le client&lt;br /&gt;
Connexion de 127.0.0.1:18119&lt;br /&gt;
Ajout de la socket 5 dans les événements EPOLL&lt;br /&gt;
Connexion de 127.0.0.1:18631&lt;br /&gt;
Ajout de la socket 6 dans les événements EPOLL&lt;br /&gt;
Suppression de la socket 5 des événements EPOLL&lt;br /&gt;
Fermeture de la connexion avec le client&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Magsss</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=C_socket&amp;diff=3282</id>
		<title>C socket</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=C_socket&amp;diff=3282"/>
		<updated>2018-11-01T18:26:05Z</updated>

		<summary type="html">&lt;p&gt;Magsss : /* Mode connecté (TCP) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
||&lt;br /&gt;
Les sockets permettent de connecter deux programmes qui s&#039;exécutent sur deux machines différentes. Cette connexion se fait à travers le réseau grâce à l&#039;utilisation d&#039;un port (TCP ou UDP) et d&#039;une adresse IP. &lt;br /&gt;
&lt;br /&gt;
Il y a deux types de sockets, une qui est démarrée par la partie serveur en &#039;&#039;écoute&#039;&#039; et l&#039;autre démarrée par la partie cliente qui se connecte à la première.&lt;br /&gt;
&lt;br /&gt;
Ci-contre une image résumant les différentes étapes pour arriver à l&#039;envoi de données.&lt;br /&gt;
||&lt;br /&gt;
[[Fichier:Socket workflow.png|centré|200px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Création  =&lt;br /&gt;
== Côté serveur ==&lt;br /&gt;
Tout d&#039;abord il faut créer l&#039;objet socket:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt; &lt;br /&gt;
&lt;br /&gt;
int socket(int domain, int type, int protocol)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*domain &amp;amp;rarr; integer, communication domain e.g., AF_INET (IPv4 protocol) , AF_INET6 (IPv6 protocol)&lt;br /&gt;
*type &amp;amp;rarr; type de communication&lt;br /&gt;
**SOCK_STREAM: TCP&lt;br /&gt;
**SOCK_DGRAM: UDP&lt;br /&gt;
**SOCK_RAW: socket à l&#039;état brut (bas niveau)&lt;br /&gt;
*protocol &amp;amp;rarr; valeur du champ &#039;&#039;protocol&#039;&#039; de l&#039;en-tête de niveau 3 (généralement &#039;&#039;0&#039;&#039;)&lt;br /&gt;
*la valeur de retour est le fichier descripteur de la socket ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Une fois le descripteur de socket créé, il est possible de le configurer:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt; &lt;br /&gt;
&lt;br /&gt;
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* level &amp;amp;rarr; niveau d&#039;application de l&#039;option (SOL_SOCKET, TCP, UDP, ...);&lt;br /&gt;
* optname &amp;amp;rarr; le nom de l&#039;option (&#039;&#039;SO_REUSEADDR&#039;&#039;, &#039;&#039;SO_REUSEPORT&#039;&#039;, &#039;&#039;SO_KEEPALIVE&#039;&#039;, ...)&lt;br /&gt;
* optval, optlen  &amp;amp;rarr; utilisé pour accéder aux options de la socket;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
Il existe plusieurs options de socket, les plus utilisées étant :&lt;br /&gt;
* &#039;&#039;SO_REUSEADDR&#039;&#039; &amp;amp;rarr; permet de réutiliser l&#039;adresse de la socket tout de suite même si cette dernière est dans l&#039;état &#039;&#039;wait&#039;&#039;;&lt;br /&gt;
* &#039;&#039;SO_REUSEPORT&#039;&#039; &amp;amp;rarr; permet de réutiliser le port de la socket tout de suite même si cette dernière est dans l&#039;état &#039;&#039;wait&#039;&#039;;&lt;br /&gt;
* &#039;&#039;SO_KEEPALIVE&#039;&#039; &amp;amp;rarr; envoie des informations périodiquement pour tester si l&#039;extrémité du tunnel est toujours présente;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Il faut maintenant attacher la socket à un port Internet et une adresse IP:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* addr &amp;amp;rarr; une structure symbolisant l&#039;adresse &lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
struct sockaddr_in {&lt;br /&gt;
    short            sin_family;   // e.g: AF_INET&lt;br /&gt;
    unsigned short   sin_port;     // e.g: htons(3490)&lt;br /&gt;
    struct in_addr   sin_addr;     // détaillé ci-dessous&lt;br /&gt;
    char             sin_zero[8];  // &#039;0&#039; habituellement&lt;br /&gt;
};&lt;br /&gt;
struct in_addr {&lt;br /&gt;
    unsigned long s_addr;  // initialiser avec inet_aton()&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On peut maintenant passer la socket en état d&#039;écoute, elle est prête à recevoir des connexions:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int listen(int sockfd, int backlog)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* backlog &amp;amp;rarr; taille maximum de la file d&#039;attente de la socket après laquelle le système répond avec &#039;&#039;ECONNREFUSED&#039;&#039;;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On peut maintenant attendre une connexion:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* addr &amp;amp;rarr; l&#039;adresse du client;&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de la structure addr;&lt;br /&gt;
* le code retour varie entre un entier positif qui correspond au descripteur de la socket cliente et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
== Côté client ==&lt;br /&gt;
Après avoir créé la socket avec &#039;&#039;socket&#039;&#039; on peut se connecter à la partie serveur:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* addr &amp;amp;rarr; une structure symbolisant l&#039;adresse (cf. ci-dessus)&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
= Lecture / écriture =&lt;br /&gt;
== Fonctions génériques ==&lt;br /&gt;
Tout d&#039;abord, on peut dire que la socket est un fichier et se manipule donc comme un fichier !&lt;br /&gt;
===Lecture===&lt;br /&gt;
Pour lire dans une socket on peut utiliser &#039;&#039;read&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t read(int fd, void *buf, size_t count);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères où mettre le message reçu;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à recevoir;&lt;br /&gt;
* le code retour correspond au nombre de caractères reçus ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
===Écriture===&lt;br /&gt;
Pour écrire dans une socket ou peut utiliser &#039;&#039;write&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t write(int fd, const void *buf, size_t count);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères contenant le message à envoyer;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à envoyer;&lt;br /&gt;
* le code retour correspond au nombre de caractères envoyés ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
===Modification===&lt;br /&gt;
Pour modifier un descripteur fichier de socket on utilise &#039;&#039;fcntl&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int fcntl(int fd, int cmd, ... /* int args */ );&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* cmd &amp;amp;rarr; la commande de modification (descripteur, drapeaux, ...);&lt;br /&gt;
* args &amp;amp;rarr; les arguments correspondant à la commande&lt;br /&gt;
&lt;br /&gt;
Cette commande va nous permettre de modifier le drapeau du descripteur de la socket (&#039;&#039;F_SETFL&#039;&#039;) pour le rendre non bloquant (&#039;&#039;O_NONBLOCK&#039;&#039;) !&lt;br /&gt;
&lt;br /&gt;
== Fonctions spécifiques ==&lt;br /&gt;
Il existe des fonctions spécifiques pour manipuler les sockets. Ces fonctions ressemblent aux précédentes mais acceptent des drapeaux en plus pour pouvoir modifier le descripteur &#039;&#039;à la volée&#039;&#039;.&lt;br /&gt;
===Lecture===&lt;br /&gt;
Pour lire dans une socket on peut utiliser &#039;&#039;recv&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t recv(int fd, void *buf, size_t count, int flags);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères où mettre le message reçu;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à recevoir;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;O_NONBLOCK&#039;&#039;);&lt;br /&gt;
* le code retour correspond au nombre de caractères reçus ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
===Écriture===&lt;br /&gt;
Pour écrire dans une socket ou peut utiliser &#039;&#039;send&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t send(int fd, const void *buf, size_t count, int flags);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères contenant le message à envoyer;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à envoyer;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;MSG_CONFIRM&#039;&#039;, &#039;&#039;MSG_DONTWAIT &#039;&#039;, ...);&lt;br /&gt;
* le code retour correspond au nombre de caractères envoyés ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
= Cas d&#039;utilisation =&lt;br /&gt;
== Création ==&lt;br /&gt;
Pour commencer, il faut créer le descripteur de fichier:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int fdsocket;&lt;br /&gt;
if ((fdsocket = socket(AF_INET, SOCK_STREAM, 0)) == -1) {&lt;br /&gt;
	printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Paramétrage des options ==&lt;br /&gt;
Il faut maintenant configurer la réutilisation de l&#039;adresse et du port&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int opt = 1;&lt;br /&gt;
if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,	sizeof(opt)) != 0) {&lt;br /&gt;
	printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Attachement ==&lt;br /&gt;
On peut maintenant attacher la socket à un port et une adresse:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
struct sockaddr_in adresse;&lt;br /&gt;
&lt;br /&gt;
adresse.sin_family = AF_INET;&lt;br /&gt;
// Ecoute sur toutes les adresses (INADDR_ANY &amp;lt;=&amp;gt; 0.0.0.0)&lt;br /&gt;
adresse.sin_addr.s_addr = INADDR_ANY;&lt;br /&gt;
// Conversion du port en valeur réseaux (Host TO Network Short)&lt;br /&gt;
adresse.sin_port = htons(8080);&lt;br /&gt;
&lt;br /&gt;
if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
	printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mise en écoute ==&lt;br /&gt;
La socket est prête à passer à l&#039;écoute des connexions des clients:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
&lt;br /&gt;
if (listen(fdsocket, BACKLOG) != 0) {&lt;br /&gt;
	printf(&amp;quot;Echéc de la mise en écoute: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Acceptation des connexions ==&lt;br /&gt;
Il est possible d&#039;accepter les nouvelles connexions:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int clientSocket;&lt;br /&gt;
// Structure contenant l&#039;adresse du client&lt;br /&gt;
struct sockaddr_in clientAdresse;&lt;br /&gt;
int addrLen = sizeof(clientAdresse);&lt;br /&gt;
if ((clientSocket = accept(*serverSocket, (struct sockaddr*) &amp;amp;clientAdresse, (socklen_t*) &amp;amp;addrLen)) != -1) {&lt;br /&gt;
	// Convertion de l&#039;IP en texte&lt;br /&gt;
	char ip[INET_ADDRSTRLEN];&lt;br /&gt;
	inet_ntop(AF_INET, &amp;amp;(clientAdresse.sin_addr), ip, INET_ADDRSTRLEN);&lt;br /&gt;
	printf(&amp;quot;Connexion de %s:%i\n&amp;quot;, ip, clientAdresse.sin_port);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Il est possible d&#039;utiliser &#039;&#039;fcntl&#039;&#039; pour passer le descripteur de la socket en non bloquant, avant l&#039;appel à la fonction &#039;&#039;accept&#039;&#039;. Si le descripteur reste bloquant, la fonction &#039;&#039;accept&#039;&#039; ne rendra pas la main tant qu&#039;un client ne se sera connecté, ayant pour résultat le blocage complet du programme !&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Passage en mode non bloquant&lt;br /&gt;
fcntl(fdsocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
On peut remarquer, au passage, l&#039;utilisation de &#039;&#039;inet_ntop&#039;&#039; pour convertir l&#039;adresse du client du format binaire au format text.&lt;br /&gt;
&lt;br /&gt;
== Lecture / Écriture ==&lt;br /&gt;
=== Mode connecté (TCP) ===&lt;br /&gt;
On peut maintenant utiliser le descripteur de la socket du client pour lire et écrire.&lt;br /&gt;
* avec les fonctions génériques:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Descripteur de la socket du client&lt;br /&gt;
int clientSocket;&lt;br /&gt;
char buffer[BUFFER_LEN];&lt;br /&gt;
// Passage en mode non bloquant, sinon read attend&lt;br /&gt;
fcntl(clientSocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
int len = read(clientSocket, buffer, BUFFER_LEN);&lt;br /&gt;
&lt;br /&gt;
send(clientSocket, &amp;quot;Coucou\n&amp;quot;, strlen(&amp;quot;Coucou\n&amp;quot;));&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* avec les fonctions spécifiques:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Descripteur de la socket du client&lt;br /&gt;
int clientSocket;&lt;br /&gt;
char buffer[BUFFER_LEN];&lt;br /&gt;
&lt;br /&gt;
int len = recv(clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
&lt;br /&gt;
send(clientSocket, &amp;quot;Coucou\n&amp;quot;, strlen(&amp;quot;Coucou\n&amp;quot;), MSG_DONTWAIT);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Analysons le retour de la fonction &#039;&#039;read&#039;&#039; ou &#039;&#039;recv&#039;&#039; lorsque l&#039;on utilise un descripteur non bloquant d&#039;après la [http://man7.org/linux/man-pages/man2/recvmsg.2.html documentation] :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
NAME &lt;br /&gt;
       recv, recvfrom, recvmsg - receive a message from a socket&lt;br /&gt;
[...]&lt;br /&gt;
MSG_DONTWAIT (since Linux 2.2)&lt;br /&gt;
    Enables nonblocking operation; if the operation would block, the call fails with the error EAGAIN or EWOULDBLOCK (this can also be enabled using the O_NONBLOCK flag with the F_SETFL fcntl(2)). &lt;br /&gt;
[...]&lt;br /&gt;
RETURN VALUE&lt;br /&gt;
[...]&lt;br /&gt;
These calls return the number of bytes received, or -1 if an error&lt;br /&gt;
       occurred.  In the event of an error, errno is set to indicate the&lt;br /&gt;
       error.&lt;br /&gt;
When a stream socket peer has performed an orderly shutdown, the&lt;br /&gt;
       return value will be 0 (the traditional &amp;quot;end-of-file&amp;quot; return).&lt;br /&gt;
[...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Le retour est :&lt;br /&gt;
* lorsque le descripteur est vide (aucune donnée de la part du client) &#039;&#039;-1&#039;&#039; et &#039;&#039;errno&#039;&#039; sera positionné à la valeur &#039;&#039;EAGAIN&#039;&#039;, il ne faut donc pas fermer la socket si le retour est &#039;&#039;-1&#039;&#039; ! Il faut d&#039;abord vérifier que &#039;&#039;errno&#039;&#039; &#039;&#039;&#039;n&#039;a pas&#039;&#039;&#039; la valeur &#039;&#039;EAGAIN&#039;&#039;;&lt;br /&gt;
* lorsque le retour est &#039;&#039;0&#039;&#039;, cela signifie que l&#039;extrémité est fermée, il faut donc fermer le descripteur local;&lt;br /&gt;
* lorsque la valeur est supérieure à &#039;&#039;0&#039;&#039; elle correspond au nombre d&#039;octets reçus.&lt;br /&gt;
&lt;br /&gt;
On peut donc en déduire le morceau de code suivant pour gérer le retour de &#039;&#039;read&#039;&#039; ou &#039;&#039;recv&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int len = recv(clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
&lt;br /&gt;
if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
	// Une erreur est survenue&lt;br /&gt;
} else if (len == 0) {&lt;br /&gt;
	// Le client s&#039;est déconnecté (extrémité de la socket fermée)			&lt;br /&gt;
} else if (len &amp;gt; 0) {&lt;br /&gt;
	// Le client à envoyé des données	&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Mode datagramme (UDP) ===&lt;br /&gt;
===Lecture===&lt;br /&gt;
Pour lire dans une socket on peut utiliser &#039;&#039;recvfrom&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t recvfrom(int fd, void *buf, size_t count, int flags, const struct sockaddr *addr, socklen_t addrlen);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères ou mettre le message reçu;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à recevoir;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;O_NONBLOCK&#039;&#039;);&lt;br /&gt;
*  addr &amp;amp;rarr; une structure symbolisant l&#039;adresse du client (cf. ci-dessus)&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour correspond au nombre de caractères reçu ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
===Écriture===&lt;br /&gt;
Pour écrire dans une socket ou peut utiliser &#039;&#039;sendto&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t sendto(int fd, const void *buf, size_t count, const struct sockaddr *addr, socklen_t addrlen);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères contenant le message à envoyer;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à envoyer;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;MSG_DONTWAIT &#039;&#039;, ...);&lt;br /&gt;
* addr &amp;amp;rarr; une structure symbolisant l&#039;adresse du client (cf. ci-dessus)&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour correspond au nombre de caractères envoyer ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
= Exemples =&lt;br /&gt;
== Serveur TCP ==&lt;br /&gt;
Voici un exemple de serveur &#039;&#039;echo&#039;&#039; qui renvoie le message au client. Les connexions utilisent TCP et un simple client comme telnet suffit pour l&#039;utiliser.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;arpa/inet.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Port d&#039;écoute de la socket&lt;br /&gt;
#define PORT 8080&lt;br /&gt;
// Adresse d&#039;écoute (toutes les adresses)&lt;br /&gt;
#define IP INADDR_ANY&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
// Nombre de connexions clients&lt;br /&gt;
#define NB_CLIENTS 2&lt;br /&gt;
// Taille du tampon de lecture des messages&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Commande pour arrêter le serveur&lt;br /&gt;
#define EXIT_WORD &amp;quot;exit&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse);&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse);&lt;br /&gt;
int waitForClient(int * serverSocket);&lt;br /&gt;
void addClientToTab(int clientSocket, int clients[]);&lt;br /&gt;
void manageClient(int clients[]);&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
	// Création et initialisation du tableau contenant les descripteurs des sockets clients&lt;br /&gt;
	int clients[NB_CLIENTS];&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_CLIENTS; i++) {&lt;br /&gt;
		clients[i] = -1;&lt;br /&gt;
	}&lt;br /&gt;
	// Structure contenant l&#039;adresse&lt;br /&gt;
	struct sockaddr_in adresse;&lt;br /&gt;
	initAdresse(&amp;amp;adresse);&lt;br /&gt;
	// Descripteur de la socket du serveur&lt;br /&gt;
	int serverSocket = initSocket(&amp;amp;adresse);&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	while (1) {&lt;br /&gt;
		// Descripteur de la socket du client, on attend une connexion&lt;br /&gt;
		if ((clientSocket = waitForClient(&amp;amp;serverSocket)) != -1) {&lt;br /&gt;
			// On ajoute le nouveau client au tableau des descripteurs&lt;br /&gt;
			addClientToTab(clientSocket, clients);&lt;br /&gt;
		}&lt;br /&gt;
		manageClient(clients);&lt;br /&gt;
	}&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
// Initialisation de la structure sockaddr_in&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse) {&lt;br /&gt;
	(*adresse).sin_family = AF_INET;&lt;br /&gt;
	(*adresse).sin_addr.s_addr = IP;&lt;br /&gt;
	(*adresse).sin_port = htons( PORT);&lt;br /&gt;
}&lt;br /&gt;
// Démarrage de la socket serveur&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse) {&lt;br /&gt;
	// Descripteur de socket&lt;br /&gt;
	int fdsocket;&lt;br /&gt;
	// Nombre d&#039;options&lt;br /&gt;
	int opt = 1;&lt;br /&gt;
	// Création de la socket en TCP&lt;br /&gt;
	if ((fdsocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Création de la socket\n&amp;quot;);&lt;br /&gt;
	// Paramètrage de la socket&lt;br /&gt;
	if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,&lt;br /&gt;
			sizeof(opt)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Paramètrage de la socket\n&amp;quot;);&lt;br /&gt;
	// Attachement de la socket sur le port et l&#039;adresse IP&lt;br /&gt;
	if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Attachement de la socket sur le port %i\n&amp;quot;, PORT);&lt;br /&gt;
	// Passage en écoute de la socket&lt;br /&gt;
	if (listen(fdsocket, BACKLOG) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la mise en écoute: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	// Passage en mode non bloquant&lt;br /&gt;
	fcntl(fdsocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
	printf(&amp;quot;Mise en écoute de la socket\n&amp;quot;);&lt;br /&gt;
	return fdsocket;&lt;br /&gt;
}&lt;br /&gt;
// Attente de connexion d&#039;un client&lt;br /&gt;
int waitForClient(int * serverSocket) {&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	// Structure contenant l&#039;adresse du client&lt;br /&gt;
	struct sockaddr_in clientAdresse;&lt;br /&gt;
	int addrLen = sizeof(clientAdresse);&lt;br /&gt;
	if ((clientSocket = accept(*serverSocket, (struct sockaddr*) &amp;amp;clientAdresse,&lt;br /&gt;
			(socklen_t*) &amp;amp;addrLen)) != -1) {&lt;br /&gt;
		// Convertion de l&#039;IP en texte&lt;br /&gt;
		char ip[INET_ADDRSTRLEN];&lt;br /&gt;
		inet_ntop(AF_INET, &amp;amp;(clientAdresse.sin_addr), ip, INET_ADDRSTRLEN);&lt;br /&gt;
		printf(&amp;quot;Connexion de %s:%i\n&amp;quot;, ip, clientAdresse.sin_port);&lt;br /&gt;
		// Passage en mode non bloquant&lt;br /&gt;
		int opt = 1;&lt;br /&gt;
		setsockopt(clientSocket, SOL_SOCKET, SO_KEEPALIVE, &amp;amp;opt, sizeof(1));&lt;br /&gt;
	}&lt;br /&gt;
	return clientSocket;&lt;br /&gt;
}&lt;br /&gt;
// Ajoute les clients au tableau&lt;br /&gt;
void addClientToTab(int clientSocket, int clients[]) {&lt;br /&gt;
	// On vérifie si on à de la place de libre&lt;br /&gt;
	int found = -1;&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_CLIENTS; i++) {&lt;br /&gt;
		// On cherche de la place&lt;br /&gt;
		if (clients[i] == -1) {&lt;br /&gt;
			// On ajoute le client&lt;br /&gt;
			printf(&amp;quot;Ajout du client à l&#039;index %i\n&amp;quot;, i);&lt;br /&gt;
			clients[i] = clientSocket;&lt;br /&gt;
			// Nouvelle connexion, envoie du message de bienvenu&lt;br /&gt;
			send(clientSocket, &amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;, 	strlen(&amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;),&lt;br /&gt;
					MSG_DONTWAIT);&lt;br /&gt;
			found = 0;&lt;br /&gt;
			break;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	if (found == -1) {&lt;br /&gt;
		// On a plus de place de libre&lt;br /&gt;
		puts(&amp;quot;Plus de place, désolé&amp;quot;);&lt;br /&gt;
		close(clientSocket);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// On traite l&#039;input des clients&lt;br /&gt;
void manageClient(int clients[]) {&lt;br /&gt;
	// Création d&#039;un tampon pour stocker les messages des clients dans la heap&lt;br /&gt;
	static char buffer[BUFFER_LEN + 1];&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_CLIENTS; i++) {&lt;br /&gt;
		// On vérifie les messages&lt;br /&gt;
		int clientSocket = clients[i];&lt;br /&gt;
		if (clientSocket == -1) {&lt;br /&gt;
			continue;&lt;br /&gt;
		}&lt;br /&gt;
		int len = recv(clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
		// Booléen pour suivre l&#039;état de la socket&lt;br /&gt;
		int isClosed = 0;&lt;br /&gt;
		if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
			// Une erreur est survenue&lt;br /&gt;
			printf(&amp;quot;Errno [%i] : %s\n&amp;quot;, errno, strerror(errno));&lt;br /&gt;
			isClosed = 1;&lt;br /&gt;
		} else if (len == 0) {&lt;br /&gt;
			// Le client s&#039;est déconnecté (extrémité de la socket fermée)&lt;br /&gt;
			isClosed = 1;&lt;br /&gt;
		} else if (len &amp;gt; 0) {&lt;br /&gt;
			// Ajout du terminateur de chaîne&lt;br /&gt;
			buffer[len] = &#039;\0&#039;;&lt;br /&gt;
			if (strncmp(buffer, EXIT_WORD, 4) == 0) {&lt;br /&gt;
				// Le client veut se déconnecter&lt;br /&gt;
				send(clientSocket, &amp;quot;Bye\n&amp;quot;, strlen(&amp;quot;Bye\n&amp;quot;), 0);&lt;br /&gt;
				isClosed = 1;&lt;br /&gt;
			} else {&lt;br /&gt;
				// On renvoie le texte au client dans un buffer assez grand&lt;br /&gt;
				int len = strlen(&amp;quot;Vous avez dit : &amp;quot;) + strlen(buffer) + 1;&lt;br /&gt;
				char response[len];&lt;br /&gt;
				strcpy(response, &amp;quot;Vous avez dit : &amp;quot;);&lt;br /&gt;
				strcat(response, buffer);&lt;br /&gt;
				// Un seul envoie permet de ne pas surcharger le réseau&lt;br /&gt;
				send(clientSocket, response, strlen(response), 0);&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
		if (isClosed == 1) {&lt;br /&gt;
			// La socket est fermé ou le client veut quitter le serveur !&lt;br /&gt;
			printf(&amp;quot;Fermeture de la connexion avec le client\n&amp;quot;);&lt;br /&gt;
			// Fermeture de la socket&lt;br /&gt;
			close(clientSocket);&lt;br /&gt;
			// On fait de la place dans le tableau&lt;br /&gt;
			clients[i] = -1;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On peut interroger notre serveur avec &#039;&#039;telnet&#039;&#039; (&#039;&#039;yum -y install telnet&#039;&#039;):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# telnet 127.0.0.1 8080&lt;br /&gt;
Trying 127.0.0.1...&lt;br /&gt;
Connected to 127.0.0.1.&lt;br /&gt;
Escape character is &#039;^]&#039;.&lt;br /&gt;
Entrez &#039;exit&#039; pour quitter&lt;br /&gt;
coucou&lt;br /&gt;
Vous avez dit : coucou&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== Serveur UDP ==&lt;br /&gt;
Si on essaye de reproduire le même type de serveur mais cette fois-ci avec une socket en UDP, cela devient beaucoup plus simple. En effet, en UDP, plus besoin de tenir un tableau avec les descripteurs des sockets des clients car... on est en mode déconnecté (datagramme) ! Entendez par la que les clients envoie un message sans attendre de réponse et sans se soucier de savoir si le message est arrivé au serveur.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;arpa/inet.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Port d&#039;écoute de la socket&lt;br /&gt;
#define PORT 8080&lt;br /&gt;
// Adresse d&#039;écoute (toutes les adresses)&lt;br /&gt;
#define IP INADDR_ANY&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
// Taille du tampon de lecture des messages&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse);&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse);&lt;br /&gt;
void manageClient(int serverSocket);&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
	// Structure contenant l&#039;adresse&lt;br /&gt;
	struct sockaddr_in adresse;&lt;br /&gt;
	initAdresse(&amp;amp;adresse);&lt;br /&gt;
	// Descripteur de la socket du serveur&lt;br /&gt;
	int serverSocket = initSocket(&amp;amp;adresse);&lt;br /&gt;
	while (1) {&lt;br /&gt;
		// Descripteur de la socket du client, on attend une connexion&lt;br /&gt;
		manageClient(serverSocket);&lt;br /&gt;
	}&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
// Initialisation de la structure sockaddr_in&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse) {&lt;br /&gt;
	(*adresse).sin_family = AF_INET;&lt;br /&gt;
	(*adresse).sin_addr.s_addr = IP;&lt;br /&gt;
	(*adresse).sin_port = htons( PORT);&lt;br /&gt;
}&lt;br /&gt;
// Démarrage de la socket serveur&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse) {&lt;br /&gt;
	// Descripteur de socket&lt;br /&gt;
	int fdsocket;&lt;br /&gt;
	// Nombre d&#039;options&lt;br /&gt;
	int opt = 1;&lt;br /&gt;
	// Création de la socket en UDP&lt;br /&gt;
	if ((fdsocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Création de la socket\n&amp;quot;);&lt;br /&gt;
	// Paramètrage de la socket&lt;br /&gt;
	if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,&lt;br /&gt;
			sizeof(opt)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Paramètrage de la socket\n&amp;quot;);&lt;br /&gt;
	// Attachement de la socket sur le port et l&#039;adresse IP&lt;br /&gt;
	if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Attachement de la socket sur le port %i\n&amp;quot;, PORT);&lt;br /&gt;
	return fdsocket;&lt;br /&gt;
}&lt;br /&gt;
// On traite l&#039;input des clients&lt;br /&gt;
void manageClient(int serverSocket) {&lt;br /&gt;
	// Création d&#039;un tampon pour stocker les messages des clients dans la heap&lt;br /&gt;
	static char buffer[BUFFER_LEN + 1];&lt;br /&gt;
	// Structure contenant l&#039;adresse du client&lt;br /&gt;
	struct sockaddr_in clientAdresse;&lt;br /&gt;
	unsigned int addrLen = sizeof(clientAdresse);&lt;br /&gt;
	// On vérifie si on a reçu un message&lt;br /&gt;
	int len = recvfrom(serverSocket, buffer, BUFFER_LEN, MSG_DONTWAIT,&lt;br /&gt;
			(struct sockaddr*) &amp;amp;clientAdresse, &amp;amp;addrLen);&lt;br /&gt;
	if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
		printf(&amp;quot;Problème de socket %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	} else if (len &amp;gt; 0) {&lt;br /&gt;
		printf(&amp;quot;Message reçu de %s:%i\n&amp;quot;, inet_ntoa(clientAdresse.sin_addr),&lt;br /&gt;
				ntohs(clientAdresse.sin_port));&lt;br /&gt;
		// Ajout du terminateur de chaîne&lt;br /&gt;
		buffer[len] = &#039;\0&#039;;&lt;br /&gt;
		// On renvoie le texte au client dans un buffer assez grand&lt;br /&gt;
		len = strlen(&amp;quot;Vous avez dit : &amp;quot;) + strlen(buffer) + 1;&lt;br /&gt;
		char response[len];&lt;br /&gt;
		strcpy(response, &amp;quot;Vous avez dit : &amp;quot;);&lt;br /&gt;
		strcat(response, buffer);&lt;br /&gt;
		// Un seul envoie permet de ne pas surcharger le réseau&lt;br /&gt;
		sendto(serverSocket, response, strlen(response), MSG_DONTWAIT,&lt;br /&gt;
				(struct sockaddr*) &amp;amp;clientAdresse, addrLen);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
On peut interroger notre serveur avec &#039;&#039;netcat&#039;&#039; (&#039;&#039;yum -y install nc&#039;&#039;):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# nc -u 127.0.0.1 8080&lt;br /&gt;
coucou&lt;br /&gt;
Vous avez dit : coucou&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
= Notification évènementielle avec epoll =&lt;br /&gt;
Depuis la version 2.5 du noyau Linux il est possible d&#039;utiliser une série de fonctions, appartenant à l&#039;espace utilisateur (user-space), permettant de suivre l&#039;état d&#039;un descripteur de fichier.&lt;br /&gt;
La différence avec read, recv ou encore recvfrom c&#039;est que l&#039;on va pouvoir surveiller plusieurs descripteurs de fichiers en même temps, laissant le CPU libre pour effectuer autre chose !&lt;br /&gt;
&lt;br /&gt;
== Utilisation ==&lt;br /&gt;
Tout d&#039;abord il faut créer un descripteur de fichier de type &#039;&#039;epoll&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int epoll_create1(int flags);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* flags &amp;amp;rarr; EPOLL_CLOEXEC pour éviter les situation de compétition en environnement parallélisé;&lt;br /&gt;
* le code retour correspond au descripteur &#039;&#039;epoll&#039;&#039; ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée).&lt;br /&gt;
&lt;br /&gt;
Une fois le descripteur &#039;&#039;epoll&#039;&#039; créé, il faut lui associer un événement:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* epfd &amp;amp;rarr; le descripteur &#039;&#039;epoll&#039;&#039; à modifier;&lt;br /&gt;
* op &amp;amp;rarr; une opération parmis:&lt;br /&gt;
** EPOLL_CTL_ADD &amp;amp;rarr; pour ajouter un événement sur un descripteur de fichier;&lt;br /&gt;
** EPOLL_CTL_MOD &amp;amp;rarr; pour modifier un événement sur un descripteur de fichier;&lt;br /&gt;
** EPOLL_CTL_DEL &amp;amp;rarr; pour supprimer un événement sur un descripteur de fichier;&lt;br /&gt;
* fd &amp;amp;rarr; un descripteur de fichier à surveiller;&lt;br /&gt;
* event &amp;amp;rarr; la structure contenant l&#039;événement à surveiller (&#039;&#039;EPOLLIN&#039;&#039;, &#039;&#039;EPOLLOUT&#039;&#039;, EPOLLRDHUP&#039;&#039;, ...);&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; en cas de succès ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée).&lt;br /&gt;
&lt;br /&gt;
Voici la structure &#039;&#039;epoll_event&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
struct epoll_event {&lt;br /&gt;
	uint32_t events;    /* Epoll events */&lt;br /&gt;
	epoll_data_t data;  /* User data variable */&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
typedef union epoll_data {&lt;br /&gt;
	void    *ptr;&lt;br /&gt;
	int      fd;&lt;br /&gt;
	uint32_t u32;&lt;br /&gt;
	uint64_t u64;&lt;br /&gt;
} epoll_data_t;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Enfin, il ne reste plus qu&#039;à &#039;&#039;attendre&#039;&#039; des événements:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* epfd &amp;amp;rarr; le descripteur &#039;&#039;epoll&#039;&#039; à modifier;&lt;br /&gt;
* events &amp;amp;rarr; un tableau de &#039;&#039;epoll_event contenant les événements;&lt;br /&gt;
* maxevents &amp;amp;rarr; le nombre maximum d&#039;événements à traiter (selon la taille du tableau);&lt;br /&gt;
* timeout &amp;amp;rarr; le temps, en millisecondes que la fonction attend:&lt;br /&gt;
** &#039;&#039;-1&#039;&#039; &amp;amp;rarr; la fonction devient bloquante;&lt;br /&gt;
** &#039;&#039;0&#039;&#039; &amp;amp;rarr; retourne immédiatement;&lt;br /&gt;
** &#039;&#039;&amp;gt; 0&#039;&#039; &amp;amp;rarr; attend le nombre de millisecondes spécifiées;&lt;br /&gt;
* le code retour correspond au nombre d&#039;événements ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée).&lt;br /&gt;
&lt;br /&gt;
== Améliorations architecturales ==&lt;br /&gt;
=== Gestion des utilisateurs===&lt;br /&gt;
Tout d&#039;abord, le faite de laisser &#039;&#039;epoll&#039;&#039; gérer les descripteurs de socket nous permet de supprimer le tableau &#039;&#039;clients&#039;&#039; ainsi que la variable &#039;&#039;NB_CLIENTS&#039;&#039;. En effet, la référence vers le descripteur est maintenu par &#039;&#039;epoll&#039;&#039; et nous pouvons gérer maintenant un nombre &#039;&#039;illimité&#039;&#039; de clients.&lt;br /&gt;
=== Performances ===&lt;br /&gt;
La boucle &#039;&#039;while&#039;&#039; de notre serveur consomme énormément de CPU, environ 100% de CPU par client ! On aurais pu utiliser un temporisateur avec &#039;&#039;nanosleep&#039;&#039; pour baisser la consommation CPU entre 5% et 10%...&lt;br /&gt;
Le faîte de basculer sur une fonction bloquante comme &#039;&#039;epoll_wait&#039;&#039; qui utilise des signaux (interruptions) permet de réduire considérablement la consommation CPU. Gardez à l&#039;esprit qu&#039;il n&#039;y a rien de mieux que les interruptions, la preuve, lorsque l&#039;on fait un &#039;&#039;top&#039;&#039;, notre programme à complétement disparu de la liste !&lt;br /&gt;
&lt;br /&gt;
== Modification du serveur ==&lt;br /&gt;
Modifions le code du serveur TCP pour y intégrer &#039;&#039;epoll&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;arpa/inet.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Port d&#039;écoute de la socket&lt;br /&gt;
#define PORT 8080&lt;br /&gt;
// Adresse d&#039;écoute (toutes les adresses)&lt;br /&gt;
#define IP INADDR_ANY&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
// Nombre de connexions clients&lt;br /&gt;
#define NB_EVENTS 2&lt;br /&gt;
// Taille du tampon de lecture des messages&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Commande pour arrêter le serveur&lt;br /&gt;
#define EXIT_WORD &amp;quot;exit&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse);&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse);&lt;br /&gt;
int waitForClient(int * serverSocket);&lt;br /&gt;
void addClientToTab(int * epfd, int * clientSocket);&lt;br /&gt;
void manageClient(int * epfd, int * clientSocket);&lt;br /&gt;
void addEpollEvent(int * epfd, int * socket);&lt;br /&gt;
void delEpollEvent(int * epfd, int * socket);&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
	// Création du descripteur EPOLL&lt;br /&gt;
	int epfd = epoll_create1(0);&lt;br /&gt;
	// Structure contenant les événements EPOLL&lt;br /&gt;
	struct epoll_event events[NB_EVENTS];&lt;br /&gt;
	// Structure contenant l&#039;adresse&lt;br /&gt;
	struct sockaddr_in adresse;&lt;br /&gt;
	initAdresse(&amp;amp;adresse);&lt;br /&gt;
	// Descripteur de la socket du serveur&lt;br /&gt;
	int serverSocket = initSocket(&amp;amp;adresse);&lt;br /&gt;
	addEpollEvent(&amp;amp;epfd, &amp;amp;serverSocket);&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	while (1) {&lt;br /&gt;
		int nfds = epoll_wait(epfd, events, NB_EVENTS, -1);&lt;br /&gt;
		for (int i = 0; i &amp;lt; nfds; i++) {&lt;br /&gt;
			if (events[i].data.fd == serverSocket) {&lt;br /&gt;
				if ((clientSocket = waitForClient(&amp;amp;serverSocket)) != -1) {&lt;br /&gt;
					// On ajoute le nouveau client au tableau des descripteurs&lt;br /&gt;
					addClientToTab(&amp;amp;epfd, &amp;amp;clientSocket);&lt;br /&gt;
				}&lt;br /&gt;
			} else if (events[i].events &amp;amp; EPOLLIN) {&lt;br /&gt;
				// On a reçu quelque chose&lt;br /&gt;
				manageClient(&amp;amp;epfd, &amp;amp;events[i].data.fd);&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	close(serverSocket);&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
// Initialisation de la structure sockaddr_in&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse) {&lt;br /&gt;
	(*adresse).sin_family = AF_INET;&lt;br /&gt;
	(*adresse).sin_addr.s_addr = IP;&lt;br /&gt;
	(*adresse).sin_port = htons( PORT);&lt;br /&gt;
}&lt;br /&gt;
// Démarrage de la socket serveur&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse) {&lt;br /&gt;
	// Descripteur de socket&lt;br /&gt;
	int fdsocket;&lt;br /&gt;
	// Nombre d&#039;options&lt;br /&gt;
	int opt = 1;&lt;br /&gt;
	// Création de la socket en TCP&lt;br /&gt;
	if ((fdsocket = socket(AF_INET, SOCK_STREAM, 0)) == -1) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Création de la socket\n&amp;quot;);&lt;br /&gt;
	// Paramètrage de la socket&lt;br /&gt;
	if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,&lt;br /&gt;
			sizeof(opt)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Paramètrage de la socket\n&amp;quot;);&lt;br /&gt;
	// Attachement de la socket sur le port et l&#039;adresse IP&lt;br /&gt;
	if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Attachement de la socket sur le port %i\n&amp;quot;, PORT);&lt;br /&gt;
	// Passage en écoute de la socket&lt;br /&gt;
	if (listen(fdsocket, BACKLOG) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la mise en écoute: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	// Passage en mode non bloquant&lt;br /&gt;
	fcntl(fdsocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
	printf(&amp;quot;Mise en écoute de la socket\n&amp;quot;);&lt;br /&gt;
	return fdsocket;&lt;br /&gt;
}&lt;br /&gt;
// Attente de connexion d&#039;un client&lt;br /&gt;
int waitForClient(int * serverSocket) {&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	// Structure contenant l&#039;adresse du client&lt;br /&gt;
	struct sockaddr_in clientAdresse;&lt;br /&gt;
	int addrLen = sizeof(clientAdresse);&lt;br /&gt;
	if ((clientSocket = accept(*serverSocket, (struct sockaddr*) &amp;amp;clientAdresse,&lt;br /&gt;
			(socklen_t*) &amp;amp;addrLen)) != -1) {&lt;br /&gt;
		// Convertion de l&#039;IP en texte&lt;br /&gt;
		char ip[INET_ADDRSTRLEN];&lt;br /&gt;
		inet_ntop(AF_INET, &amp;amp;(clientAdresse.sin_addr), ip, INET_ADDRSTRLEN);&lt;br /&gt;
		printf(&amp;quot;Connexion de %s:%i\n&amp;quot;, ip, clientAdresse.sin_port);&lt;br /&gt;
		// Passage en mode non bloquant&lt;br /&gt;
		int opt = 1;&lt;br /&gt;
		setsockopt(clientSocket, SOL_SOCKET, SO_KEEPALIVE, &amp;amp;opt, sizeof(1));&lt;br /&gt;
	}&lt;br /&gt;
	return clientSocket;&lt;br /&gt;
}&lt;br /&gt;
// Ajoute les clients au tableau&lt;br /&gt;
void addClientToTab(int * epfd, int * clientSocket) {&lt;br /&gt;
	addEpollEvent(epfd, clientSocket);&lt;br /&gt;
	// Nouvelle connexion, envoie du message de bienvenu&lt;br /&gt;
	send(*clientSocket, &amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;,&lt;br /&gt;
			strlen(&amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;), MSG_DONTWAIT);&lt;br /&gt;
}&lt;br /&gt;
// On traite l&#039;input des clients&lt;br /&gt;
void manageClient(int * epfd, int * clientSocket) {&lt;br /&gt;
	// Création d&#039;un tampon pour stocker les messages des clients dans la heap&lt;br /&gt;
	static char buffer[BUFFER_LEN + 1];&lt;br /&gt;
	int len = recv(*clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
	// Booléen pour suivre l&#039;état de la socket&lt;br /&gt;
	int isClosed = 0;&lt;br /&gt;
	if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
		// Une erreur est survenue&lt;br /&gt;
		printf(&amp;quot;Errno [%i] : %s\n&amp;quot;, errno, strerror(errno));&lt;br /&gt;
		isClosed = 1;&lt;br /&gt;
	} else if (len == 0) {&lt;br /&gt;
		// Le client s&#039;est déconnecté (extrémité de la socket fermée)&lt;br /&gt;
		isClosed = 1;&lt;br /&gt;
	} else if (len &amp;gt; 0) {&lt;br /&gt;
		// Ajout du terminateur de chaîne&lt;br /&gt;
		buffer[len] = &#039;\0&#039;;&lt;br /&gt;
		if (strncmp(buffer, EXIT_WORD, 4) == 0) {&lt;br /&gt;
			// Le client veut se déconnecter&lt;br /&gt;
			send(*clientSocket, &amp;quot;Bye\n&amp;quot;, strlen(&amp;quot;Bye\n&amp;quot;), 0);&lt;br /&gt;
			isClosed = 1;&lt;br /&gt;
		} else {&lt;br /&gt;
			// On renvoie le texte au client dans un buffer assez grand&lt;br /&gt;
			int len = strlen(&amp;quot;Vous avez dit : &amp;quot;) + strlen(buffer) + 1;&lt;br /&gt;
			char response[len];&lt;br /&gt;
			strcpy(response, &amp;quot;Vous avez dit : &amp;quot;);&lt;br /&gt;
			strcat(response, buffer);&lt;br /&gt;
			// Un seul envoie permet de ne pas surcharger le réseau&lt;br /&gt;
			send(*clientSocket, response, strlen(response), 0);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	if (isClosed == 1) {&lt;br /&gt;
		// On enlève la socket des événements EPOLL&lt;br /&gt;
		delEpollEvent(epfd, clientSocket);&lt;br /&gt;
		// La socket est fermé ou le client veut quitter le serveur !&lt;br /&gt;
		printf(&amp;quot;Fermeture de la connexion avec le client\n&amp;quot;);&lt;br /&gt;
		// Fermeture de la socket&lt;br /&gt;
		close(*clientSocket);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// Ajoute un descripteur de socket aux événements EPOLL&lt;br /&gt;
void addEpollEvent(int * epfd, int * socket) {&lt;br /&gt;
	printf(&amp;quot;Ajout de la socket %i dans les événements EPOLL\n&amp;quot;, *socket);&lt;br /&gt;
	// Structure contenant les événements EPOLL&lt;br /&gt;
	struct epoll_event event;&lt;br /&gt;
	// On enregistre la socket du serveur comme descripteur EPOLL&lt;br /&gt;
	event.data.fd = *socket;&lt;br /&gt;
	// On surveille les message entrant, la fermeture de la socket&lt;br /&gt;
	event.events = EPOLLIN | EPOLLRDHUP;&lt;br /&gt;
	epoll_ctl(*epfd, EPOLL_CTL_ADD, *socket, &amp;amp;event);&lt;br /&gt;
}&lt;br /&gt;
// Efface un descripteur de socket des événements EPOLL&lt;br /&gt;
void delEpollEvent(int * epfd, int * socket) {&lt;br /&gt;
	printf(&amp;quot;Suppression de la socket %i des événements EPOLL\n&amp;quot;, *socket);&lt;br /&gt;
	// Structure contenant les événements EPOLL&lt;br /&gt;
	struct epoll_event event;&lt;br /&gt;
	// On enregistre la socket du serveur comme descripteur EPOLL&lt;br /&gt;
	event.data.fd = *socket;&lt;br /&gt;
	// On surveille les message entrant, la fermeture de la socket&lt;br /&gt;
	event.events = EPOLLIN | EPOLLRDHUP;&lt;br /&gt;
	epoll_ctl(*epfd, EPOLL_CTL_DEL, *socket, &amp;amp;event);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Voici ce que l&#039;on peut observer dans la console:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Création de la socket&lt;br /&gt;
Paramètrage de la socket&lt;br /&gt;
Attachement de la socket sur le port 8080&lt;br /&gt;
Mise en écoute de la socket&lt;br /&gt;
Ajout de la socket 4 dans les événements EPOLL&lt;br /&gt;
Connexion de 127.0.0.1:17607&lt;br /&gt;
Ajout de la socket 5 dans les événements EPOLL&lt;br /&gt;
Suppression de la socket 5 des événements EPOLL&lt;br /&gt;
Fermeture de la connexion avec le client&lt;br /&gt;
Connexion de 127.0.0.1:18119&lt;br /&gt;
Ajout de la socket 5 dans les événements EPOLL&lt;br /&gt;
Connexion de 127.0.0.1:18631&lt;br /&gt;
Ajout de la socket 6 dans les événements EPOLL&lt;br /&gt;
Suppression de la socket 5 des événements EPOLL&lt;br /&gt;
Fermeture de la connexion avec le client&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Magsss</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=C_socket&amp;diff=3281</id>
		<title>C socket</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=C_socket&amp;diff=3281"/>
		<updated>2018-11-01T18:22:17Z</updated>

		<summary type="html">&lt;p&gt;Magsss : /* Écriture */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
||&lt;br /&gt;
Les sockets permettent de connecter deux programmes qui s&#039;exécutent sur deux machines différentes. Cette connexion se fait à travers le réseau grâce à l&#039;utilisation d&#039;un port (TCP ou UDP) et d&#039;une adresse IP. &lt;br /&gt;
&lt;br /&gt;
Il y a deux types de sockets, une qui est démarrée par la partie serveur en &#039;&#039;écoute&#039;&#039; et l&#039;autre démarrée par la partie cliente qui se connecte à la première.&lt;br /&gt;
&lt;br /&gt;
Ci-contre une image résumant les différentes étapes pour arriver à l&#039;envoi de données.&lt;br /&gt;
||&lt;br /&gt;
[[Fichier:Socket workflow.png|centré|200px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Création  =&lt;br /&gt;
== Côté serveur ==&lt;br /&gt;
Tout d&#039;abord il faut créer l&#039;objet socket:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt; &lt;br /&gt;
&lt;br /&gt;
int socket(int domain, int type, int protocol)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*domain &amp;amp;rarr; integer, communication domain e.g., AF_INET (IPv4 protocol) , AF_INET6 (IPv6 protocol)&lt;br /&gt;
*type &amp;amp;rarr; type de communication&lt;br /&gt;
**SOCK_STREAM: TCP&lt;br /&gt;
**SOCK_DGRAM: UDP&lt;br /&gt;
**SOCK_RAW: socket à l&#039;état brut (bas niveau)&lt;br /&gt;
*protocol &amp;amp;rarr; valeur du champ &#039;&#039;protocol&#039;&#039; de l&#039;en-tête de niveau 3 (généralement &#039;&#039;0&#039;&#039;)&lt;br /&gt;
*la valeur de retour est le fichier descripteur de la socket ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Une fois le descripteur de socket créé, il est possible de le configurer:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt; &lt;br /&gt;
&lt;br /&gt;
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* level &amp;amp;rarr; niveau d&#039;application de l&#039;option (SOL_SOCKET, TCP, UDP, ...);&lt;br /&gt;
* optname &amp;amp;rarr; le nom de l&#039;option (&#039;&#039;SO_REUSEADDR&#039;&#039;, &#039;&#039;SO_REUSEPORT&#039;&#039;, &#039;&#039;SO_KEEPALIVE&#039;&#039;, ...)&lt;br /&gt;
* optval, optlen  &amp;amp;rarr; utilisé pour accéder aux options de la socket;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
Il existe plusieurs options de socket, les plus utilisées étant :&lt;br /&gt;
* &#039;&#039;SO_REUSEADDR&#039;&#039; &amp;amp;rarr; permet de réutiliser l&#039;adresse de la socket tout de suite même si cette dernière est dans l&#039;état &#039;&#039;wait&#039;&#039;;&lt;br /&gt;
* &#039;&#039;SO_REUSEPORT&#039;&#039; &amp;amp;rarr; permet de réutiliser le port de la socket tout de suite même si cette dernière est dans l&#039;état &#039;&#039;wait&#039;&#039;;&lt;br /&gt;
* &#039;&#039;SO_KEEPALIVE&#039;&#039; &amp;amp;rarr; envoie des informations périodiquement pour tester si l&#039;extrémité du tunnel est toujours présente;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Il faut maintenant attacher la socket à un port Internet et une adresse IP:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* addr &amp;amp;rarr; une structure symbolisant l&#039;adresse &lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
struct sockaddr_in {&lt;br /&gt;
    short            sin_family;   // e.g: AF_INET&lt;br /&gt;
    unsigned short   sin_port;     // e.g: htons(3490)&lt;br /&gt;
    struct in_addr   sin_addr;     // détaillé ci-dessous&lt;br /&gt;
    char             sin_zero[8];  // &#039;0&#039; habituellement&lt;br /&gt;
};&lt;br /&gt;
struct in_addr {&lt;br /&gt;
    unsigned long s_addr;  // initialiser avec inet_aton()&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On peut maintenant passer la socket en état d&#039;écoute, elle est prête à recevoir des connexions:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int listen(int sockfd, int backlog)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* backlog &amp;amp;rarr; taille maximum de la file d&#039;attente de la socket après laquelle le système répond avec &#039;&#039;ECONNREFUSED&#039;&#039;;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On peut maintenant attendre une connexion:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* addr &amp;amp;rarr; l&#039;adresse du client;&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de la structure addr;&lt;br /&gt;
* le code retour varie entre un entier positif qui correspond au descripteur de la socket cliente et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
== Côté client ==&lt;br /&gt;
Après avoir créé la socket avec &#039;&#039;socket&#039;&#039; on peut se connecter à la partie serveur:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* addr &amp;amp;rarr; une structure symbolisant l&#039;adresse (cf. ci-dessus)&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
= Lecture / écriture =&lt;br /&gt;
== Fonctions génériques ==&lt;br /&gt;
Tout d&#039;abord, on peut dire que la socket est un fichier et se manipule donc comme un fichier !&lt;br /&gt;
===Lecture===&lt;br /&gt;
Pour lire dans une socket on peut utiliser &#039;&#039;read&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t read(int fd, void *buf, size_t count);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères où mettre le message reçu;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à recevoir;&lt;br /&gt;
* le code retour correspond au nombre de caractères reçus ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
===Écriture===&lt;br /&gt;
Pour écrire dans une socket ou peut utiliser &#039;&#039;write&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t write(int fd, const void *buf, size_t count);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères contenant le message à envoyer;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à envoyer;&lt;br /&gt;
* le code retour correspond au nombre de caractères envoyés ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
===Modification===&lt;br /&gt;
Pour modifier un descripteur fichier de socket on utilise &#039;&#039;fcntl&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int fcntl(int fd, int cmd, ... /* int args */ );&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* cmd &amp;amp;rarr; la commande de modification (descripteur, drapeaux, ...);&lt;br /&gt;
* args &amp;amp;rarr; les arguments correspondant à la commande&lt;br /&gt;
&lt;br /&gt;
Cette commande va nous permettre de modifier le drapeau du descripteur de la socket (&#039;&#039;F_SETFL&#039;&#039;) pour le rendre non bloquant (&#039;&#039;O_NONBLOCK&#039;&#039;) !&lt;br /&gt;
&lt;br /&gt;
== Fonctions spécifiques ==&lt;br /&gt;
Il existe des fonctions spécifiques pour manipuler les sockets. Ces fonctions ressemblent aux précédentes mais acceptent des drapeaux en plus pour pouvoir modifier le descripteur &#039;&#039;à la volée&#039;&#039;.&lt;br /&gt;
===Lecture===&lt;br /&gt;
Pour lire dans une socket on peut utiliser &#039;&#039;recv&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t recv(int fd, void *buf, size_t count, int flags);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères où mettre le message reçu;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à recevoir;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;O_NONBLOCK&#039;&#039;);&lt;br /&gt;
* le code retour correspond au nombre de caractères reçus ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
===Écriture===&lt;br /&gt;
Pour écrire dans une socket ou peut utiliser &#039;&#039;send&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t send(int fd, const void *buf, size_t count, int flags);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères contenant le message à envoyer;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à envoyer;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;MSG_CONFIRM&#039;&#039;, &#039;&#039;MSG_DONTWAIT &#039;&#039;, ...);&lt;br /&gt;
* le code retour correspond au nombre de caractères envoyés ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
= Cas d&#039;utilisation =&lt;br /&gt;
== Création ==&lt;br /&gt;
Pour commencer, il faut créer le descripteur de fichier:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int fdsocket;&lt;br /&gt;
if ((fdsocket = socket(AF_INET, SOCK_STREAM, 0)) == -1) {&lt;br /&gt;
	printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Paramétrage des options ==&lt;br /&gt;
Il faut maintenant configurer la réutilisation de l&#039;adresse et du port&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int opt = 1;&lt;br /&gt;
if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,	sizeof(opt)) != 0) {&lt;br /&gt;
	printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Attachement ==&lt;br /&gt;
On peut maintenant attacher la socket à un port et une adresse:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
struct sockaddr_in adresse;&lt;br /&gt;
&lt;br /&gt;
adresse.sin_family = AF_INET;&lt;br /&gt;
// Ecoute sur toutes les adresses (INADDR_ANY &amp;lt;=&amp;gt; 0.0.0.0)&lt;br /&gt;
adresse.sin_addr.s_addr = INADDR_ANY;&lt;br /&gt;
// Conversion du port en valeur réseaux (Host TO Network Short)&lt;br /&gt;
adresse.sin_port = htons(8080);&lt;br /&gt;
&lt;br /&gt;
if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
	printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mise en écoute ==&lt;br /&gt;
La socket est prête à passer à l&#039;écoute des connexions des clients:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
&lt;br /&gt;
if (listen(fdsocket, BACKLOG) != 0) {&lt;br /&gt;
	printf(&amp;quot;Echéc de la mise en écoute: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Acceptation des connexions ==&lt;br /&gt;
Il est possible d&#039;accepter les nouvelles connexions:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int clientSocket;&lt;br /&gt;
// Structure contenant l&#039;adresse du client&lt;br /&gt;
struct sockaddr_in clientAdresse;&lt;br /&gt;
int addrLen = sizeof(clientAdresse);&lt;br /&gt;
if ((clientSocket = accept(*serverSocket, (struct sockaddr*) &amp;amp;clientAdresse, (socklen_t*) &amp;amp;addrLen)) != -1) {&lt;br /&gt;
	// Convertion de l&#039;IP en texte&lt;br /&gt;
	char ip[INET_ADDRSTRLEN];&lt;br /&gt;
	inet_ntop(AF_INET, &amp;amp;(clientAdresse.sin_addr), ip, INET_ADDRSTRLEN);&lt;br /&gt;
	printf(&amp;quot;Connexion de %s:%i\n&amp;quot;, ip, clientAdresse.sin_port);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Il est possible d&#039;utiliser &#039;&#039;fcntl&#039;&#039; pour passer le descripteur de la socket en non bloquant, avant l&#039;appel à la fonction &#039;&#039;accept&#039;&#039;. Si le descripteur reste bloquant, la fonction &#039;&#039;accept&#039;&#039; ne rendra pas la main tant qu&#039;un client ne se sera connecté, ayant pour résultat le blocage complet du programme !&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Passage en mode non bloquant&lt;br /&gt;
fcntl(fdsocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
On peut remarquer, au passage, l&#039;utilisation de &#039;&#039;inet_ntop&#039;&#039; pour convertir l&#039;adresse du client du format binaire au format text.&lt;br /&gt;
&lt;br /&gt;
== Lecture / Écriture ==&lt;br /&gt;
=== Mode connecté (TCP) ===&lt;br /&gt;
On peut maintenant utiliser le descripteur de la socket du client pour lire et écrire.&lt;br /&gt;
* avec les fonctions génériques:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Descripteur de la socket du client&lt;br /&gt;
int clientSocket;&lt;br /&gt;
char buffer[BUFFER_LEN];&lt;br /&gt;
// Passage en mode non bloquant, sinon read attend&lt;br /&gt;
fcntl(clientSocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
int len = read(clientSocket, buffer, BUFFER_LEN);&lt;br /&gt;
&lt;br /&gt;
send(clientSocket, &amp;quot;Coucou\n&amp;quot;, strlen(&amp;quot;Coucou\n&amp;quot;));&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* avec les fonctions spécifiques:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Descripteur de la socket du client&lt;br /&gt;
int clientSocket;&lt;br /&gt;
char buffer[BUFFER_LEN];&lt;br /&gt;
&lt;br /&gt;
int len = recv(clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
&lt;br /&gt;
send(clientSocket, &amp;quot;Coucou\n&amp;quot;, strlen(&amp;quot;Coucou\n&amp;quot;), MSG_DONTWAIT);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Analysons le retour de la fonction &#039;&#039;read&#039;&#039; ou &#039;&#039;recv&#039;&#039; lorsque l&#039;on utilise un descripteur non bloquant d&#039;après la [http://man7.org/linux/man-pages/man2/recvmsg.2.html documentation] :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
NAME &lt;br /&gt;
       recv, recvfrom, recvmsg - receive a message from a socket&lt;br /&gt;
[...]&lt;br /&gt;
MSG_DONTWAIT (since Linux 2.2)&lt;br /&gt;
    Enables nonblocking operation; if the operation would block, the call fails with the error EAGAIN or EWOULDBLOCK (this can also be enabled using the O_NONBLOCK flag with the F_SETFL fcntl(2)). &lt;br /&gt;
[...]&lt;br /&gt;
RETURN VALUE&lt;br /&gt;
[...]&lt;br /&gt;
These calls return the number of bytes received, or -1 if an error&lt;br /&gt;
       occurred.  In the event of an error, errno is set to indicate the&lt;br /&gt;
       error.&lt;br /&gt;
When a stream socket peer has performed an orderly shutdown, the&lt;br /&gt;
       return value will be 0 (the traditional &amp;quot;end-of-file&amp;quot; return).&lt;br /&gt;
[...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Le retour est :&lt;br /&gt;
* lorsque le descripteur est vide (aucune donnée de la part du client) &#039;&#039;-1&#039;&#039; et &#039;&#039;errno&#039;&#039; sera positionné à la valeur &#039;&#039;EAGAIN&#039;&#039;, il ne faut donc pas fermer la socket si le retour est &#039;&#039;-1&#039;&#039; ! Il faut d&#039;abord vérifier que &#039;&#039;errno&#039;&#039; &#039;&#039;&#039;n&#039;a pas&#039;&#039;&#039; la valeur &#039;&#039;EAGAIN&#039;&#039;;&lt;br /&gt;
* lorsque le retour est &#039;&#039;0&#039;&#039;, cela signifie que l&#039;extrémité est fermée, il faut donc fermer le descripteur local;&lt;br /&gt;
* lorsque la valeur est supérieur à &#039;&#039;0&#039;&#039; elle correspond au nombre d&#039;octets reçu.&lt;br /&gt;
&lt;br /&gt;
On peut donc en déduire le morceau de code suivant pour gérer le retour de &#039;&#039;read&#039;&#039; ou &#039;&#039;recv&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int len = recv(clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
&lt;br /&gt;
if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
	// Une erreur est survenue&lt;br /&gt;
} else if (len == 0) {&lt;br /&gt;
	// Le client s&#039;est déconnecté (extrémité de la socket fermée)			&lt;br /&gt;
} else if (len &amp;gt; 0) {&lt;br /&gt;
	// Le client à envoyé des données	&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Mode datagramme (UDP) ===&lt;br /&gt;
===Lecture===&lt;br /&gt;
Pour lire dans une socket on peut utiliser &#039;&#039;recvfrom&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t recvfrom(int fd, void *buf, size_t count, int flags, const struct sockaddr *addr, socklen_t addrlen);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères ou mettre le message reçu;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à recevoir;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;O_NONBLOCK&#039;&#039;);&lt;br /&gt;
*  addr &amp;amp;rarr; une structure symbolisant l&#039;adresse du client (cf. ci-dessus)&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour correspond au nombre de caractères reçu ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
===Écriture===&lt;br /&gt;
Pour écrire dans une socket ou peut utiliser &#039;&#039;sendto&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t sendto(int fd, const void *buf, size_t count, const struct sockaddr *addr, socklen_t addrlen);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères contenant le message à envoyer;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à envoyer;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;MSG_DONTWAIT &#039;&#039;, ...);&lt;br /&gt;
* addr &amp;amp;rarr; une structure symbolisant l&#039;adresse du client (cf. ci-dessus)&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour correspond au nombre de caractères envoyer ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
= Exemples =&lt;br /&gt;
== Serveur TCP ==&lt;br /&gt;
Voici un exemple de serveur &#039;&#039;echo&#039;&#039; qui renvoie le message au client. Les connexions utilisent TCP et un simple client comme telnet suffit pour l&#039;utiliser.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;arpa/inet.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Port d&#039;écoute de la socket&lt;br /&gt;
#define PORT 8080&lt;br /&gt;
// Adresse d&#039;écoute (toutes les adresses)&lt;br /&gt;
#define IP INADDR_ANY&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
// Nombre de connexions clients&lt;br /&gt;
#define NB_CLIENTS 2&lt;br /&gt;
// Taille du tampon de lecture des messages&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Commande pour arrêter le serveur&lt;br /&gt;
#define EXIT_WORD &amp;quot;exit&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse);&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse);&lt;br /&gt;
int waitForClient(int * serverSocket);&lt;br /&gt;
void addClientToTab(int clientSocket, int clients[]);&lt;br /&gt;
void manageClient(int clients[]);&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
	// Création et initialisation du tableau contenant les descripteurs des sockets clients&lt;br /&gt;
	int clients[NB_CLIENTS];&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_CLIENTS; i++) {&lt;br /&gt;
		clients[i] = -1;&lt;br /&gt;
	}&lt;br /&gt;
	// Structure contenant l&#039;adresse&lt;br /&gt;
	struct sockaddr_in adresse;&lt;br /&gt;
	initAdresse(&amp;amp;adresse);&lt;br /&gt;
	// Descripteur de la socket du serveur&lt;br /&gt;
	int serverSocket = initSocket(&amp;amp;adresse);&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	while (1) {&lt;br /&gt;
		// Descripteur de la socket du client, on attend une connexion&lt;br /&gt;
		if ((clientSocket = waitForClient(&amp;amp;serverSocket)) != -1) {&lt;br /&gt;
			// On ajoute le nouveau client au tableau des descripteurs&lt;br /&gt;
			addClientToTab(clientSocket, clients);&lt;br /&gt;
		}&lt;br /&gt;
		manageClient(clients);&lt;br /&gt;
	}&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
// Initialisation de la structure sockaddr_in&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse) {&lt;br /&gt;
	(*adresse).sin_family = AF_INET;&lt;br /&gt;
	(*adresse).sin_addr.s_addr = IP;&lt;br /&gt;
	(*adresse).sin_port = htons( PORT);&lt;br /&gt;
}&lt;br /&gt;
// Démarrage de la socket serveur&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse) {&lt;br /&gt;
	// Descripteur de socket&lt;br /&gt;
	int fdsocket;&lt;br /&gt;
	// Nombre d&#039;options&lt;br /&gt;
	int opt = 1;&lt;br /&gt;
	// Création de la socket en TCP&lt;br /&gt;
	if ((fdsocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Création de la socket\n&amp;quot;);&lt;br /&gt;
	// Paramètrage de la socket&lt;br /&gt;
	if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,&lt;br /&gt;
			sizeof(opt)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Paramètrage de la socket\n&amp;quot;);&lt;br /&gt;
	// Attachement de la socket sur le port et l&#039;adresse IP&lt;br /&gt;
	if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Attachement de la socket sur le port %i\n&amp;quot;, PORT);&lt;br /&gt;
	// Passage en écoute de la socket&lt;br /&gt;
	if (listen(fdsocket, BACKLOG) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la mise en écoute: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	// Passage en mode non bloquant&lt;br /&gt;
	fcntl(fdsocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
	printf(&amp;quot;Mise en écoute de la socket\n&amp;quot;);&lt;br /&gt;
	return fdsocket;&lt;br /&gt;
}&lt;br /&gt;
// Attente de connexion d&#039;un client&lt;br /&gt;
int waitForClient(int * serverSocket) {&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	// Structure contenant l&#039;adresse du client&lt;br /&gt;
	struct sockaddr_in clientAdresse;&lt;br /&gt;
	int addrLen = sizeof(clientAdresse);&lt;br /&gt;
	if ((clientSocket = accept(*serverSocket, (struct sockaddr*) &amp;amp;clientAdresse,&lt;br /&gt;
			(socklen_t*) &amp;amp;addrLen)) != -1) {&lt;br /&gt;
		// Convertion de l&#039;IP en texte&lt;br /&gt;
		char ip[INET_ADDRSTRLEN];&lt;br /&gt;
		inet_ntop(AF_INET, &amp;amp;(clientAdresse.sin_addr), ip, INET_ADDRSTRLEN);&lt;br /&gt;
		printf(&amp;quot;Connexion de %s:%i\n&amp;quot;, ip, clientAdresse.sin_port);&lt;br /&gt;
		// Passage en mode non bloquant&lt;br /&gt;
		int opt = 1;&lt;br /&gt;
		setsockopt(clientSocket, SOL_SOCKET, SO_KEEPALIVE, &amp;amp;opt, sizeof(1));&lt;br /&gt;
	}&lt;br /&gt;
	return clientSocket;&lt;br /&gt;
}&lt;br /&gt;
// Ajoute les clients au tableau&lt;br /&gt;
void addClientToTab(int clientSocket, int clients[]) {&lt;br /&gt;
	// On vérifie si on à de la place de libre&lt;br /&gt;
	int found = -1;&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_CLIENTS; i++) {&lt;br /&gt;
		// On cherche de la place&lt;br /&gt;
		if (clients[i] == -1) {&lt;br /&gt;
			// On ajoute le client&lt;br /&gt;
			printf(&amp;quot;Ajout du client à l&#039;index %i\n&amp;quot;, i);&lt;br /&gt;
			clients[i] = clientSocket;&lt;br /&gt;
			// Nouvelle connexion, envoie du message de bienvenu&lt;br /&gt;
			send(clientSocket, &amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;, 	strlen(&amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;),&lt;br /&gt;
					MSG_DONTWAIT);&lt;br /&gt;
			found = 0;&lt;br /&gt;
			break;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	if (found == -1) {&lt;br /&gt;
		// On a plus de place de libre&lt;br /&gt;
		puts(&amp;quot;Plus de place, désolé&amp;quot;);&lt;br /&gt;
		close(clientSocket);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// On traite l&#039;input des clients&lt;br /&gt;
void manageClient(int clients[]) {&lt;br /&gt;
	// Création d&#039;un tampon pour stocker les messages des clients dans la heap&lt;br /&gt;
	static char buffer[BUFFER_LEN + 1];&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_CLIENTS; i++) {&lt;br /&gt;
		// On vérifie les messages&lt;br /&gt;
		int clientSocket = clients[i];&lt;br /&gt;
		if (clientSocket == -1) {&lt;br /&gt;
			continue;&lt;br /&gt;
		}&lt;br /&gt;
		int len = recv(clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
		// Booléen pour suivre l&#039;état de la socket&lt;br /&gt;
		int isClosed = 0;&lt;br /&gt;
		if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
			// Une erreur est survenue&lt;br /&gt;
			printf(&amp;quot;Errno [%i] : %s\n&amp;quot;, errno, strerror(errno));&lt;br /&gt;
			isClosed = 1;&lt;br /&gt;
		} else if (len == 0) {&lt;br /&gt;
			// Le client s&#039;est déconnecté (extrémité de la socket fermée)&lt;br /&gt;
			isClosed = 1;&lt;br /&gt;
		} else if (len &amp;gt; 0) {&lt;br /&gt;
			// Ajout du terminateur de chaîne&lt;br /&gt;
			buffer[len] = &#039;\0&#039;;&lt;br /&gt;
			if (strncmp(buffer, EXIT_WORD, 4) == 0) {&lt;br /&gt;
				// Le client veut se déconnecter&lt;br /&gt;
				send(clientSocket, &amp;quot;Bye\n&amp;quot;, strlen(&amp;quot;Bye\n&amp;quot;), 0);&lt;br /&gt;
				isClosed = 1;&lt;br /&gt;
			} else {&lt;br /&gt;
				// On renvoie le texte au client dans un buffer assez grand&lt;br /&gt;
				int len = strlen(&amp;quot;Vous avez dit : &amp;quot;) + strlen(buffer) + 1;&lt;br /&gt;
				char response[len];&lt;br /&gt;
				strcpy(response, &amp;quot;Vous avez dit : &amp;quot;);&lt;br /&gt;
				strcat(response, buffer);&lt;br /&gt;
				// Un seul envoie permet de ne pas surcharger le réseau&lt;br /&gt;
				send(clientSocket, response, strlen(response), 0);&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
		if (isClosed == 1) {&lt;br /&gt;
			// La socket est fermé ou le client veut quitter le serveur !&lt;br /&gt;
			printf(&amp;quot;Fermeture de la connexion avec le client\n&amp;quot;);&lt;br /&gt;
			// Fermeture de la socket&lt;br /&gt;
			close(clientSocket);&lt;br /&gt;
			// On fait de la place dans le tableau&lt;br /&gt;
			clients[i] = -1;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On peut interroger notre serveur avec &#039;&#039;telnet&#039;&#039; (&#039;&#039;yum -y install telnet&#039;&#039;):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# telnet 127.0.0.1 8080&lt;br /&gt;
Trying 127.0.0.1...&lt;br /&gt;
Connected to 127.0.0.1.&lt;br /&gt;
Escape character is &#039;^]&#039;.&lt;br /&gt;
Entrez &#039;exit&#039; pour quitter&lt;br /&gt;
coucou&lt;br /&gt;
Vous avez dit : coucou&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== Serveur UDP ==&lt;br /&gt;
Si on essaye de reproduire le même type de serveur mais cette fois-ci avec une socket en UDP, cela devient beaucoup plus simple. En effet, en UDP, plus besoin de tenir un tableau avec les descripteurs des sockets des clients car... on est en mode déconnecté (datagramme) ! Entendez par la que les clients envoie un message sans attendre de réponse et sans se soucier de savoir si le message est arrivé au serveur.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;arpa/inet.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Port d&#039;écoute de la socket&lt;br /&gt;
#define PORT 8080&lt;br /&gt;
// Adresse d&#039;écoute (toutes les adresses)&lt;br /&gt;
#define IP INADDR_ANY&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
// Taille du tampon de lecture des messages&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse);&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse);&lt;br /&gt;
void manageClient(int serverSocket);&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
	// Structure contenant l&#039;adresse&lt;br /&gt;
	struct sockaddr_in adresse;&lt;br /&gt;
	initAdresse(&amp;amp;adresse);&lt;br /&gt;
	// Descripteur de la socket du serveur&lt;br /&gt;
	int serverSocket = initSocket(&amp;amp;adresse);&lt;br /&gt;
	while (1) {&lt;br /&gt;
		// Descripteur de la socket du client, on attend une connexion&lt;br /&gt;
		manageClient(serverSocket);&lt;br /&gt;
	}&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
// Initialisation de la structure sockaddr_in&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse) {&lt;br /&gt;
	(*adresse).sin_family = AF_INET;&lt;br /&gt;
	(*adresse).sin_addr.s_addr = IP;&lt;br /&gt;
	(*adresse).sin_port = htons( PORT);&lt;br /&gt;
}&lt;br /&gt;
// Démarrage de la socket serveur&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse) {&lt;br /&gt;
	// Descripteur de socket&lt;br /&gt;
	int fdsocket;&lt;br /&gt;
	// Nombre d&#039;options&lt;br /&gt;
	int opt = 1;&lt;br /&gt;
	// Création de la socket en UDP&lt;br /&gt;
	if ((fdsocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Création de la socket\n&amp;quot;);&lt;br /&gt;
	// Paramètrage de la socket&lt;br /&gt;
	if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,&lt;br /&gt;
			sizeof(opt)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Paramètrage de la socket\n&amp;quot;);&lt;br /&gt;
	// Attachement de la socket sur le port et l&#039;adresse IP&lt;br /&gt;
	if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Attachement de la socket sur le port %i\n&amp;quot;, PORT);&lt;br /&gt;
	return fdsocket;&lt;br /&gt;
}&lt;br /&gt;
// On traite l&#039;input des clients&lt;br /&gt;
void manageClient(int serverSocket) {&lt;br /&gt;
	// Création d&#039;un tampon pour stocker les messages des clients dans la heap&lt;br /&gt;
	static char buffer[BUFFER_LEN + 1];&lt;br /&gt;
	// Structure contenant l&#039;adresse du client&lt;br /&gt;
	struct sockaddr_in clientAdresse;&lt;br /&gt;
	unsigned int addrLen = sizeof(clientAdresse);&lt;br /&gt;
	// On vérifie si on a reçu un message&lt;br /&gt;
	int len = recvfrom(serverSocket, buffer, BUFFER_LEN, MSG_DONTWAIT,&lt;br /&gt;
			(struct sockaddr*) &amp;amp;clientAdresse, &amp;amp;addrLen);&lt;br /&gt;
	if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
		printf(&amp;quot;Problème de socket %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	} else if (len &amp;gt; 0) {&lt;br /&gt;
		printf(&amp;quot;Message reçu de %s:%i\n&amp;quot;, inet_ntoa(clientAdresse.sin_addr),&lt;br /&gt;
				ntohs(clientAdresse.sin_port));&lt;br /&gt;
		// Ajout du terminateur de chaîne&lt;br /&gt;
		buffer[len] = &#039;\0&#039;;&lt;br /&gt;
		// On renvoie le texte au client dans un buffer assez grand&lt;br /&gt;
		len = strlen(&amp;quot;Vous avez dit : &amp;quot;) + strlen(buffer) + 1;&lt;br /&gt;
		char response[len];&lt;br /&gt;
		strcpy(response, &amp;quot;Vous avez dit : &amp;quot;);&lt;br /&gt;
		strcat(response, buffer);&lt;br /&gt;
		// Un seul envoie permet de ne pas surcharger le réseau&lt;br /&gt;
		sendto(serverSocket, response, strlen(response), MSG_DONTWAIT,&lt;br /&gt;
				(struct sockaddr*) &amp;amp;clientAdresse, addrLen);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
On peut interroger notre serveur avec &#039;&#039;netcat&#039;&#039; (&#039;&#039;yum -y install nc&#039;&#039;):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# nc -u 127.0.0.1 8080&lt;br /&gt;
coucou&lt;br /&gt;
Vous avez dit : coucou&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
= Notification évènementielle avec epoll =&lt;br /&gt;
Depuis la version 2.5 du noyau Linux il est possible d&#039;utiliser une série de fonctions, appartenant à l&#039;espace utilisateur (user-space), permettant de suivre l&#039;état d&#039;un descripteur de fichier.&lt;br /&gt;
La différence avec read, recv ou encore recvfrom c&#039;est que l&#039;on va pouvoir surveiller plusieurs descripteurs de fichiers en même temps, laissant le CPU libre pour effectuer autre chose !&lt;br /&gt;
&lt;br /&gt;
== Utilisation ==&lt;br /&gt;
Tout d&#039;abord il faut créer un descripteur de fichier de type &#039;&#039;epoll&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int epoll_create1(int flags);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* flags &amp;amp;rarr; EPOLL_CLOEXEC pour éviter les situation de compétition en environnement parallélisé;&lt;br /&gt;
* le code retour correspond au descripteur &#039;&#039;epoll&#039;&#039; ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée).&lt;br /&gt;
&lt;br /&gt;
Une fois le descripteur &#039;&#039;epoll&#039;&#039; créé, il faut lui associer un événement:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* epfd &amp;amp;rarr; le descripteur &#039;&#039;epoll&#039;&#039; à modifier;&lt;br /&gt;
* op &amp;amp;rarr; une opération parmis:&lt;br /&gt;
** EPOLL_CTL_ADD &amp;amp;rarr; pour ajouter un événement sur un descripteur de fichier;&lt;br /&gt;
** EPOLL_CTL_MOD &amp;amp;rarr; pour modifier un événement sur un descripteur de fichier;&lt;br /&gt;
** EPOLL_CTL_DEL &amp;amp;rarr; pour supprimer un événement sur un descripteur de fichier;&lt;br /&gt;
* fd &amp;amp;rarr; un descripteur de fichier à surveiller;&lt;br /&gt;
* event &amp;amp;rarr; la structure contenant l&#039;événement à surveiller (&#039;&#039;EPOLLIN&#039;&#039;, &#039;&#039;EPOLLOUT&#039;&#039;, EPOLLRDHUP&#039;&#039;, ...);&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; en cas de succès ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée).&lt;br /&gt;
&lt;br /&gt;
Voici la structure &#039;&#039;epoll_event&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
struct epoll_event {&lt;br /&gt;
	uint32_t events;    /* Epoll events */&lt;br /&gt;
	epoll_data_t data;  /* User data variable */&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
typedef union epoll_data {&lt;br /&gt;
	void    *ptr;&lt;br /&gt;
	int      fd;&lt;br /&gt;
	uint32_t u32;&lt;br /&gt;
	uint64_t u64;&lt;br /&gt;
} epoll_data_t;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Enfin, il ne reste plus qu&#039;à &#039;&#039;attendre&#039;&#039; des événements:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* epfd &amp;amp;rarr; le descripteur &#039;&#039;epoll&#039;&#039; à modifier;&lt;br /&gt;
* events &amp;amp;rarr; un tableau de &#039;&#039;epoll_event contenant les événements;&lt;br /&gt;
* maxevents &amp;amp;rarr; le nombre maximum d&#039;événements à traiter (selon la taille du tableau);&lt;br /&gt;
* timeout &amp;amp;rarr; le temps, en millisecondes que la fonction attend:&lt;br /&gt;
** &#039;&#039;-1&#039;&#039; &amp;amp;rarr; la fonction devient bloquante;&lt;br /&gt;
** &#039;&#039;0&#039;&#039; &amp;amp;rarr; retourne immédiatement;&lt;br /&gt;
** &#039;&#039;&amp;gt; 0&#039;&#039; &amp;amp;rarr; attend le nombre de millisecondes spécifiées;&lt;br /&gt;
* le code retour correspond au nombre d&#039;événements ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée).&lt;br /&gt;
&lt;br /&gt;
== Améliorations architecturales ==&lt;br /&gt;
=== Gestion des utilisateurs===&lt;br /&gt;
Tout d&#039;abord, le faite de laisser &#039;&#039;epoll&#039;&#039; gérer les descripteurs de socket nous permet de supprimer le tableau &#039;&#039;clients&#039;&#039; ainsi que la variable &#039;&#039;NB_CLIENTS&#039;&#039;. En effet, la référence vers le descripteur est maintenu par &#039;&#039;epoll&#039;&#039; et nous pouvons gérer maintenant un nombre &#039;&#039;illimité&#039;&#039; de clients.&lt;br /&gt;
=== Performances ===&lt;br /&gt;
La boucle &#039;&#039;while&#039;&#039; de notre serveur consomme énormément de CPU, environ 100% de CPU par client ! On aurais pu utiliser un temporisateur avec &#039;&#039;nanosleep&#039;&#039; pour baisser la consommation CPU entre 5% et 10%...&lt;br /&gt;
Le faîte de basculer sur une fonction bloquante comme &#039;&#039;epoll_wait&#039;&#039; qui utilise des signaux (interruptions) permet de réduire considérablement la consommation CPU. Gardez à l&#039;esprit qu&#039;il n&#039;y a rien de mieux que les interruptions, la preuve, lorsque l&#039;on fait un &#039;&#039;top&#039;&#039;, notre programme à complétement disparu de la liste !&lt;br /&gt;
&lt;br /&gt;
== Modification du serveur ==&lt;br /&gt;
Modifions le code du serveur TCP pour y intégrer &#039;&#039;epoll&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;arpa/inet.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Port d&#039;écoute de la socket&lt;br /&gt;
#define PORT 8080&lt;br /&gt;
// Adresse d&#039;écoute (toutes les adresses)&lt;br /&gt;
#define IP INADDR_ANY&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
// Nombre de connexions clients&lt;br /&gt;
#define NB_EVENTS 2&lt;br /&gt;
// Taille du tampon de lecture des messages&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Commande pour arrêter le serveur&lt;br /&gt;
#define EXIT_WORD &amp;quot;exit&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse);&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse);&lt;br /&gt;
int waitForClient(int * serverSocket);&lt;br /&gt;
void addClientToTab(int * epfd, int * clientSocket);&lt;br /&gt;
void manageClient(int * epfd, int * clientSocket);&lt;br /&gt;
void addEpollEvent(int * epfd, int * socket);&lt;br /&gt;
void delEpollEvent(int * epfd, int * socket);&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
	// Création du descripteur EPOLL&lt;br /&gt;
	int epfd = epoll_create1(0);&lt;br /&gt;
	// Structure contenant les événements EPOLL&lt;br /&gt;
	struct epoll_event events[NB_EVENTS];&lt;br /&gt;
	// Structure contenant l&#039;adresse&lt;br /&gt;
	struct sockaddr_in adresse;&lt;br /&gt;
	initAdresse(&amp;amp;adresse);&lt;br /&gt;
	// Descripteur de la socket du serveur&lt;br /&gt;
	int serverSocket = initSocket(&amp;amp;adresse);&lt;br /&gt;
	addEpollEvent(&amp;amp;epfd, &amp;amp;serverSocket);&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	while (1) {&lt;br /&gt;
		int nfds = epoll_wait(epfd, events, NB_EVENTS, -1);&lt;br /&gt;
		for (int i = 0; i &amp;lt; nfds; i++) {&lt;br /&gt;
			if (events[i].data.fd == serverSocket) {&lt;br /&gt;
				if ((clientSocket = waitForClient(&amp;amp;serverSocket)) != -1) {&lt;br /&gt;
					// On ajoute le nouveau client au tableau des descripteurs&lt;br /&gt;
					addClientToTab(&amp;amp;epfd, &amp;amp;clientSocket);&lt;br /&gt;
				}&lt;br /&gt;
			} else if (events[i].events &amp;amp; EPOLLIN) {&lt;br /&gt;
				// On a reçu quelque chose&lt;br /&gt;
				manageClient(&amp;amp;epfd, &amp;amp;events[i].data.fd);&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	close(serverSocket);&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
// Initialisation de la structure sockaddr_in&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse) {&lt;br /&gt;
	(*adresse).sin_family = AF_INET;&lt;br /&gt;
	(*adresse).sin_addr.s_addr = IP;&lt;br /&gt;
	(*adresse).sin_port = htons( PORT);&lt;br /&gt;
}&lt;br /&gt;
// Démarrage de la socket serveur&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse) {&lt;br /&gt;
	// Descripteur de socket&lt;br /&gt;
	int fdsocket;&lt;br /&gt;
	// Nombre d&#039;options&lt;br /&gt;
	int opt = 1;&lt;br /&gt;
	// Création de la socket en TCP&lt;br /&gt;
	if ((fdsocket = socket(AF_INET, SOCK_STREAM, 0)) == -1) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Création de la socket\n&amp;quot;);&lt;br /&gt;
	// Paramètrage de la socket&lt;br /&gt;
	if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,&lt;br /&gt;
			sizeof(opt)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Paramètrage de la socket\n&amp;quot;);&lt;br /&gt;
	// Attachement de la socket sur le port et l&#039;adresse IP&lt;br /&gt;
	if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Attachement de la socket sur le port %i\n&amp;quot;, PORT);&lt;br /&gt;
	// Passage en écoute de la socket&lt;br /&gt;
	if (listen(fdsocket, BACKLOG) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la mise en écoute: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	// Passage en mode non bloquant&lt;br /&gt;
	fcntl(fdsocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
	printf(&amp;quot;Mise en écoute de la socket\n&amp;quot;);&lt;br /&gt;
	return fdsocket;&lt;br /&gt;
}&lt;br /&gt;
// Attente de connexion d&#039;un client&lt;br /&gt;
int waitForClient(int * serverSocket) {&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	// Structure contenant l&#039;adresse du client&lt;br /&gt;
	struct sockaddr_in clientAdresse;&lt;br /&gt;
	int addrLen = sizeof(clientAdresse);&lt;br /&gt;
	if ((clientSocket = accept(*serverSocket, (struct sockaddr*) &amp;amp;clientAdresse,&lt;br /&gt;
			(socklen_t*) &amp;amp;addrLen)) != -1) {&lt;br /&gt;
		// Convertion de l&#039;IP en texte&lt;br /&gt;
		char ip[INET_ADDRSTRLEN];&lt;br /&gt;
		inet_ntop(AF_INET, &amp;amp;(clientAdresse.sin_addr), ip, INET_ADDRSTRLEN);&lt;br /&gt;
		printf(&amp;quot;Connexion de %s:%i\n&amp;quot;, ip, clientAdresse.sin_port);&lt;br /&gt;
		// Passage en mode non bloquant&lt;br /&gt;
		int opt = 1;&lt;br /&gt;
		setsockopt(clientSocket, SOL_SOCKET, SO_KEEPALIVE, &amp;amp;opt, sizeof(1));&lt;br /&gt;
	}&lt;br /&gt;
	return clientSocket;&lt;br /&gt;
}&lt;br /&gt;
// Ajoute les clients au tableau&lt;br /&gt;
void addClientToTab(int * epfd, int * clientSocket) {&lt;br /&gt;
	addEpollEvent(epfd, clientSocket);&lt;br /&gt;
	// Nouvelle connexion, envoie du message de bienvenu&lt;br /&gt;
	send(*clientSocket, &amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;,&lt;br /&gt;
			strlen(&amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;), MSG_DONTWAIT);&lt;br /&gt;
}&lt;br /&gt;
// On traite l&#039;input des clients&lt;br /&gt;
void manageClient(int * epfd, int * clientSocket) {&lt;br /&gt;
	// Création d&#039;un tampon pour stocker les messages des clients dans la heap&lt;br /&gt;
	static char buffer[BUFFER_LEN + 1];&lt;br /&gt;
	int len = recv(*clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
	// Booléen pour suivre l&#039;état de la socket&lt;br /&gt;
	int isClosed = 0;&lt;br /&gt;
	if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
		// Une erreur est survenue&lt;br /&gt;
		printf(&amp;quot;Errno [%i] : %s\n&amp;quot;, errno, strerror(errno));&lt;br /&gt;
		isClosed = 1;&lt;br /&gt;
	} else if (len == 0) {&lt;br /&gt;
		// Le client s&#039;est déconnecté (extrémité de la socket fermée)&lt;br /&gt;
		isClosed = 1;&lt;br /&gt;
	} else if (len &amp;gt; 0) {&lt;br /&gt;
		// Ajout du terminateur de chaîne&lt;br /&gt;
		buffer[len] = &#039;\0&#039;;&lt;br /&gt;
		if (strncmp(buffer, EXIT_WORD, 4) == 0) {&lt;br /&gt;
			// Le client veut se déconnecter&lt;br /&gt;
			send(*clientSocket, &amp;quot;Bye\n&amp;quot;, strlen(&amp;quot;Bye\n&amp;quot;), 0);&lt;br /&gt;
			isClosed = 1;&lt;br /&gt;
		} else {&lt;br /&gt;
			// On renvoie le texte au client dans un buffer assez grand&lt;br /&gt;
			int len = strlen(&amp;quot;Vous avez dit : &amp;quot;) + strlen(buffer) + 1;&lt;br /&gt;
			char response[len];&lt;br /&gt;
			strcpy(response, &amp;quot;Vous avez dit : &amp;quot;);&lt;br /&gt;
			strcat(response, buffer);&lt;br /&gt;
			// Un seul envoie permet de ne pas surcharger le réseau&lt;br /&gt;
			send(*clientSocket, response, strlen(response), 0);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	if (isClosed == 1) {&lt;br /&gt;
		// On enlève la socket des événements EPOLL&lt;br /&gt;
		delEpollEvent(epfd, clientSocket);&lt;br /&gt;
		// La socket est fermé ou le client veut quitter le serveur !&lt;br /&gt;
		printf(&amp;quot;Fermeture de la connexion avec le client\n&amp;quot;);&lt;br /&gt;
		// Fermeture de la socket&lt;br /&gt;
		close(*clientSocket);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// Ajoute un descripteur de socket aux événements EPOLL&lt;br /&gt;
void addEpollEvent(int * epfd, int * socket) {&lt;br /&gt;
	printf(&amp;quot;Ajout de la socket %i dans les événements EPOLL\n&amp;quot;, *socket);&lt;br /&gt;
	// Structure contenant les événements EPOLL&lt;br /&gt;
	struct epoll_event event;&lt;br /&gt;
	// On enregistre la socket du serveur comme descripteur EPOLL&lt;br /&gt;
	event.data.fd = *socket;&lt;br /&gt;
	// On surveille les message entrant, la fermeture de la socket&lt;br /&gt;
	event.events = EPOLLIN | EPOLLRDHUP;&lt;br /&gt;
	epoll_ctl(*epfd, EPOLL_CTL_ADD, *socket, &amp;amp;event);&lt;br /&gt;
}&lt;br /&gt;
// Efface un descripteur de socket des événements EPOLL&lt;br /&gt;
void delEpollEvent(int * epfd, int * socket) {&lt;br /&gt;
	printf(&amp;quot;Suppression de la socket %i des événements EPOLL\n&amp;quot;, *socket);&lt;br /&gt;
	// Structure contenant les événements EPOLL&lt;br /&gt;
	struct epoll_event event;&lt;br /&gt;
	// On enregistre la socket du serveur comme descripteur EPOLL&lt;br /&gt;
	event.data.fd = *socket;&lt;br /&gt;
	// On surveille les message entrant, la fermeture de la socket&lt;br /&gt;
	event.events = EPOLLIN | EPOLLRDHUP;&lt;br /&gt;
	epoll_ctl(*epfd, EPOLL_CTL_DEL, *socket, &amp;amp;event);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Voici ce que l&#039;on peut observer dans la console:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Création de la socket&lt;br /&gt;
Paramètrage de la socket&lt;br /&gt;
Attachement de la socket sur le port 8080&lt;br /&gt;
Mise en écoute de la socket&lt;br /&gt;
Ajout de la socket 4 dans les événements EPOLL&lt;br /&gt;
Connexion de 127.0.0.1:17607&lt;br /&gt;
Ajout de la socket 5 dans les événements EPOLL&lt;br /&gt;
Suppression de la socket 5 des événements EPOLL&lt;br /&gt;
Fermeture de la connexion avec le client&lt;br /&gt;
Connexion de 127.0.0.1:18119&lt;br /&gt;
Ajout de la socket 5 dans les événements EPOLL&lt;br /&gt;
Connexion de 127.0.0.1:18631&lt;br /&gt;
Ajout de la socket 6 dans les événements EPOLL&lt;br /&gt;
Suppression de la socket 5 des événements EPOLL&lt;br /&gt;
Fermeture de la connexion avec le client&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Magsss</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=C_socket&amp;diff=3280</id>
		<title>C socket</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=C_socket&amp;diff=3280"/>
		<updated>2018-11-01T18:20:43Z</updated>

		<summary type="html">&lt;p&gt;Magsss : /* Lecture */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
||&lt;br /&gt;
Les sockets permettent de connecter deux programmes qui s&#039;exécutent sur deux machines différentes. Cette connexion se fait à travers le réseau grâce à l&#039;utilisation d&#039;un port (TCP ou UDP) et d&#039;une adresse IP. &lt;br /&gt;
&lt;br /&gt;
Il y a deux types de sockets, une qui est démarrée par la partie serveur en &#039;&#039;écoute&#039;&#039; et l&#039;autre démarrée par la partie cliente qui se connecte à la première.&lt;br /&gt;
&lt;br /&gt;
Ci-contre une image résumant les différentes étapes pour arriver à l&#039;envoi de données.&lt;br /&gt;
||&lt;br /&gt;
[[Fichier:Socket workflow.png|centré|200px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Création  =&lt;br /&gt;
== Côté serveur ==&lt;br /&gt;
Tout d&#039;abord il faut créer l&#039;objet socket:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt; &lt;br /&gt;
&lt;br /&gt;
int socket(int domain, int type, int protocol)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*domain &amp;amp;rarr; integer, communication domain e.g., AF_INET (IPv4 protocol) , AF_INET6 (IPv6 protocol)&lt;br /&gt;
*type &amp;amp;rarr; type de communication&lt;br /&gt;
**SOCK_STREAM: TCP&lt;br /&gt;
**SOCK_DGRAM: UDP&lt;br /&gt;
**SOCK_RAW: socket à l&#039;état brut (bas niveau)&lt;br /&gt;
*protocol &amp;amp;rarr; valeur du champ &#039;&#039;protocol&#039;&#039; de l&#039;en-tête de niveau 3 (généralement &#039;&#039;0&#039;&#039;)&lt;br /&gt;
*la valeur de retour est le fichier descripteur de la socket ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Une fois le descripteur de socket créé, il est possible de le configurer:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt; &lt;br /&gt;
&lt;br /&gt;
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* level &amp;amp;rarr; niveau d&#039;application de l&#039;option (SOL_SOCKET, TCP, UDP, ...);&lt;br /&gt;
* optname &amp;amp;rarr; le nom de l&#039;option (&#039;&#039;SO_REUSEADDR&#039;&#039;, &#039;&#039;SO_REUSEPORT&#039;&#039;, &#039;&#039;SO_KEEPALIVE&#039;&#039;, ...)&lt;br /&gt;
* optval, optlen  &amp;amp;rarr; utilisé pour accéder aux options de la socket;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
Il existe plusieurs options de socket, les plus utilisées étant :&lt;br /&gt;
* &#039;&#039;SO_REUSEADDR&#039;&#039; &amp;amp;rarr; permet de réutiliser l&#039;adresse de la socket tout de suite même si cette dernière est dans l&#039;état &#039;&#039;wait&#039;&#039;;&lt;br /&gt;
* &#039;&#039;SO_REUSEPORT&#039;&#039; &amp;amp;rarr; permet de réutiliser le port de la socket tout de suite même si cette dernière est dans l&#039;état &#039;&#039;wait&#039;&#039;;&lt;br /&gt;
* &#039;&#039;SO_KEEPALIVE&#039;&#039; &amp;amp;rarr; envoie des informations périodiquement pour tester si l&#039;extrémité du tunnel est toujours présente;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Il faut maintenant attacher la socket à un port Internet et une adresse IP:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* addr &amp;amp;rarr; une structure symbolisant l&#039;adresse &lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
struct sockaddr_in {&lt;br /&gt;
    short            sin_family;   // e.g: AF_INET&lt;br /&gt;
    unsigned short   sin_port;     // e.g: htons(3490)&lt;br /&gt;
    struct in_addr   sin_addr;     // détaillé ci-dessous&lt;br /&gt;
    char             sin_zero[8];  // &#039;0&#039; habituellement&lt;br /&gt;
};&lt;br /&gt;
struct in_addr {&lt;br /&gt;
    unsigned long s_addr;  // initialiser avec inet_aton()&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On peut maintenant passer la socket en état d&#039;écoute, elle est prête à recevoir des connexions:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int listen(int sockfd, int backlog)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* backlog &amp;amp;rarr; taille maximum de la file d&#039;attente de la socket après laquelle le système répond avec &#039;&#039;ECONNREFUSED&#039;&#039;;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On peut maintenant attendre une connexion:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* addr &amp;amp;rarr; l&#039;adresse du client;&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de la structure addr;&lt;br /&gt;
* le code retour varie entre un entier positif qui correspond au descripteur de la socket cliente et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
== Côté client ==&lt;br /&gt;
Après avoir créé la socket avec &#039;&#039;socket&#039;&#039; on peut se connecter à la partie serveur:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* addr &amp;amp;rarr; une structure symbolisant l&#039;adresse (cf. ci-dessus)&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
= Lecture / écriture =&lt;br /&gt;
== Fonctions génériques ==&lt;br /&gt;
Tout d&#039;abord, on peut dire que la socket est un fichier et se manipule donc comme un fichier !&lt;br /&gt;
===Lecture===&lt;br /&gt;
Pour lire dans une socket on peut utiliser &#039;&#039;read&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t read(int fd, void *buf, size_t count);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères où mettre le message reçu;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à recevoir;&lt;br /&gt;
* le code retour correspond au nombre de caractères reçus ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
===Écriture===&lt;br /&gt;
Pour écrire dans une socket ou peut utiliser &#039;&#039;write&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t write(int fd, const void *buf, size_t count);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères contenant le message à envoyer;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à envoyer;&lt;br /&gt;
* le code retour correspond au nombre de caractères envoyés ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
===Modification===&lt;br /&gt;
Pour modifier un descripteur fichier de socket on utilise &#039;&#039;fcntl&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int fcntl(int fd, int cmd, ... /* int args */ );&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* cmd &amp;amp;rarr; la commande de modification (descripteur, drapeaux, ...);&lt;br /&gt;
* args &amp;amp;rarr; les arguments correspondant à la commande&lt;br /&gt;
&lt;br /&gt;
Cette commande va nous permettre de modifier le drapeau du descripteur de la socket (&#039;&#039;F_SETFL&#039;&#039;) pour le rendre non bloquant (&#039;&#039;O_NONBLOCK&#039;&#039;) !&lt;br /&gt;
&lt;br /&gt;
== Fonctions spécifiques ==&lt;br /&gt;
Il existe des fonctions spécifiques pour manipuler les sockets. Ces fonctions ressemblent aux précédentes mais acceptent des drapeaux en plus pour pouvoir modifier le descripteur &#039;&#039;à la volée&#039;&#039;.&lt;br /&gt;
===Lecture===&lt;br /&gt;
Pour lire dans une socket on peut utiliser &#039;&#039;recv&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t recv(int fd, void *buf, size_t count, int flags);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères où mettre le message reçu;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à recevoir;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;O_NONBLOCK&#039;&#039;);&lt;br /&gt;
* le code retour correspond au nombre de caractères reçus ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
===Écriture===&lt;br /&gt;
Pour écrire dans une socket ou peut utiliser &#039;&#039;send&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t send(int fd, const void *buf, size_t count, int flags);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères contenant le message à envoyer;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à envoyer;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;MSG_CONFIRM&#039;&#039;, &#039;&#039;MSG_DONTWAIT &#039;&#039;, ...);&lt;br /&gt;
* le code retour correspond au nombre de caractères envoyer ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
= Cas d&#039;utilisation =&lt;br /&gt;
== Création ==&lt;br /&gt;
Pour commencer, il faut créer le descripteur de fichier:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int fdsocket;&lt;br /&gt;
if ((fdsocket = socket(AF_INET, SOCK_STREAM, 0)) == -1) {&lt;br /&gt;
	printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Paramétrage des options ==&lt;br /&gt;
Il faut maintenant configurer la réutilisation de l&#039;adresse et du port&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int opt = 1;&lt;br /&gt;
if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,	sizeof(opt)) != 0) {&lt;br /&gt;
	printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Attachement ==&lt;br /&gt;
On peut maintenant attacher la socket à un port et une adresse:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
struct sockaddr_in adresse;&lt;br /&gt;
&lt;br /&gt;
adresse.sin_family = AF_INET;&lt;br /&gt;
// Ecoute sur toutes les adresses (INADDR_ANY &amp;lt;=&amp;gt; 0.0.0.0)&lt;br /&gt;
adresse.sin_addr.s_addr = INADDR_ANY;&lt;br /&gt;
// Conversion du port en valeur réseaux (Host TO Network Short)&lt;br /&gt;
adresse.sin_port = htons(8080);&lt;br /&gt;
&lt;br /&gt;
if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
	printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mise en écoute ==&lt;br /&gt;
La socket est prête à passer à l&#039;écoute des connexions des clients:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
&lt;br /&gt;
if (listen(fdsocket, BACKLOG) != 0) {&lt;br /&gt;
	printf(&amp;quot;Echéc de la mise en écoute: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Acceptation des connexions ==&lt;br /&gt;
Il est possible d&#039;accepter les nouvelles connexions:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int clientSocket;&lt;br /&gt;
// Structure contenant l&#039;adresse du client&lt;br /&gt;
struct sockaddr_in clientAdresse;&lt;br /&gt;
int addrLen = sizeof(clientAdresse);&lt;br /&gt;
if ((clientSocket = accept(*serverSocket, (struct sockaddr*) &amp;amp;clientAdresse, (socklen_t*) &amp;amp;addrLen)) != -1) {&lt;br /&gt;
	// Convertion de l&#039;IP en texte&lt;br /&gt;
	char ip[INET_ADDRSTRLEN];&lt;br /&gt;
	inet_ntop(AF_INET, &amp;amp;(clientAdresse.sin_addr), ip, INET_ADDRSTRLEN);&lt;br /&gt;
	printf(&amp;quot;Connexion de %s:%i\n&amp;quot;, ip, clientAdresse.sin_port);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Il est possible d&#039;utiliser &#039;&#039;fcntl&#039;&#039; pour passer le descripteur de la socket en non bloquant, avant l&#039;appel à la fonction &#039;&#039;accept&#039;&#039;. Si le descripteur reste bloquant, la fonction &#039;&#039;accept&#039;&#039; ne rendra pas la main tant qu&#039;un client ne se sera connecté, ayant pour résultat le blocage complet du programme !&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Passage en mode non bloquant&lt;br /&gt;
fcntl(fdsocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
On peut remarquer, au passage, l&#039;utilisation de &#039;&#039;inet_ntop&#039;&#039; pour convertir l&#039;adresse du client du format binaire au format text.&lt;br /&gt;
&lt;br /&gt;
== Lecture / Écriture ==&lt;br /&gt;
=== Mode connecté (TCP) ===&lt;br /&gt;
On peut maintenant utiliser le descripteur de la socket du client pour lire et écrire.&lt;br /&gt;
* avec les fonctions génériques:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Descripteur de la socket du client&lt;br /&gt;
int clientSocket;&lt;br /&gt;
char buffer[BUFFER_LEN];&lt;br /&gt;
// Passage en mode non bloquant, sinon read attend&lt;br /&gt;
fcntl(clientSocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
int len = read(clientSocket, buffer, BUFFER_LEN);&lt;br /&gt;
&lt;br /&gt;
send(clientSocket, &amp;quot;Coucou\n&amp;quot;, strlen(&amp;quot;Coucou\n&amp;quot;));&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* avec les fonctions spécifiques:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Descripteur de la socket du client&lt;br /&gt;
int clientSocket;&lt;br /&gt;
char buffer[BUFFER_LEN];&lt;br /&gt;
&lt;br /&gt;
int len = recv(clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
&lt;br /&gt;
send(clientSocket, &amp;quot;Coucou\n&amp;quot;, strlen(&amp;quot;Coucou\n&amp;quot;), MSG_DONTWAIT);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Analysons le retour de la fonction &#039;&#039;read&#039;&#039; ou &#039;&#039;recv&#039;&#039; lorsque l&#039;on utilise un descripteur non bloquant d&#039;après la [http://man7.org/linux/man-pages/man2/recvmsg.2.html documentation] :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
NAME &lt;br /&gt;
       recv, recvfrom, recvmsg - receive a message from a socket&lt;br /&gt;
[...]&lt;br /&gt;
MSG_DONTWAIT (since Linux 2.2)&lt;br /&gt;
    Enables nonblocking operation; if the operation would block, the call fails with the error EAGAIN or EWOULDBLOCK (this can also be enabled using the O_NONBLOCK flag with the F_SETFL fcntl(2)). &lt;br /&gt;
[...]&lt;br /&gt;
RETURN VALUE&lt;br /&gt;
[...]&lt;br /&gt;
These calls return the number of bytes received, or -1 if an error&lt;br /&gt;
       occurred.  In the event of an error, errno is set to indicate the&lt;br /&gt;
       error.&lt;br /&gt;
When a stream socket peer has performed an orderly shutdown, the&lt;br /&gt;
       return value will be 0 (the traditional &amp;quot;end-of-file&amp;quot; return).&lt;br /&gt;
[...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Le retour est :&lt;br /&gt;
* lorsque le descripteur est vide (aucune donnée de la part du client) &#039;&#039;-1&#039;&#039; et &#039;&#039;errno&#039;&#039; sera positionné à la valeur &#039;&#039;EAGAIN&#039;&#039;, il ne faut donc pas fermer la socket si le retour est &#039;&#039;-1&#039;&#039; ! Il faut d&#039;abord vérifier que &#039;&#039;errno&#039;&#039; &#039;&#039;&#039;n&#039;a pas&#039;&#039;&#039; la valeur &#039;&#039;EAGAIN&#039;&#039;;&lt;br /&gt;
* lorsque le retour est &#039;&#039;0&#039;&#039;, cela signifie que l&#039;extrémité est fermée, il faut donc fermer le descripteur local;&lt;br /&gt;
* lorsque la valeur est supérieur à &#039;&#039;0&#039;&#039; elle correspond au nombre d&#039;octets reçu.&lt;br /&gt;
&lt;br /&gt;
On peut donc en déduire le morceau de code suivant pour gérer le retour de &#039;&#039;read&#039;&#039; ou &#039;&#039;recv&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int len = recv(clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
&lt;br /&gt;
if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
	// Une erreur est survenue&lt;br /&gt;
} else if (len == 0) {&lt;br /&gt;
	// Le client s&#039;est déconnecté (extrémité de la socket fermée)			&lt;br /&gt;
} else if (len &amp;gt; 0) {&lt;br /&gt;
	// Le client à envoyé des données	&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Mode datagramme (UDP) ===&lt;br /&gt;
===Lecture===&lt;br /&gt;
Pour lire dans une socket on peut utiliser &#039;&#039;recvfrom&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t recvfrom(int fd, void *buf, size_t count, int flags, const struct sockaddr *addr, socklen_t addrlen);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères ou mettre le message reçu;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à recevoir;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;O_NONBLOCK&#039;&#039;);&lt;br /&gt;
*  addr &amp;amp;rarr; une structure symbolisant l&#039;adresse du client (cf. ci-dessus)&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour correspond au nombre de caractères reçu ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
===Écriture===&lt;br /&gt;
Pour écrire dans une socket ou peut utiliser &#039;&#039;sendto&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t sendto(int fd, const void *buf, size_t count, const struct sockaddr *addr, socklen_t addrlen);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères contenant le message à envoyer;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à envoyer;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;MSG_DONTWAIT &#039;&#039;, ...);&lt;br /&gt;
* addr &amp;amp;rarr; une structure symbolisant l&#039;adresse du client (cf. ci-dessus)&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour correspond au nombre de caractères envoyer ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
= Exemples =&lt;br /&gt;
== Serveur TCP ==&lt;br /&gt;
Voici un exemple de serveur &#039;&#039;echo&#039;&#039; qui renvoie le message au client. Les connexions utilisent TCP et un simple client comme telnet suffit pour l&#039;utiliser.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;arpa/inet.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Port d&#039;écoute de la socket&lt;br /&gt;
#define PORT 8080&lt;br /&gt;
// Adresse d&#039;écoute (toutes les adresses)&lt;br /&gt;
#define IP INADDR_ANY&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
// Nombre de connexions clients&lt;br /&gt;
#define NB_CLIENTS 2&lt;br /&gt;
// Taille du tampon de lecture des messages&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Commande pour arrêter le serveur&lt;br /&gt;
#define EXIT_WORD &amp;quot;exit&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse);&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse);&lt;br /&gt;
int waitForClient(int * serverSocket);&lt;br /&gt;
void addClientToTab(int clientSocket, int clients[]);&lt;br /&gt;
void manageClient(int clients[]);&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
	// Création et initialisation du tableau contenant les descripteurs des sockets clients&lt;br /&gt;
	int clients[NB_CLIENTS];&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_CLIENTS; i++) {&lt;br /&gt;
		clients[i] = -1;&lt;br /&gt;
	}&lt;br /&gt;
	// Structure contenant l&#039;adresse&lt;br /&gt;
	struct sockaddr_in adresse;&lt;br /&gt;
	initAdresse(&amp;amp;adresse);&lt;br /&gt;
	// Descripteur de la socket du serveur&lt;br /&gt;
	int serverSocket = initSocket(&amp;amp;adresse);&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	while (1) {&lt;br /&gt;
		// Descripteur de la socket du client, on attend une connexion&lt;br /&gt;
		if ((clientSocket = waitForClient(&amp;amp;serverSocket)) != -1) {&lt;br /&gt;
			// On ajoute le nouveau client au tableau des descripteurs&lt;br /&gt;
			addClientToTab(clientSocket, clients);&lt;br /&gt;
		}&lt;br /&gt;
		manageClient(clients);&lt;br /&gt;
	}&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
// Initialisation de la structure sockaddr_in&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse) {&lt;br /&gt;
	(*adresse).sin_family = AF_INET;&lt;br /&gt;
	(*adresse).sin_addr.s_addr = IP;&lt;br /&gt;
	(*adresse).sin_port = htons( PORT);&lt;br /&gt;
}&lt;br /&gt;
// Démarrage de la socket serveur&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse) {&lt;br /&gt;
	// Descripteur de socket&lt;br /&gt;
	int fdsocket;&lt;br /&gt;
	// Nombre d&#039;options&lt;br /&gt;
	int opt = 1;&lt;br /&gt;
	// Création de la socket en TCP&lt;br /&gt;
	if ((fdsocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Création de la socket\n&amp;quot;);&lt;br /&gt;
	// Paramètrage de la socket&lt;br /&gt;
	if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,&lt;br /&gt;
			sizeof(opt)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Paramètrage de la socket\n&amp;quot;);&lt;br /&gt;
	// Attachement de la socket sur le port et l&#039;adresse IP&lt;br /&gt;
	if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Attachement de la socket sur le port %i\n&amp;quot;, PORT);&lt;br /&gt;
	// Passage en écoute de la socket&lt;br /&gt;
	if (listen(fdsocket, BACKLOG) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la mise en écoute: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	// Passage en mode non bloquant&lt;br /&gt;
	fcntl(fdsocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
	printf(&amp;quot;Mise en écoute de la socket\n&amp;quot;);&lt;br /&gt;
	return fdsocket;&lt;br /&gt;
}&lt;br /&gt;
// Attente de connexion d&#039;un client&lt;br /&gt;
int waitForClient(int * serverSocket) {&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	// Structure contenant l&#039;adresse du client&lt;br /&gt;
	struct sockaddr_in clientAdresse;&lt;br /&gt;
	int addrLen = sizeof(clientAdresse);&lt;br /&gt;
	if ((clientSocket = accept(*serverSocket, (struct sockaddr*) &amp;amp;clientAdresse,&lt;br /&gt;
			(socklen_t*) &amp;amp;addrLen)) != -1) {&lt;br /&gt;
		// Convertion de l&#039;IP en texte&lt;br /&gt;
		char ip[INET_ADDRSTRLEN];&lt;br /&gt;
		inet_ntop(AF_INET, &amp;amp;(clientAdresse.sin_addr), ip, INET_ADDRSTRLEN);&lt;br /&gt;
		printf(&amp;quot;Connexion de %s:%i\n&amp;quot;, ip, clientAdresse.sin_port);&lt;br /&gt;
		// Passage en mode non bloquant&lt;br /&gt;
		int opt = 1;&lt;br /&gt;
		setsockopt(clientSocket, SOL_SOCKET, SO_KEEPALIVE, &amp;amp;opt, sizeof(1));&lt;br /&gt;
	}&lt;br /&gt;
	return clientSocket;&lt;br /&gt;
}&lt;br /&gt;
// Ajoute les clients au tableau&lt;br /&gt;
void addClientToTab(int clientSocket, int clients[]) {&lt;br /&gt;
	// On vérifie si on à de la place de libre&lt;br /&gt;
	int found = -1;&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_CLIENTS; i++) {&lt;br /&gt;
		// On cherche de la place&lt;br /&gt;
		if (clients[i] == -1) {&lt;br /&gt;
			// On ajoute le client&lt;br /&gt;
			printf(&amp;quot;Ajout du client à l&#039;index %i\n&amp;quot;, i);&lt;br /&gt;
			clients[i] = clientSocket;&lt;br /&gt;
			// Nouvelle connexion, envoie du message de bienvenu&lt;br /&gt;
			send(clientSocket, &amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;, 	strlen(&amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;),&lt;br /&gt;
					MSG_DONTWAIT);&lt;br /&gt;
			found = 0;&lt;br /&gt;
			break;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	if (found == -1) {&lt;br /&gt;
		// On a plus de place de libre&lt;br /&gt;
		puts(&amp;quot;Plus de place, désolé&amp;quot;);&lt;br /&gt;
		close(clientSocket);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// On traite l&#039;input des clients&lt;br /&gt;
void manageClient(int clients[]) {&lt;br /&gt;
	// Création d&#039;un tampon pour stocker les messages des clients dans la heap&lt;br /&gt;
	static char buffer[BUFFER_LEN + 1];&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_CLIENTS; i++) {&lt;br /&gt;
		// On vérifie les messages&lt;br /&gt;
		int clientSocket = clients[i];&lt;br /&gt;
		if (clientSocket == -1) {&lt;br /&gt;
			continue;&lt;br /&gt;
		}&lt;br /&gt;
		int len = recv(clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
		// Booléen pour suivre l&#039;état de la socket&lt;br /&gt;
		int isClosed = 0;&lt;br /&gt;
		if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
			// Une erreur est survenue&lt;br /&gt;
			printf(&amp;quot;Errno [%i] : %s\n&amp;quot;, errno, strerror(errno));&lt;br /&gt;
			isClosed = 1;&lt;br /&gt;
		} else if (len == 0) {&lt;br /&gt;
			// Le client s&#039;est déconnecté (extrémité de la socket fermée)&lt;br /&gt;
			isClosed = 1;&lt;br /&gt;
		} else if (len &amp;gt; 0) {&lt;br /&gt;
			// Ajout du terminateur de chaîne&lt;br /&gt;
			buffer[len] = &#039;\0&#039;;&lt;br /&gt;
			if (strncmp(buffer, EXIT_WORD, 4) == 0) {&lt;br /&gt;
				// Le client veut se déconnecter&lt;br /&gt;
				send(clientSocket, &amp;quot;Bye\n&amp;quot;, strlen(&amp;quot;Bye\n&amp;quot;), 0);&lt;br /&gt;
				isClosed = 1;&lt;br /&gt;
			} else {&lt;br /&gt;
				// On renvoie le texte au client dans un buffer assez grand&lt;br /&gt;
				int len = strlen(&amp;quot;Vous avez dit : &amp;quot;) + strlen(buffer) + 1;&lt;br /&gt;
				char response[len];&lt;br /&gt;
				strcpy(response, &amp;quot;Vous avez dit : &amp;quot;);&lt;br /&gt;
				strcat(response, buffer);&lt;br /&gt;
				// Un seul envoie permet de ne pas surcharger le réseau&lt;br /&gt;
				send(clientSocket, response, strlen(response), 0);&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
		if (isClosed == 1) {&lt;br /&gt;
			// La socket est fermé ou le client veut quitter le serveur !&lt;br /&gt;
			printf(&amp;quot;Fermeture de la connexion avec le client\n&amp;quot;);&lt;br /&gt;
			// Fermeture de la socket&lt;br /&gt;
			close(clientSocket);&lt;br /&gt;
			// On fait de la place dans le tableau&lt;br /&gt;
			clients[i] = -1;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On peut interroger notre serveur avec &#039;&#039;telnet&#039;&#039; (&#039;&#039;yum -y install telnet&#039;&#039;):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# telnet 127.0.0.1 8080&lt;br /&gt;
Trying 127.0.0.1...&lt;br /&gt;
Connected to 127.0.0.1.&lt;br /&gt;
Escape character is &#039;^]&#039;.&lt;br /&gt;
Entrez &#039;exit&#039; pour quitter&lt;br /&gt;
coucou&lt;br /&gt;
Vous avez dit : coucou&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== Serveur UDP ==&lt;br /&gt;
Si on essaye de reproduire le même type de serveur mais cette fois-ci avec une socket en UDP, cela devient beaucoup plus simple. En effet, en UDP, plus besoin de tenir un tableau avec les descripteurs des sockets des clients car... on est en mode déconnecté (datagramme) ! Entendez par la que les clients envoie un message sans attendre de réponse et sans se soucier de savoir si le message est arrivé au serveur.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;arpa/inet.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Port d&#039;écoute de la socket&lt;br /&gt;
#define PORT 8080&lt;br /&gt;
// Adresse d&#039;écoute (toutes les adresses)&lt;br /&gt;
#define IP INADDR_ANY&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
// Taille du tampon de lecture des messages&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse);&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse);&lt;br /&gt;
void manageClient(int serverSocket);&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
	// Structure contenant l&#039;adresse&lt;br /&gt;
	struct sockaddr_in adresse;&lt;br /&gt;
	initAdresse(&amp;amp;adresse);&lt;br /&gt;
	// Descripteur de la socket du serveur&lt;br /&gt;
	int serverSocket = initSocket(&amp;amp;adresse);&lt;br /&gt;
	while (1) {&lt;br /&gt;
		// Descripteur de la socket du client, on attend une connexion&lt;br /&gt;
		manageClient(serverSocket);&lt;br /&gt;
	}&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
// Initialisation de la structure sockaddr_in&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse) {&lt;br /&gt;
	(*adresse).sin_family = AF_INET;&lt;br /&gt;
	(*adresse).sin_addr.s_addr = IP;&lt;br /&gt;
	(*adresse).sin_port = htons( PORT);&lt;br /&gt;
}&lt;br /&gt;
// Démarrage de la socket serveur&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse) {&lt;br /&gt;
	// Descripteur de socket&lt;br /&gt;
	int fdsocket;&lt;br /&gt;
	// Nombre d&#039;options&lt;br /&gt;
	int opt = 1;&lt;br /&gt;
	// Création de la socket en UDP&lt;br /&gt;
	if ((fdsocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Création de la socket\n&amp;quot;);&lt;br /&gt;
	// Paramètrage de la socket&lt;br /&gt;
	if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,&lt;br /&gt;
			sizeof(opt)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Paramètrage de la socket\n&amp;quot;);&lt;br /&gt;
	// Attachement de la socket sur le port et l&#039;adresse IP&lt;br /&gt;
	if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Attachement de la socket sur le port %i\n&amp;quot;, PORT);&lt;br /&gt;
	return fdsocket;&lt;br /&gt;
}&lt;br /&gt;
// On traite l&#039;input des clients&lt;br /&gt;
void manageClient(int serverSocket) {&lt;br /&gt;
	// Création d&#039;un tampon pour stocker les messages des clients dans la heap&lt;br /&gt;
	static char buffer[BUFFER_LEN + 1];&lt;br /&gt;
	// Structure contenant l&#039;adresse du client&lt;br /&gt;
	struct sockaddr_in clientAdresse;&lt;br /&gt;
	unsigned int addrLen = sizeof(clientAdresse);&lt;br /&gt;
	// On vérifie si on a reçu un message&lt;br /&gt;
	int len = recvfrom(serverSocket, buffer, BUFFER_LEN, MSG_DONTWAIT,&lt;br /&gt;
			(struct sockaddr*) &amp;amp;clientAdresse, &amp;amp;addrLen);&lt;br /&gt;
	if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
		printf(&amp;quot;Problème de socket %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	} else if (len &amp;gt; 0) {&lt;br /&gt;
		printf(&amp;quot;Message reçu de %s:%i\n&amp;quot;, inet_ntoa(clientAdresse.sin_addr),&lt;br /&gt;
				ntohs(clientAdresse.sin_port));&lt;br /&gt;
		// Ajout du terminateur de chaîne&lt;br /&gt;
		buffer[len] = &#039;\0&#039;;&lt;br /&gt;
		// On renvoie le texte au client dans un buffer assez grand&lt;br /&gt;
		len = strlen(&amp;quot;Vous avez dit : &amp;quot;) + strlen(buffer) + 1;&lt;br /&gt;
		char response[len];&lt;br /&gt;
		strcpy(response, &amp;quot;Vous avez dit : &amp;quot;);&lt;br /&gt;
		strcat(response, buffer);&lt;br /&gt;
		// Un seul envoie permet de ne pas surcharger le réseau&lt;br /&gt;
		sendto(serverSocket, response, strlen(response), MSG_DONTWAIT,&lt;br /&gt;
				(struct sockaddr*) &amp;amp;clientAdresse, addrLen);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
On peut interroger notre serveur avec &#039;&#039;netcat&#039;&#039; (&#039;&#039;yum -y install nc&#039;&#039;):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# nc -u 127.0.0.1 8080&lt;br /&gt;
coucou&lt;br /&gt;
Vous avez dit : coucou&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
= Notification évènementielle avec epoll =&lt;br /&gt;
Depuis la version 2.5 du noyau Linux il est possible d&#039;utiliser une série de fonctions, appartenant à l&#039;espace utilisateur (user-space), permettant de suivre l&#039;état d&#039;un descripteur de fichier.&lt;br /&gt;
La différence avec read, recv ou encore recvfrom c&#039;est que l&#039;on va pouvoir surveiller plusieurs descripteurs de fichiers en même temps, laissant le CPU libre pour effectuer autre chose !&lt;br /&gt;
&lt;br /&gt;
== Utilisation ==&lt;br /&gt;
Tout d&#039;abord il faut créer un descripteur de fichier de type &#039;&#039;epoll&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int epoll_create1(int flags);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* flags &amp;amp;rarr; EPOLL_CLOEXEC pour éviter les situation de compétition en environnement parallélisé;&lt;br /&gt;
* le code retour correspond au descripteur &#039;&#039;epoll&#039;&#039; ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée).&lt;br /&gt;
&lt;br /&gt;
Une fois le descripteur &#039;&#039;epoll&#039;&#039; créé, il faut lui associer un événement:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* epfd &amp;amp;rarr; le descripteur &#039;&#039;epoll&#039;&#039; à modifier;&lt;br /&gt;
* op &amp;amp;rarr; une opération parmis:&lt;br /&gt;
** EPOLL_CTL_ADD &amp;amp;rarr; pour ajouter un événement sur un descripteur de fichier;&lt;br /&gt;
** EPOLL_CTL_MOD &amp;amp;rarr; pour modifier un événement sur un descripteur de fichier;&lt;br /&gt;
** EPOLL_CTL_DEL &amp;amp;rarr; pour supprimer un événement sur un descripteur de fichier;&lt;br /&gt;
* fd &amp;amp;rarr; un descripteur de fichier à surveiller;&lt;br /&gt;
* event &amp;amp;rarr; la structure contenant l&#039;événement à surveiller (&#039;&#039;EPOLLIN&#039;&#039;, &#039;&#039;EPOLLOUT&#039;&#039;, EPOLLRDHUP&#039;&#039;, ...);&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; en cas de succès ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée).&lt;br /&gt;
&lt;br /&gt;
Voici la structure &#039;&#039;epoll_event&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
struct epoll_event {&lt;br /&gt;
	uint32_t events;    /* Epoll events */&lt;br /&gt;
	epoll_data_t data;  /* User data variable */&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
typedef union epoll_data {&lt;br /&gt;
	void    *ptr;&lt;br /&gt;
	int      fd;&lt;br /&gt;
	uint32_t u32;&lt;br /&gt;
	uint64_t u64;&lt;br /&gt;
} epoll_data_t;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Enfin, il ne reste plus qu&#039;à &#039;&#039;attendre&#039;&#039; des événements:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* epfd &amp;amp;rarr; le descripteur &#039;&#039;epoll&#039;&#039; à modifier;&lt;br /&gt;
* events &amp;amp;rarr; un tableau de &#039;&#039;epoll_event contenant les événements;&lt;br /&gt;
* maxevents &amp;amp;rarr; le nombre maximum d&#039;événements à traiter (selon la taille du tableau);&lt;br /&gt;
* timeout &amp;amp;rarr; le temps, en millisecondes que la fonction attend:&lt;br /&gt;
** &#039;&#039;-1&#039;&#039; &amp;amp;rarr; la fonction devient bloquante;&lt;br /&gt;
** &#039;&#039;0&#039;&#039; &amp;amp;rarr; retourne immédiatement;&lt;br /&gt;
** &#039;&#039;&amp;gt; 0&#039;&#039; &amp;amp;rarr; attend le nombre de millisecondes spécifiées;&lt;br /&gt;
* le code retour correspond au nombre d&#039;événements ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée).&lt;br /&gt;
&lt;br /&gt;
== Améliorations architecturales ==&lt;br /&gt;
=== Gestion des utilisateurs===&lt;br /&gt;
Tout d&#039;abord, le faite de laisser &#039;&#039;epoll&#039;&#039; gérer les descripteurs de socket nous permet de supprimer le tableau &#039;&#039;clients&#039;&#039; ainsi que la variable &#039;&#039;NB_CLIENTS&#039;&#039;. En effet, la référence vers le descripteur est maintenu par &#039;&#039;epoll&#039;&#039; et nous pouvons gérer maintenant un nombre &#039;&#039;illimité&#039;&#039; de clients.&lt;br /&gt;
=== Performances ===&lt;br /&gt;
La boucle &#039;&#039;while&#039;&#039; de notre serveur consomme énormément de CPU, environ 100% de CPU par client ! On aurais pu utiliser un temporisateur avec &#039;&#039;nanosleep&#039;&#039; pour baisser la consommation CPU entre 5% et 10%...&lt;br /&gt;
Le faîte de basculer sur une fonction bloquante comme &#039;&#039;epoll_wait&#039;&#039; qui utilise des signaux (interruptions) permet de réduire considérablement la consommation CPU. Gardez à l&#039;esprit qu&#039;il n&#039;y a rien de mieux que les interruptions, la preuve, lorsque l&#039;on fait un &#039;&#039;top&#039;&#039;, notre programme à complétement disparu de la liste !&lt;br /&gt;
&lt;br /&gt;
== Modification du serveur ==&lt;br /&gt;
Modifions le code du serveur TCP pour y intégrer &#039;&#039;epoll&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;arpa/inet.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Port d&#039;écoute de la socket&lt;br /&gt;
#define PORT 8080&lt;br /&gt;
// Adresse d&#039;écoute (toutes les adresses)&lt;br /&gt;
#define IP INADDR_ANY&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
// Nombre de connexions clients&lt;br /&gt;
#define NB_EVENTS 2&lt;br /&gt;
// Taille du tampon de lecture des messages&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Commande pour arrêter le serveur&lt;br /&gt;
#define EXIT_WORD &amp;quot;exit&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse);&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse);&lt;br /&gt;
int waitForClient(int * serverSocket);&lt;br /&gt;
void addClientToTab(int * epfd, int * clientSocket);&lt;br /&gt;
void manageClient(int * epfd, int * clientSocket);&lt;br /&gt;
void addEpollEvent(int * epfd, int * socket);&lt;br /&gt;
void delEpollEvent(int * epfd, int * socket);&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
	// Création du descripteur EPOLL&lt;br /&gt;
	int epfd = epoll_create1(0);&lt;br /&gt;
	// Structure contenant les événements EPOLL&lt;br /&gt;
	struct epoll_event events[NB_EVENTS];&lt;br /&gt;
	// Structure contenant l&#039;adresse&lt;br /&gt;
	struct sockaddr_in adresse;&lt;br /&gt;
	initAdresse(&amp;amp;adresse);&lt;br /&gt;
	// Descripteur de la socket du serveur&lt;br /&gt;
	int serverSocket = initSocket(&amp;amp;adresse);&lt;br /&gt;
	addEpollEvent(&amp;amp;epfd, &amp;amp;serverSocket);&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	while (1) {&lt;br /&gt;
		int nfds = epoll_wait(epfd, events, NB_EVENTS, -1);&lt;br /&gt;
		for (int i = 0; i &amp;lt; nfds; i++) {&lt;br /&gt;
			if (events[i].data.fd == serverSocket) {&lt;br /&gt;
				if ((clientSocket = waitForClient(&amp;amp;serverSocket)) != -1) {&lt;br /&gt;
					// On ajoute le nouveau client au tableau des descripteurs&lt;br /&gt;
					addClientToTab(&amp;amp;epfd, &amp;amp;clientSocket);&lt;br /&gt;
				}&lt;br /&gt;
			} else if (events[i].events &amp;amp; EPOLLIN) {&lt;br /&gt;
				// On a reçu quelque chose&lt;br /&gt;
				manageClient(&amp;amp;epfd, &amp;amp;events[i].data.fd);&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	close(serverSocket);&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
// Initialisation de la structure sockaddr_in&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse) {&lt;br /&gt;
	(*adresse).sin_family = AF_INET;&lt;br /&gt;
	(*adresse).sin_addr.s_addr = IP;&lt;br /&gt;
	(*adresse).sin_port = htons( PORT);&lt;br /&gt;
}&lt;br /&gt;
// Démarrage de la socket serveur&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse) {&lt;br /&gt;
	// Descripteur de socket&lt;br /&gt;
	int fdsocket;&lt;br /&gt;
	// Nombre d&#039;options&lt;br /&gt;
	int opt = 1;&lt;br /&gt;
	// Création de la socket en TCP&lt;br /&gt;
	if ((fdsocket = socket(AF_INET, SOCK_STREAM, 0)) == -1) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Création de la socket\n&amp;quot;);&lt;br /&gt;
	// Paramètrage de la socket&lt;br /&gt;
	if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,&lt;br /&gt;
			sizeof(opt)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Paramètrage de la socket\n&amp;quot;);&lt;br /&gt;
	// Attachement de la socket sur le port et l&#039;adresse IP&lt;br /&gt;
	if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Attachement de la socket sur le port %i\n&amp;quot;, PORT);&lt;br /&gt;
	// Passage en écoute de la socket&lt;br /&gt;
	if (listen(fdsocket, BACKLOG) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la mise en écoute: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	// Passage en mode non bloquant&lt;br /&gt;
	fcntl(fdsocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
	printf(&amp;quot;Mise en écoute de la socket\n&amp;quot;);&lt;br /&gt;
	return fdsocket;&lt;br /&gt;
}&lt;br /&gt;
// Attente de connexion d&#039;un client&lt;br /&gt;
int waitForClient(int * serverSocket) {&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	// Structure contenant l&#039;adresse du client&lt;br /&gt;
	struct sockaddr_in clientAdresse;&lt;br /&gt;
	int addrLen = sizeof(clientAdresse);&lt;br /&gt;
	if ((clientSocket = accept(*serverSocket, (struct sockaddr*) &amp;amp;clientAdresse,&lt;br /&gt;
			(socklen_t*) &amp;amp;addrLen)) != -1) {&lt;br /&gt;
		// Convertion de l&#039;IP en texte&lt;br /&gt;
		char ip[INET_ADDRSTRLEN];&lt;br /&gt;
		inet_ntop(AF_INET, &amp;amp;(clientAdresse.sin_addr), ip, INET_ADDRSTRLEN);&lt;br /&gt;
		printf(&amp;quot;Connexion de %s:%i\n&amp;quot;, ip, clientAdresse.sin_port);&lt;br /&gt;
		// Passage en mode non bloquant&lt;br /&gt;
		int opt = 1;&lt;br /&gt;
		setsockopt(clientSocket, SOL_SOCKET, SO_KEEPALIVE, &amp;amp;opt, sizeof(1));&lt;br /&gt;
	}&lt;br /&gt;
	return clientSocket;&lt;br /&gt;
}&lt;br /&gt;
// Ajoute les clients au tableau&lt;br /&gt;
void addClientToTab(int * epfd, int * clientSocket) {&lt;br /&gt;
	addEpollEvent(epfd, clientSocket);&lt;br /&gt;
	// Nouvelle connexion, envoie du message de bienvenu&lt;br /&gt;
	send(*clientSocket, &amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;,&lt;br /&gt;
			strlen(&amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;), MSG_DONTWAIT);&lt;br /&gt;
}&lt;br /&gt;
// On traite l&#039;input des clients&lt;br /&gt;
void manageClient(int * epfd, int * clientSocket) {&lt;br /&gt;
	// Création d&#039;un tampon pour stocker les messages des clients dans la heap&lt;br /&gt;
	static char buffer[BUFFER_LEN + 1];&lt;br /&gt;
	int len = recv(*clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
	// Booléen pour suivre l&#039;état de la socket&lt;br /&gt;
	int isClosed = 0;&lt;br /&gt;
	if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
		// Une erreur est survenue&lt;br /&gt;
		printf(&amp;quot;Errno [%i] : %s\n&amp;quot;, errno, strerror(errno));&lt;br /&gt;
		isClosed = 1;&lt;br /&gt;
	} else if (len == 0) {&lt;br /&gt;
		// Le client s&#039;est déconnecté (extrémité de la socket fermée)&lt;br /&gt;
		isClosed = 1;&lt;br /&gt;
	} else if (len &amp;gt; 0) {&lt;br /&gt;
		// Ajout du terminateur de chaîne&lt;br /&gt;
		buffer[len] = &#039;\0&#039;;&lt;br /&gt;
		if (strncmp(buffer, EXIT_WORD, 4) == 0) {&lt;br /&gt;
			// Le client veut se déconnecter&lt;br /&gt;
			send(*clientSocket, &amp;quot;Bye\n&amp;quot;, strlen(&amp;quot;Bye\n&amp;quot;), 0);&lt;br /&gt;
			isClosed = 1;&lt;br /&gt;
		} else {&lt;br /&gt;
			// On renvoie le texte au client dans un buffer assez grand&lt;br /&gt;
			int len = strlen(&amp;quot;Vous avez dit : &amp;quot;) + strlen(buffer) + 1;&lt;br /&gt;
			char response[len];&lt;br /&gt;
			strcpy(response, &amp;quot;Vous avez dit : &amp;quot;);&lt;br /&gt;
			strcat(response, buffer);&lt;br /&gt;
			// Un seul envoie permet de ne pas surcharger le réseau&lt;br /&gt;
			send(*clientSocket, response, strlen(response), 0);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	if (isClosed == 1) {&lt;br /&gt;
		// On enlève la socket des événements EPOLL&lt;br /&gt;
		delEpollEvent(epfd, clientSocket);&lt;br /&gt;
		// La socket est fermé ou le client veut quitter le serveur !&lt;br /&gt;
		printf(&amp;quot;Fermeture de la connexion avec le client\n&amp;quot;);&lt;br /&gt;
		// Fermeture de la socket&lt;br /&gt;
		close(*clientSocket);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// Ajoute un descripteur de socket aux événements EPOLL&lt;br /&gt;
void addEpollEvent(int * epfd, int * socket) {&lt;br /&gt;
	printf(&amp;quot;Ajout de la socket %i dans les événements EPOLL\n&amp;quot;, *socket);&lt;br /&gt;
	// Structure contenant les événements EPOLL&lt;br /&gt;
	struct epoll_event event;&lt;br /&gt;
	// On enregistre la socket du serveur comme descripteur EPOLL&lt;br /&gt;
	event.data.fd = *socket;&lt;br /&gt;
	// On surveille les message entrant, la fermeture de la socket&lt;br /&gt;
	event.events = EPOLLIN | EPOLLRDHUP;&lt;br /&gt;
	epoll_ctl(*epfd, EPOLL_CTL_ADD, *socket, &amp;amp;event);&lt;br /&gt;
}&lt;br /&gt;
// Efface un descripteur de socket des événements EPOLL&lt;br /&gt;
void delEpollEvent(int * epfd, int * socket) {&lt;br /&gt;
	printf(&amp;quot;Suppression de la socket %i des événements EPOLL\n&amp;quot;, *socket);&lt;br /&gt;
	// Structure contenant les événements EPOLL&lt;br /&gt;
	struct epoll_event event;&lt;br /&gt;
	// On enregistre la socket du serveur comme descripteur EPOLL&lt;br /&gt;
	event.data.fd = *socket;&lt;br /&gt;
	// On surveille les message entrant, la fermeture de la socket&lt;br /&gt;
	event.events = EPOLLIN | EPOLLRDHUP;&lt;br /&gt;
	epoll_ctl(*epfd, EPOLL_CTL_DEL, *socket, &amp;amp;event);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Voici ce que l&#039;on peut observer dans la console:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Création de la socket&lt;br /&gt;
Paramètrage de la socket&lt;br /&gt;
Attachement de la socket sur le port 8080&lt;br /&gt;
Mise en écoute de la socket&lt;br /&gt;
Ajout de la socket 4 dans les événements EPOLL&lt;br /&gt;
Connexion de 127.0.0.1:17607&lt;br /&gt;
Ajout de la socket 5 dans les événements EPOLL&lt;br /&gt;
Suppression de la socket 5 des événements EPOLL&lt;br /&gt;
Fermeture de la connexion avec le client&lt;br /&gt;
Connexion de 127.0.0.1:18119&lt;br /&gt;
Ajout de la socket 5 dans les événements EPOLL&lt;br /&gt;
Connexion de 127.0.0.1:18631&lt;br /&gt;
Ajout de la socket 6 dans les événements EPOLL&lt;br /&gt;
Suppression de la socket 5 des événements EPOLL&lt;br /&gt;
Fermeture de la connexion avec le client&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Magsss</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=C_socket&amp;diff=3279</id>
		<title>C socket</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=C_socket&amp;diff=3279"/>
		<updated>2018-11-01T18:20:07Z</updated>

		<summary type="html">&lt;p&gt;Magsss : /* Fonctions spécifiques */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
||&lt;br /&gt;
Les sockets permettent de connecter deux programmes qui s&#039;exécutent sur deux machines différentes. Cette connexion se fait à travers le réseau grâce à l&#039;utilisation d&#039;un port (TCP ou UDP) et d&#039;une adresse IP. &lt;br /&gt;
&lt;br /&gt;
Il y a deux types de sockets, une qui est démarrée par la partie serveur en &#039;&#039;écoute&#039;&#039; et l&#039;autre démarrée par la partie cliente qui se connecte à la première.&lt;br /&gt;
&lt;br /&gt;
Ci-contre une image résumant les différentes étapes pour arriver à l&#039;envoi de données.&lt;br /&gt;
||&lt;br /&gt;
[[Fichier:Socket workflow.png|centré|200px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Création  =&lt;br /&gt;
== Côté serveur ==&lt;br /&gt;
Tout d&#039;abord il faut créer l&#039;objet socket:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt; &lt;br /&gt;
&lt;br /&gt;
int socket(int domain, int type, int protocol)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*domain &amp;amp;rarr; integer, communication domain e.g., AF_INET (IPv4 protocol) , AF_INET6 (IPv6 protocol)&lt;br /&gt;
*type &amp;amp;rarr; type de communication&lt;br /&gt;
**SOCK_STREAM: TCP&lt;br /&gt;
**SOCK_DGRAM: UDP&lt;br /&gt;
**SOCK_RAW: socket à l&#039;état brut (bas niveau)&lt;br /&gt;
*protocol &amp;amp;rarr; valeur du champ &#039;&#039;protocol&#039;&#039; de l&#039;en-tête de niveau 3 (généralement &#039;&#039;0&#039;&#039;)&lt;br /&gt;
*la valeur de retour est le fichier descripteur de la socket ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Une fois le descripteur de socket créé, il est possible de le configurer:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt; &lt;br /&gt;
&lt;br /&gt;
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* level &amp;amp;rarr; niveau d&#039;application de l&#039;option (SOL_SOCKET, TCP, UDP, ...);&lt;br /&gt;
* optname &amp;amp;rarr; le nom de l&#039;option (&#039;&#039;SO_REUSEADDR&#039;&#039;, &#039;&#039;SO_REUSEPORT&#039;&#039;, &#039;&#039;SO_KEEPALIVE&#039;&#039;, ...)&lt;br /&gt;
* optval, optlen  &amp;amp;rarr; utilisé pour accéder aux options de la socket;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
Il existe plusieurs options de socket, les plus utilisées étant :&lt;br /&gt;
* &#039;&#039;SO_REUSEADDR&#039;&#039; &amp;amp;rarr; permet de réutiliser l&#039;adresse de la socket tout de suite même si cette dernière est dans l&#039;état &#039;&#039;wait&#039;&#039;;&lt;br /&gt;
* &#039;&#039;SO_REUSEPORT&#039;&#039; &amp;amp;rarr; permet de réutiliser le port de la socket tout de suite même si cette dernière est dans l&#039;état &#039;&#039;wait&#039;&#039;;&lt;br /&gt;
* &#039;&#039;SO_KEEPALIVE&#039;&#039; &amp;amp;rarr; envoie des informations périodiquement pour tester si l&#039;extrémité du tunnel est toujours présente;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Il faut maintenant attacher la socket à un port Internet et une adresse IP:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* addr &amp;amp;rarr; une structure symbolisant l&#039;adresse &lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
struct sockaddr_in {&lt;br /&gt;
    short            sin_family;   // e.g: AF_INET&lt;br /&gt;
    unsigned short   sin_port;     // e.g: htons(3490)&lt;br /&gt;
    struct in_addr   sin_addr;     // détaillé ci-dessous&lt;br /&gt;
    char             sin_zero[8];  // &#039;0&#039; habituellement&lt;br /&gt;
};&lt;br /&gt;
struct in_addr {&lt;br /&gt;
    unsigned long s_addr;  // initialiser avec inet_aton()&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On peut maintenant passer la socket en état d&#039;écoute, elle est prête à recevoir des connexions:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int listen(int sockfd, int backlog)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* backlog &amp;amp;rarr; taille maximum de la file d&#039;attente de la socket après laquelle le système répond avec &#039;&#039;ECONNREFUSED&#039;&#039;;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On peut maintenant attendre une connexion:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* addr &amp;amp;rarr; l&#039;adresse du client;&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de la structure addr;&lt;br /&gt;
* le code retour varie entre un entier positif qui correspond au descripteur de la socket cliente et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
== Côté client ==&lt;br /&gt;
Après avoir créé la socket avec &#039;&#039;socket&#039;&#039; on peut se connecter à la partie serveur:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* addr &amp;amp;rarr; une structure symbolisant l&#039;adresse (cf. ci-dessus)&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
= Lecture / écriture =&lt;br /&gt;
== Fonctions génériques ==&lt;br /&gt;
Tout d&#039;abord, on peut dire que la socket est un fichier et se manipule donc comme un fichier !&lt;br /&gt;
===Lecture===&lt;br /&gt;
Pour lire dans une socket on peut utiliser &#039;&#039;read&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t read(int fd, void *buf, size_t count);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères où mettre le message reçu;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à recevoir;&lt;br /&gt;
* le code retour correspond au nombre de caractères reçus ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
===Écriture===&lt;br /&gt;
Pour écrire dans une socket ou peut utiliser &#039;&#039;write&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t write(int fd, const void *buf, size_t count);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères contenant le message à envoyer;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à envoyer;&lt;br /&gt;
* le code retour correspond au nombre de caractères envoyés ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
===Modification===&lt;br /&gt;
Pour modifier un descripteur fichier de socket on utilise &#039;&#039;fcntl&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int fcntl(int fd, int cmd, ... /* int args */ );&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* cmd &amp;amp;rarr; la commande de modification (descripteur, drapeaux, ...);&lt;br /&gt;
* args &amp;amp;rarr; les arguments correspondant à la commande&lt;br /&gt;
&lt;br /&gt;
Cette commande va nous permettre de modifier le drapeau du descripteur de la socket (&#039;&#039;F_SETFL&#039;&#039;) pour le rendre non bloquant (&#039;&#039;O_NONBLOCK&#039;&#039;) !&lt;br /&gt;
&lt;br /&gt;
== Fonctions spécifiques ==&lt;br /&gt;
Il existe des fonctions spécifiques pour manipuler les sockets. Ces fonctions ressemblent aux précédentes mais acceptent des drapeaux en plus pour pouvoir modifier le descripteur &#039;&#039;à la volée&#039;&#039;.&lt;br /&gt;
===Lecture===&lt;br /&gt;
Pour lire dans une socket on peut utiliser &#039;&#039;recv&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t recv(int fd, void *buf, size_t count, int flags);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères ou mettre le message reçu;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à recevoir;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;O_NONBLOCK&#039;&#039;);&lt;br /&gt;
* le code retour correspond au nombre de caractères reçu ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
===Écriture===&lt;br /&gt;
Pour écrire dans une socket ou peut utiliser &#039;&#039;send&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t send(int fd, const void *buf, size_t count, int flags);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères contenant le message à envoyer;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à envoyer;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;MSG_CONFIRM&#039;&#039;, &#039;&#039;MSG_DONTWAIT &#039;&#039;, ...);&lt;br /&gt;
* le code retour correspond au nombre de caractères envoyer ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
= Cas d&#039;utilisation =&lt;br /&gt;
== Création ==&lt;br /&gt;
Pour commencer, il faut créer le descripteur de fichier:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int fdsocket;&lt;br /&gt;
if ((fdsocket = socket(AF_INET, SOCK_STREAM, 0)) == -1) {&lt;br /&gt;
	printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Paramétrage des options ==&lt;br /&gt;
Il faut maintenant configurer la réutilisation de l&#039;adresse et du port&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int opt = 1;&lt;br /&gt;
if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,	sizeof(opt)) != 0) {&lt;br /&gt;
	printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Attachement ==&lt;br /&gt;
On peut maintenant attacher la socket à un port et une adresse:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
struct sockaddr_in adresse;&lt;br /&gt;
&lt;br /&gt;
adresse.sin_family = AF_INET;&lt;br /&gt;
// Ecoute sur toutes les adresses (INADDR_ANY &amp;lt;=&amp;gt; 0.0.0.0)&lt;br /&gt;
adresse.sin_addr.s_addr = INADDR_ANY;&lt;br /&gt;
// Conversion du port en valeur réseaux (Host TO Network Short)&lt;br /&gt;
adresse.sin_port = htons(8080);&lt;br /&gt;
&lt;br /&gt;
if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
	printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mise en écoute ==&lt;br /&gt;
La socket est prête à passer à l&#039;écoute des connexions des clients:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
&lt;br /&gt;
if (listen(fdsocket, BACKLOG) != 0) {&lt;br /&gt;
	printf(&amp;quot;Echéc de la mise en écoute: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Acceptation des connexions ==&lt;br /&gt;
Il est possible d&#039;accepter les nouvelles connexions:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int clientSocket;&lt;br /&gt;
// Structure contenant l&#039;adresse du client&lt;br /&gt;
struct sockaddr_in clientAdresse;&lt;br /&gt;
int addrLen = sizeof(clientAdresse);&lt;br /&gt;
if ((clientSocket = accept(*serverSocket, (struct sockaddr*) &amp;amp;clientAdresse, (socklen_t*) &amp;amp;addrLen)) != -1) {&lt;br /&gt;
	// Convertion de l&#039;IP en texte&lt;br /&gt;
	char ip[INET_ADDRSTRLEN];&lt;br /&gt;
	inet_ntop(AF_INET, &amp;amp;(clientAdresse.sin_addr), ip, INET_ADDRSTRLEN);&lt;br /&gt;
	printf(&amp;quot;Connexion de %s:%i\n&amp;quot;, ip, clientAdresse.sin_port);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Il est possible d&#039;utiliser &#039;&#039;fcntl&#039;&#039; pour passer le descripteur de la socket en non bloquant, avant l&#039;appel à la fonction &#039;&#039;accept&#039;&#039;. Si le descripteur reste bloquant, la fonction &#039;&#039;accept&#039;&#039; ne rendra pas la main tant qu&#039;un client ne se sera connecté, ayant pour résultat le blocage complet du programme !&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Passage en mode non bloquant&lt;br /&gt;
fcntl(fdsocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
On peut remarquer, au passage, l&#039;utilisation de &#039;&#039;inet_ntop&#039;&#039; pour convertir l&#039;adresse du client du format binaire au format text.&lt;br /&gt;
&lt;br /&gt;
== Lecture / Écriture ==&lt;br /&gt;
=== Mode connecté (TCP) ===&lt;br /&gt;
On peut maintenant utiliser le descripteur de la socket du client pour lire et écrire.&lt;br /&gt;
* avec les fonctions génériques:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Descripteur de la socket du client&lt;br /&gt;
int clientSocket;&lt;br /&gt;
char buffer[BUFFER_LEN];&lt;br /&gt;
// Passage en mode non bloquant, sinon read attend&lt;br /&gt;
fcntl(clientSocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
int len = read(clientSocket, buffer, BUFFER_LEN);&lt;br /&gt;
&lt;br /&gt;
send(clientSocket, &amp;quot;Coucou\n&amp;quot;, strlen(&amp;quot;Coucou\n&amp;quot;));&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* avec les fonctions spécifiques:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Descripteur de la socket du client&lt;br /&gt;
int clientSocket;&lt;br /&gt;
char buffer[BUFFER_LEN];&lt;br /&gt;
&lt;br /&gt;
int len = recv(clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
&lt;br /&gt;
send(clientSocket, &amp;quot;Coucou\n&amp;quot;, strlen(&amp;quot;Coucou\n&amp;quot;), MSG_DONTWAIT);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Analysons le retour de la fonction &#039;&#039;read&#039;&#039; ou &#039;&#039;recv&#039;&#039; lorsque l&#039;on utilise un descripteur non bloquant d&#039;après la [http://man7.org/linux/man-pages/man2/recvmsg.2.html documentation] :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
NAME &lt;br /&gt;
       recv, recvfrom, recvmsg - receive a message from a socket&lt;br /&gt;
[...]&lt;br /&gt;
MSG_DONTWAIT (since Linux 2.2)&lt;br /&gt;
    Enables nonblocking operation; if the operation would block, the call fails with the error EAGAIN or EWOULDBLOCK (this can also be enabled using the O_NONBLOCK flag with the F_SETFL fcntl(2)). &lt;br /&gt;
[...]&lt;br /&gt;
RETURN VALUE&lt;br /&gt;
[...]&lt;br /&gt;
These calls return the number of bytes received, or -1 if an error&lt;br /&gt;
       occurred.  In the event of an error, errno is set to indicate the&lt;br /&gt;
       error.&lt;br /&gt;
When a stream socket peer has performed an orderly shutdown, the&lt;br /&gt;
       return value will be 0 (the traditional &amp;quot;end-of-file&amp;quot; return).&lt;br /&gt;
[...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Le retour est :&lt;br /&gt;
* lorsque le descripteur est vide (aucune donnée de la part du client) &#039;&#039;-1&#039;&#039; et &#039;&#039;errno&#039;&#039; sera positionné à la valeur &#039;&#039;EAGAIN&#039;&#039;, il ne faut donc pas fermer la socket si le retour est &#039;&#039;-1&#039;&#039; ! Il faut d&#039;abord vérifier que &#039;&#039;errno&#039;&#039; &#039;&#039;&#039;n&#039;a pas&#039;&#039;&#039; la valeur &#039;&#039;EAGAIN&#039;&#039;;&lt;br /&gt;
* lorsque le retour est &#039;&#039;0&#039;&#039;, cela signifie que l&#039;extrémité est fermée, il faut donc fermer le descripteur local;&lt;br /&gt;
* lorsque la valeur est supérieur à &#039;&#039;0&#039;&#039; elle correspond au nombre d&#039;octets reçu.&lt;br /&gt;
&lt;br /&gt;
On peut donc en déduire le morceau de code suivant pour gérer le retour de &#039;&#039;read&#039;&#039; ou &#039;&#039;recv&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int len = recv(clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
&lt;br /&gt;
if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
	// Une erreur est survenue&lt;br /&gt;
} else if (len == 0) {&lt;br /&gt;
	// Le client s&#039;est déconnecté (extrémité de la socket fermée)			&lt;br /&gt;
} else if (len &amp;gt; 0) {&lt;br /&gt;
	// Le client à envoyé des données	&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Mode datagramme (UDP) ===&lt;br /&gt;
===Lecture===&lt;br /&gt;
Pour lire dans une socket on peut utiliser &#039;&#039;recvfrom&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t recvfrom(int fd, void *buf, size_t count, int flags, const struct sockaddr *addr, socklen_t addrlen);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères ou mettre le message reçu;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à recevoir;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;O_NONBLOCK&#039;&#039;);&lt;br /&gt;
*  addr &amp;amp;rarr; une structure symbolisant l&#039;adresse du client (cf. ci-dessus)&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour correspond au nombre de caractères reçu ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
===Écriture===&lt;br /&gt;
Pour écrire dans une socket ou peut utiliser &#039;&#039;sendto&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t sendto(int fd, const void *buf, size_t count, const struct sockaddr *addr, socklen_t addrlen);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères contenant le message à envoyer;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à envoyer;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;MSG_DONTWAIT &#039;&#039;, ...);&lt;br /&gt;
* addr &amp;amp;rarr; une structure symbolisant l&#039;adresse du client (cf. ci-dessus)&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour correspond au nombre de caractères envoyer ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
= Exemples =&lt;br /&gt;
== Serveur TCP ==&lt;br /&gt;
Voici un exemple de serveur &#039;&#039;echo&#039;&#039; qui renvoie le message au client. Les connexions utilisent TCP et un simple client comme telnet suffit pour l&#039;utiliser.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;arpa/inet.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Port d&#039;écoute de la socket&lt;br /&gt;
#define PORT 8080&lt;br /&gt;
// Adresse d&#039;écoute (toutes les adresses)&lt;br /&gt;
#define IP INADDR_ANY&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
// Nombre de connexions clients&lt;br /&gt;
#define NB_CLIENTS 2&lt;br /&gt;
// Taille du tampon de lecture des messages&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Commande pour arrêter le serveur&lt;br /&gt;
#define EXIT_WORD &amp;quot;exit&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse);&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse);&lt;br /&gt;
int waitForClient(int * serverSocket);&lt;br /&gt;
void addClientToTab(int clientSocket, int clients[]);&lt;br /&gt;
void manageClient(int clients[]);&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
	// Création et initialisation du tableau contenant les descripteurs des sockets clients&lt;br /&gt;
	int clients[NB_CLIENTS];&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_CLIENTS; i++) {&lt;br /&gt;
		clients[i] = -1;&lt;br /&gt;
	}&lt;br /&gt;
	// Structure contenant l&#039;adresse&lt;br /&gt;
	struct sockaddr_in adresse;&lt;br /&gt;
	initAdresse(&amp;amp;adresse);&lt;br /&gt;
	// Descripteur de la socket du serveur&lt;br /&gt;
	int serverSocket = initSocket(&amp;amp;adresse);&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	while (1) {&lt;br /&gt;
		// Descripteur de la socket du client, on attend une connexion&lt;br /&gt;
		if ((clientSocket = waitForClient(&amp;amp;serverSocket)) != -1) {&lt;br /&gt;
			// On ajoute le nouveau client au tableau des descripteurs&lt;br /&gt;
			addClientToTab(clientSocket, clients);&lt;br /&gt;
		}&lt;br /&gt;
		manageClient(clients);&lt;br /&gt;
	}&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
// Initialisation de la structure sockaddr_in&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse) {&lt;br /&gt;
	(*adresse).sin_family = AF_INET;&lt;br /&gt;
	(*adresse).sin_addr.s_addr = IP;&lt;br /&gt;
	(*adresse).sin_port = htons( PORT);&lt;br /&gt;
}&lt;br /&gt;
// Démarrage de la socket serveur&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse) {&lt;br /&gt;
	// Descripteur de socket&lt;br /&gt;
	int fdsocket;&lt;br /&gt;
	// Nombre d&#039;options&lt;br /&gt;
	int opt = 1;&lt;br /&gt;
	// Création de la socket en TCP&lt;br /&gt;
	if ((fdsocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Création de la socket\n&amp;quot;);&lt;br /&gt;
	// Paramètrage de la socket&lt;br /&gt;
	if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,&lt;br /&gt;
			sizeof(opt)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Paramètrage de la socket\n&amp;quot;);&lt;br /&gt;
	// Attachement de la socket sur le port et l&#039;adresse IP&lt;br /&gt;
	if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Attachement de la socket sur le port %i\n&amp;quot;, PORT);&lt;br /&gt;
	// Passage en écoute de la socket&lt;br /&gt;
	if (listen(fdsocket, BACKLOG) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la mise en écoute: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	// Passage en mode non bloquant&lt;br /&gt;
	fcntl(fdsocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
	printf(&amp;quot;Mise en écoute de la socket\n&amp;quot;);&lt;br /&gt;
	return fdsocket;&lt;br /&gt;
}&lt;br /&gt;
// Attente de connexion d&#039;un client&lt;br /&gt;
int waitForClient(int * serverSocket) {&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	// Structure contenant l&#039;adresse du client&lt;br /&gt;
	struct sockaddr_in clientAdresse;&lt;br /&gt;
	int addrLen = sizeof(clientAdresse);&lt;br /&gt;
	if ((clientSocket = accept(*serverSocket, (struct sockaddr*) &amp;amp;clientAdresse,&lt;br /&gt;
			(socklen_t*) &amp;amp;addrLen)) != -1) {&lt;br /&gt;
		// Convertion de l&#039;IP en texte&lt;br /&gt;
		char ip[INET_ADDRSTRLEN];&lt;br /&gt;
		inet_ntop(AF_INET, &amp;amp;(clientAdresse.sin_addr), ip, INET_ADDRSTRLEN);&lt;br /&gt;
		printf(&amp;quot;Connexion de %s:%i\n&amp;quot;, ip, clientAdresse.sin_port);&lt;br /&gt;
		// Passage en mode non bloquant&lt;br /&gt;
		int opt = 1;&lt;br /&gt;
		setsockopt(clientSocket, SOL_SOCKET, SO_KEEPALIVE, &amp;amp;opt, sizeof(1));&lt;br /&gt;
	}&lt;br /&gt;
	return clientSocket;&lt;br /&gt;
}&lt;br /&gt;
// Ajoute les clients au tableau&lt;br /&gt;
void addClientToTab(int clientSocket, int clients[]) {&lt;br /&gt;
	// On vérifie si on à de la place de libre&lt;br /&gt;
	int found = -1;&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_CLIENTS; i++) {&lt;br /&gt;
		// On cherche de la place&lt;br /&gt;
		if (clients[i] == -1) {&lt;br /&gt;
			// On ajoute le client&lt;br /&gt;
			printf(&amp;quot;Ajout du client à l&#039;index %i\n&amp;quot;, i);&lt;br /&gt;
			clients[i] = clientSocket;&lt;br /&gt;
			// Nouvelle connexion, envoie du message de bienvenu&lt;br /&gt;
			send(clientSocket, &amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;, 	strlen(&amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;),&lt;br /&gt;
					MSG_DONTWAIT);&lt;br /&gt;
			found = 0;&lt;br /&gt;
			break;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	if (found == -1) {&lt;br /&gt;
		// On a plus de place de libre&lt;br /&gt;
		puts(&amp;quot;Plus de place, désolé&amp;quot;);&lt;br /&gt;
		close(clientSocket);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// On traite l&#039;input des clients&lt;br /&gt;
void manageClient(int clients[]) {&lt;br /&gt;
	// Création d&#039;un tampon pour stocker les messages des clients dans la heap&lt;br /&gt;
	static char buffer[BUFFER_LEN + 1];&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_CLIENTS; i++) {&lt;br /&gt;
		// On vérifie les messages&lt;br /&gt;
		int clientSocket = clients[i];&lt;br /&gt;
		if (clientSocket == -1) {&lt;br /&gt;
			continue;&lt;br /&gt;
		}&lt;br /&gt;
		int len = recv(clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
		// Booléen pour suivre l&#039;état de la socket&lt;br /&gt;
		int isClosed = 0;&lt;br /&gt;
		if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
			// Une erreur est survenue&lt;br /&gt;
			printf(&amp;quot;Errno [%i] : %s\n&amp;quot;, errno, strerror(errno));&lt;br /&gt;
			isClosed = 1;&lt;br /&gt;
		} else if (len == 0) {&lt;br /&gt;
			// Le client s&#039;est déconnecté (extrémité de la socket fermée)&lt;br /&gt;
			isClosed = 1;&lt;br /&gt;
		} else if (len &amp;gt; 0) {&lt;br /&gt;
			// Ajout du terminateur de chaîne&lt;br /&gt;
			buffer[len] = &#039;\0&#039;;&lt;br /&gt;
			if (strncmp(buffer, EXIT_WORD, 4) == 0) {&lt;br /&gt;
				// Le client veut se déconnecter&lt;br /&gt;
				send(clientSocket, &amp;quot;Bye\n&amp;quot;, strlen(&amp;quot;Bye\n&amp;quot;), 0);&lt;br /&gt;
				isClosed = 1;&lt;br /&gt;
			} else {&lt;br /&gt;
				// On renvoie le texte au client dans un buffer assez grand&lt;br /&gt;
				int len = strlen(&amp;quot;Vous avez dit : &amp;quot;) + strlen(buffer) + 1;&lt;br /&gt;
				char response[len];&lt;br /&gt;
				strcpy(response, &amp;quot;Vous avez dit : &amp;quot;);&lt;br /&gt;
				strcat(response, buffer);&lt;br /&gt;
				// Un seul envoie permet de ne pas surcharger le réseau&lt;br /&gt;
				send(clientSocket, response, strlen(response), 0);&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
		if (isClosed == 1) {&lt;br /&gt;
			// La socket est fermé ou le client veut quitter le serveur !&lt;br /&gt;
			printf(&amp;quot;Fermeture de la connexion avec le client\n&amp;quot;);&lt;br /&gt;
			// Fermeture de la socket&lt;br /&gt;
			close(clientSocket);&lt;br /&gt;
			// On fait de la place dans le tableau&lt;br /&gt;
			clients[i] = -1;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On peut interroger notre serveur avec &#039;&#039;telnet&#039;&#039; (&#039;&#039;yum -y install telnet&#039;&#039;):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# telnet 127.0.0.1 8080&lt;br /&gt;
Trying 127.0.0.1...&lt;br /&gt;
Connected to 127.0.0.1.&lt;br /&gt;
Escape character is &#039;^]&#039;.&lt;br /&gt;
Entrez &#039;exit&#039; pour quitter&lt;br /&gt;
coucou&lt;br /&gt;
Vous avez dit : coucou&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== Serveur UDP ==&lt;br /&gt;
Si on essaye de reproduire le même type de serveur mais cette fois-ci avec une socket en UDP, cela devient beaucoup plus simple. En effet, en UDP, plus besoin de tenir un tableau avec les descripteurs des sockets des clients car... on est en mode déconnecté (datagramme) ! Entendez par la que les clients envoie un message sans attendre de réponse et sans se soucier de savoir si le message est arrivé au serveur.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;arpa/inet.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Port d&#039;écoute de la socket&lt;br /&gt;
#define PORT 8080&lt;br /&gt;
// Adresse d&#039;écoute (toutes les adresses)&lt;br /&gt;
#define IP INADDR_ANY&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
// Taille du tampon de lecture des messages&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse);&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse);&lt;br /&gt;
void manageClient(int serverSocket);&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
	// Structure contenant l&#039;adresse&lt;br /&gt;
	struct sockaddr_in adresse;&lt;br /&gt;
	initAdresse(&amp;amp;adresse);&lt;br /&gt;
	// Descripteur de la socket du serveur&lt;br /&gt;
	int serverSocket = initSocket(&amp;amp;adresse);&lt;br /&gt;
	while (1) {&lt;br /&gt;
		// Descripteur de la socket du client, on attend une connexion&lt;br /&gt;
		manageClient(serverSocket);&lt;br /&gt;
	}&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
// Initialisation de la structure sockaddr_in&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse) {&lt;br /&gt;
	(*adresse).sin_family = AF_INET;&lt;br /&gt;
	(*adresse).sin_addr.s_addr = IP;&lt;br /&gt;
	(*adresse).sin_port = htons( PORT);&lt;br /&gt;
}&lt;br /&gt;
// Démarrage de la socket serveur&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse) {&lt;br /&gt;
	// Descripteur de socket&lt;br /&gt;
	int fdsocket;&lt;br /&gt;
	// Nombre d&#039;options&lt;br /&gt;
	int opt = 1;&lt;br /&gt;
	// Création de la socket en UDP&lt;br /&gt;
	if ((fdsocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Création de la socket\n&amp;quot;);&lt;br /&gt;
	// Paramètrage de la socket&lt;br /&gt;
	if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,&lt;br /&gt;
			sizeof(opt)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Paramètrage de la socket\n&amp;quot;);&lt;br /&gt;
	// Attachement de la socket sur le port et l&#039;adresse IP&lt;br /&gt;
	if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Attachement de la socket sur le port %i\n&amp;quot;, PORT);&lt;br /&gt;
	return fdsocket;&lt;br /&gt;
}&lt;br /&gt;
// On traite l&#039;input des clients&lt;br /&gt;
void manageClient(int serverSocket) {&lt;br /&gt;
	// Création d&#039;un tampon pour stocker les messages des clients dans la heap&lt;br /&gt;
	static char buffer[BUFFER_LEN + 1];&lt;br /&gt;
	// Structure contenant l&#039;adresse du client&lt;br /&gt;
	struct sockaddr_in clientAdresse;&lt;br /&gt;
	unsigned int addrLen = sizeof(clientAdresse);&lt;br /&gt;
	// On vérifie si on a reçu un message&lt;br /&gt;
	int len = recvfrom(serverSocket, buffer, BUFFER_LEN, MSG_DONTWAIT,&lt;br /&gt;
			(struct sockaddr*) &amp;amp;clientAdresse, &amp;amp;addrLen);&lt;br /&gt;
	if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
		printf(&amp;quot;Problème de socket %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	} else if (len &amp;gt; 0) {&lt;br /&gt;
		printf(&amp;quot;Message reçu de %s:%i\n&amp;quot;, inet_ntoa(clientAdresse.sin_addr),&lt;br /&gt;
				ntohs(clientAdresse.sin_port));&lt;br /&gt;
		// Ajout du terminateur de chaîne&lt;br /&gt;
		buffer[len] = &#039;\0&#039;;&lt;br /&gt;
		// On renvoie le texte au client dans un buffer assez grand&lt;br /&gt;
		len = strlen(&amp;quot;Vous avez dit : &amp;quot;) + strlen(buffer) + 1;&lt;br /&gt;
		char response[len];&lt;br /&gt;
		strcpy(response, &amp;quot;Vous avez dit : &amp;quot;);&lt;br /&gt;
		strcat(response, buffer);&lt;br /&gt;
		// Un seul envoie permet de ne pas surcharger le réseau&lt;br /&gt;
		sendto(serverSocket, response, strlen(response), MSG_DONTWAIT,&lt;br /&gt;
				(struct sockaddr*) &amp;amp;clientAdresse, addrLen);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
On peut interroger notre serveur avec &#039;&#039;netcat&#039;&#039; (&#039;&#039;yum -y install nc&#039;&#039;):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# nc -u 127.0.0.1 8080&lt;br /&gt;
coucou&lt;br /&gt;
Vous avez dit : coucou&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
= Notification évènementielle avec epoll =&lt;br /&gt;
Depuis la version 2.5 du noyau Linux il est possible d&#039;utiliser une série de fonctions, appartenant à l&#039;espace utilisateur (user-space), permettant de suivre l&#039;état d&#039;un descripteur de fichier.&lt;br /&gt;
La différence avec read, recv ou encore recvfrom c&#039;est que l&#039;on va pouvoir surveiller plusieurs descripteurs de fichiers en même temps, laissant le CPU libre pour effectuer autre chose !&lt;br /&gt;
&lt;br /&gt;
== Utilisation ==&lt;br /&gt;
Tout d&#039;abord il faut créer un descripteur de fichier de type &#039;&#039;epoll&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int epoll_create1(int flags);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* flags &amp;amp;rarr; EPOLL_CLOEXEC pour éviter les situation de compétition en environnement parallélisé;&lt;br /&gt;
* le code retour correspond au descripteur &#039;&#039;epoll&#039;&#039; ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée).&lt;br /&gt;
&lt;br /&gt;
Une fois le descripteur &#039;&#039;epoll&#039;&#039; créé, il faut lui associer un événement:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* epfd &amp;amp;rarr; le descripteur &#039;&#039;epoll&#039;&#039; à modifier;&lt;br /&gt;
* op &amp;amp;rarr; une opération parmis:&lt;br /&gt;
** EPOLL_CTL_ADD &amp;amp;rarr; pour ajouter un événement sur un descripteur de fichier;&lt;br /&gt;
** EPOLL_CTL_MOD &amp;amp;rarr; pour modifier un événement sur un descripteur de fichier;&lt;br /&gt;
** EPOLL_CTL_DEL &amp;amp;rarr; pour supprimer un événement sur un descripteur de fichier;&lt;br /&gt;
* fd &amp;amp;rarr; un descripteur de fichier à surveiller;&lt;br /&gt;
* event &amp;amp;rarr; la structure contenant l&#039;événement à surveiller (&#039;&#039;EPOLLIN&#039;&#039;, &#039;&#039;EPOLLOUT&#039;&#039;, EPOLLRDHUP&#039;&#039;, ...);&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; en cas de succès ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée).&lt;br /&gt;
&lt;br /&gt;
Voici la structure &#039;&#039;epoll_event&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
struct epoll_event {&lt;br /&gt;
	uint32_t events;    /* Epoll events */&lt;br /&gt;
	epoll_data_t data;  /* User data variable */&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
typedef union epoll_data {&lt;br /&gt;
	void    *ptr;&lt;br /&gt;
	int      fd;&lt;br /&gt;
	uint32_t u32;&lt;br /&gt;
	uint64_t u64;&lt;br /&gt;
} epoll_data_t;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Enfin, il ne reste plus qu&#039;à &#039;&#039;attendre&#039;&#039; des événements:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* epfd &amp;amp;rarr; le descripteur &#039;&#039;epoll&#039;&#039; à modifier;&lt;br /&gt;
* events &amp;amp;rarr; un tableau de &#039;&#039;epoll_event contenant les événements;&lt;br /&gt;
* maxevents &amp;amp;rarr; le nombre maximum d&#039;événements à traiter (selon la taille du tableau);&lt;br /&gt;
* timeout &amp;amp;rarr; le temps, en millisecondes que la fonction attend:&lt;br /&gt;
** &#039;&#039;-1&#039;&#039; &amp;amp;rarr; la fonction devient bloquante;&lt;br /&gt;
** &#039;&#039;0&#039;&#039; &amp;amp;rarr; retourne immédiatement;&lt;br /&gt;
** &#039;&#039;&amp;gt; 0&#039;&#039; &amp;amp;rarr; attend le nombre de millisecondes spécifiées;&lt;br /&gt;
* le code retour correspond au nombre d&#039;événements ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée).&lt;br /&gt;
&lt;br /&gt;
== Améliorations architecturales ==&lt;br /&gt;
=== Gestion des utilisateurs===&lt;br /&gt;
Tout d&#039;abord, le faite de laisser &#039;&#039;epoll&#039;&#039; gérer les descripteurs de socket nous permet de supprimer le tableau &#039;&#039;clients&#039;&#039; ainsi que la variable &#039;&#039;NB_CLIENTS&#039;&#039;. En effet, la référence vers le descripteur est maintenu par &#039;&#039;epoll&#039;&#039; et nous pouvons gérer maintenant un nombre &#039;&#039;illimité&#039;&#039; de clients.&lt;br /&gt;
=== Performances ===&lt;br /&gt;
La boucle &#039;&#039;while&#039;&#039; de notre serveur consomme énormément de CPU, environ 100% de CPU par client ! On aurais pu utiliser un temporisateur avec &#039;&#039;nanosleep&#039;&#039; pour baisser la consommation CPU entre 5% et 10%...&lt;br /&gt;
Le faîte de basculer sur une fonction bloquante comme &#039;&#039;epoll_wait&#039;&#039; qui utilise des signaux (interruptions) permet de réduire considérablement la consommation CPU. Gardez à l&#039;esprit qu&#039;il n&#039;y a rien de mieux que les interruptions, la preuve, lorsque l&#039;on fait un &#039;&#039;top&#039;&#039;, notre programme à complétement disparu de la liste !&lt;br /&gt;
&lt;br /&gt;
== Modification du serveur ==&lt;br /&gt;
Modifions le code du serveur TCP pour y intégrer &#039;&#039;epoll&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;arpa/inet.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Port d&#039;écoute de la socket&lt;br /&gt;
#define PORT 8080&lt;br /&gt;
// Adresse d&#039;écoute (toutes les adresses)&lt;br /&gt;
#define IP INADDR_ANY&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
// Nombre de connexions clients&lt;br /&gt;
#define NB_EVENTS 2&lt;br /&gt;
// Taille du tampon de lecture des messages&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Commande pour arrêter le serveur&lt;br /&gt;
#define EXIT_WORD &amp;quot;exit&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse);&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse);&lt;br /&gt;
int waitForClient(int * serverSocket);&lt;br /&gt;
void addClientToTab(int * epfd, int * clientSocket);&lt;br /&gt;
void manageClient(int * epfd, int * clientSocket);&lt;br /&gt;
void addEpollEvent(int * epfd, int * socket);&lt;br /&gt;
void delEpollEvent(int * epfd, int * socket);&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
	// Création du descripteur EPOLL&lt;br /&gt;
	int epfd = epoll_create1(0);&lt;br /&gt;
	// Structure contenant les événements EPOLL&lt;br /&gt;
	struct epoll_event events[NB_EVENTS];&lt;br /&gt;
	// Structure contenant l&#039;adresse&lt;br /&gt;
	struct sockaddr_in adresse;&lt;br /&gt;
	initAdresse(&amp;amp;adresse);&lt;br /&gt;
	// Descripteur de la socket du serveur&lt;br /&gt;
	int serverSocket = initSocket(&amp;amp;adresse);&lt;br /&gt;
	addEpollEvent(&amp;amp;epfd, &amp;amp;serverSocket);&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	while (1) {&lt;br /&gt;
		int nfds = epoll_wait(epfd, events, NB_EVENTS, -1);&lt;br /&gt;
		for (int i = 0; i &amp;lt; nfds; i++) {&lt;br /&gt;
			if (events[i].data.fd == serverSocket) {&lt;br /&gt;
				if ((clientSocket = waitForClient(&amp;amp;serverSocket)) != -1) {&lt;br /&gt;
					// On ajoute le nouveau client au tableau des descripteurs&lt;br /&gt;
					addClientToTab(&amp;amp;epfd, &amp;amp;clientSocket);&lt;br /&gt;
				}&lt;br /&gt;
			} else if (events[i].events &amp;amp; EPOLLIN) {&lt;br /&gt;
				// On a reçu quelque chose&lt;br /&gt;
				manageClient(&amp;amp;epfd, &amp;amp;events[i].data.fd);&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	close(serverSocket);&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
// Initialisation de la structure sockaddr_in&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse) {&lt;br /&gt;
	(*adresse).sin_family = AF_INET;&lt;br /&gt;
	(*adresse).sin_addr.s_addr = IP;&lt;br /&gt;
	(*adresse).sin_port = htons( PORT);&lt;br /&gt;
}&lt;br /&gt;
// Démarrage de la socket serveur&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse) {&lt;br /&gt;
	// Descripteur de socket&lt;br /&gt;
	int fdsocket;&lt;br /&gt;
	// Nombre d&#039;options&lt;br /&gt;
	int opt = 1;&lt;br /&gt;
	// Création de la socket en TCP&lt;br /&gt;
	if ((fdsocket = socket(AF_INET, SOCK_STREAM, 0)) == -1) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Création de la socket\n&amp;quot;);&lt;br /&gt;
	// Paramètrage de la socket&lt;br /&gt;
	if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,&lt;br /&gt;
			sizeof(opt)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Paramètrage de la socket\n&amp;quot;);&lt;br /&gt;
	// Attachement de la socket sur le port et l&#039;adresse IP&lt;br /&gt;
	if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Attachement de la socket sur le port %i\n&amp;quot;, PORT);&lt;br /&gt;
	// Passage en écoute de la socket&lt;br /&gt;
	if (listen(fdsocket, BACKLOG) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la mise en écoute: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	// Passage en mode non bloquant&lt;br /&gt;
	fcntl(fdsocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
	printf(&amp;quot;Mise en écoute de la socket\n&amp;quot;);&lt;br /&gt;
	return fdsocket;&lt;br /&gt;
}&lt;br /&gt;
// Attente de connexion d&#039;un client&lt;br /&gt;
int waitForClient(int * serverSocket) {&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	// Structure contenant l&#039;adresse du client&lt;br /&gt;
	struct sockaddr_in clientAdresse;&lt;br /&gt;
	int addrLen = sizeof(clientAdresse);&lt;br /&gt;
	if ((clientSocket = accept(*serverSocket, (struct sockaddr*) &amp;amp;clientAdresse,&lt;br /&gt;
			(socklen_t*) &amp;amp;addrLen)) != -1) {&lt;br /&gt;
		// Convertion de l&#039;IP en texte&lt;br /&gt;
		char ip[INET_ADDRSTRLEN];&lt;br /&gt;
		inet_ntop(AF_INET, &amp;amp;(clientAdresse.sin_addr), ip, INET_ADDRSTRLEN);&lt;br /&gt;
		printf(&amp;quot;Connexion de %s:%i\n&amp;quot;, ip, clientAdresse.sin_port);&lt;br /&gt;
		// Passage en mode non bloquant&lt;br /&gt;
		int opt = 1;&lt;br /&gt;
		setsockopt(clientSocket, SOL_SOCKET, SO_KEEPALIVE, &amp;amp;opt, sizeof(1));&lt;br /&gt;
	}&lt;br /&gt;
	return clientSocket;&lt;br /&gt;
}&lt;br /&gt;
// Ajoute les clients au tableau&lt;br /&gt;
void addClientToTab(int * epfd, int * clientSocket) {&lt;br /&gt;
	addEpollEvent(epfd, clientSocket);&lt;br /&gt;
	// Nouvelle connexion, envoie du message de bienvenu&lt;br /&gt;
	send(*clientSocket, &amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;,&lt;br /&gt;
			strlen(&amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;), MSG_DONTWAIT);&lt;br /&gt;
}&lt;br /&gt;
// On traite l&#039;input des clients&lt;br /&gt;
void manageClient(int * epfd, int * clientSocket) {&lt;br /&gt;
	// Création d&#039;un tampon pour stocker les messages des clients dans la heap&lt;br /&gt;
	static char buffer[BUFFER_LEN + 1];&lt;br /&gt;
	int len = recv(*clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
	// Booléen pour suivre l&#039;état de la socket&lt;br /&gt;
	int isClosed = 0;&lt;br /&gt;
	if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
		// Une erreur est survenue&lt;br /&gt;
		printf(&amp;quot;Errno [%i] : %s\n&amp;quot;, errno, strerror(errno));&lt;br /&gt;
		isClosed = 1;&lt;br /&gt;
	} else if (len == 0) {&lt;br /&gt;
		// Le client s&#039;est déconnecté (extrémité de la socket fermée)&lt;br /&gt;
		isClosed = 1;&lt;br /&gt;
	} else if (len &amp;gt; 0) {&lt;br /&gt;
		// Ajout du terminateur de chaîne&lt;br /&gt;
		buffer[len] = &#039;\0&#039;;&lt;br /&gt;
		if (strncmp(buffer, EXIT_WORD, 4) == 0) {&lt;br /&gt;
			// Le client veut se déconnecter&lt;br /&gt;
			send(*clientSocket, &amp;quot;Bye\n&amp;quot;, strlen(&amp;quot;Bye\n&amp;quot;), 0);&lt;br /&gt;
			isClosed = 1;&lt;br /&gt;
		} else {&lt;br /&gt;
			// On renvoie le texte au client dans un buffer assez grand&lt;br /&gt;
			int len = strlen(&amp;quot;Vous avez dit : &amp;quot;) + strlen(buffer) + 1;&lt;br /&gt;
			char response[len];&lt;br /&gt;
			strcpy(response, &amp;quot;Vous avez dit : &amp;quot;);&lt;br /&gt;
			strcat(response, buffer);&lt;br /&gt;
			// Un seul envoie permet de ne pas surcharger le réseau&lt;br /&gt;
			send(*clientSocket, response, strlen(response), 0);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	if (isClosed == 1) {&lt;br /&gt;
		// On enlève la socket des événements EPOLL&lt;br /&gt;
		delEpollEvent(epfd, clientSocket);&lt;br /&gt;
		// La socket est fermé ou le client veut quitter le serveur !&lt;br /&gt;
		printf(&amp;quot;Fermeture de la connexion avec le client\n&amp;quot;);&lt;br /&gt;
		// Fermeture de la socket&lt;br /&gt;
		close(*clientSocket);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// Ajoute un descripteur de socket aux événements EPOLL&lt;br /&gt;
void addEpollEvent(int * epfd, int * socket) {&lt;br /&gt;
	printf(&amp;quot;Ajout de la socket %i dans les événements EPOLL\n&amp;quot;, *socket);&lt;br /&gt;
	// Structure contenant les événements EPOLL&lt;br /&gt;
	struct epoll_event event;&lt;br /&gt;
	// On enregistre la socket du serveur comme descripteur EPOLL&lt;br /&gt;
	event.data.fd = *socket;&lt;br /&gt;
	// On surveille les message entrant, la fermeture de la socket&lt;br /&gt;
	event.events = EPOLLIN | EPOLLRDHUP;&lt;br /&gt;
	epoll_ctl(*epfd, EPOLL_CTL_ADD, *socket, &amp;amp;event);&lt;br /&gt;
}&lt;br /&gt;
// Efface un descripteur de socket des événements EPOLL&lt;br /&gt;
void delEpollEvent(int * epfd, int * socket) {&lt;br /&gt;
	printf(&amp;quot;Suppression de la socket %i des événements EPOLL\n&amp;quot;, *socket);&lt;br /&gt;
	// Structure contenant les événements EPOLL&lt;br /&gt;
	struct epoll_event event;&lt;br /&gt;
	// On enregistre la socket du serveur comme descripteur EPOLL&lt;br /&gt;
	event.data.fd = *socket;&lt;br /&gt;
	// On surveille les message entrant, la fermeture de la socket&lt;br /&gt;
	event.events = EPOLLIN | EPOLLRDHUP;&lt;br /&gt;
	epoll_ctl(*epfd, EPOLL_CTL_DEL, *socket, &amp;amp;event);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Voici ce que l&#039;on peut observer dans la console:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Création de la socket&lt;br /&gt;
Paramètrage de la socket&lt;br /&gt;
Attachement de la socket sur le port 8080&lt;br /&gt;
Mise en écoute de la socket&lt;br /&gt;
Ajout de la socket 4 dans les événements EPOLL&lt;br /&gt;
Connexion de 127.0.0.1:17607&lt;br /&gt;
Ajout de la socket 5 dans les événements EPOLL&lt;br /&gt;
Suppression de la socket 5 des événements EPOLL&lt;br /&gt;
Fermeture de la connexion avec le client&lt;br /&gt;
Connexion de 127.0.0.1:18119&lt;br /&gt;
Ajout de la socket 5 dans les événements EPOLL&lt;br /&gt;
Connexion de 127.0.0.1:18631&lt;br /&gt;
Ajout de la socket 6 dans les événements EPOLL&lt;br /&gt;
Suppression de la socket 5 des événements EPOLL&lt;br /&gt;
Fermeture de la connexion avec le client&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Magsss</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=C_socket&amp;diff=3278</id>
		<title>C socket</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=C_socket&amp;diff=3278"/>
		<updated>2018-11-01T18:19:35Z</updated>

		<summary type="html">&lt;p&gt;Magsss : /* Modification */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
||&lt;br /&gt;
Les sockets permettent de connecter deux programmes qui s&#039;exécutent sur deux machines différentes. Cette connexion se fait à travers le réseau grâce à l&#039;utilisation d&#039;un port (TCP ou UDP) et d&#039;une adresse IP. &lt;br /&gt;
&lt;br /&gt;
Il y a deux types de sockets, une qui est démarrée par la partie serveur en &#039;&#039;écoute&#039;&#039; et l&#039;autre démarrée par la partie cliente qui se connecte à la première.&lt;br /&gt;
&lt;br /&gt;
Ci-contre une image résumant les différentes étapes pour arriver à l&#039;envoi de données.&lt;br /&gt;
||&lt;br /&gt;
[[Fichier:Socket workflow.png|centré|200px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Création  =&lt;br /&gt;
== Côté serveur ==&lt;br /&gt;
Tout d&#039;abord il faut créer l&#039;objet socket:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt; &lt;br /&gt;
&lt;br /&gt;
int socket(int domain, int type, int protocol)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*domain &amp;amp;rarr; integer, communication domain e.g., AF_INET (IPv4 protocol) , AF_INET6 (IPv6 protocol)&lt;br /&gt;
*type &amp;amp;rarr; type de communication&lt;br /&gt;
**SOCK_STREAM: TCP&lt;br /&gt;
**SOCK_DGRAM: UDP&lt;br /&gt;
**SOCK_RAW: socket à l&#039;état brut (bas niveau)&lt;br /&gt;
*protocol &amp;amp;rarr; valeur du champ &#039;&#039;protocol&#039;&#039; de l&#039;en-tête de niveau 3 (généralement &#039;&#039;0&#039;&#039;)&lt;br /&gt;
*la valeur de retour est le fichier descripteur de la socket ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Une fois le descripteur de socket créé, il est possible de le configurer:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt; &lt;br /&gt;
&lt;br /&gt;
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* level &amp;amp;rarr; niveau d&#039;application de l&#039;option (SOL_SOCKET, TCP, UDP, ...);&lt;br /&gt;
* optname &amp;amp;rarr; le nom de l&#039;option (&#039;&#039;SO_REUSEADDR&#039;&#039;, &#039;&#039;SO_REUSEPORT&#039;&#039;, &#039;&#039;SO_KEEPALIVE&#039;&#039;, ...)&lt;br /&gt;
* optval, optlen  &amp;amp;rarr; utilisé pour accéder aux options de la socket;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
Il existe plusieurs options de socket, les plus utilisées étant :&lt;br /&gt;
* &#039;&#039;SO_REUSEADDR&#039;&#039; &amp;amp;rarr; permet de réutiliser l&#039;adresse de la socket tout de suite même si cette dernière est dans l&#039;état &#039;&#039;wait&#039;&#039;;&lt;br /&gt;
* &#039;&#039;SO_REUSEPORT&#039;&#039; &amp;amp;rarr; permet de réutiliser le port de la socket tout de suite même si cette dernière est dans l&#039;état &#039;&#039;wait&#039;&#039;;&lt;br /&gt;
* &#039;&#039;SO_KEEPALIVE&#039;&#039; &amp;amp;rarr; envoie des informations périodiquement pour tester si l&#039;extrémité du tunnel est toujours présente;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Il faut maintenant attacher la socket à un port Internet et une adresse IP:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* addr &amp;amp;rarr; une structure symbolisant l&#039;adresse &lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
struct sockaddr_in {&lt;br /&gt;
    short            sin_family;   // e.g: AF_INET&lt;br /&gt;
    unsigned short   sin_port;     // e.g: htons(3490)&lt;br /&gt;
    struct in_addr   sin_addr;     // détaillé ci-dessous&lt;br /&gt;
    char             sin_zero[8];  // &#039;0&#039; habituellement&lt;br /&gt;
};&lt;br /&gt;
struct in_addr {&lt;br /&gt;
    unsigned long s_addr;  // initialiser avec inet_aton()&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On peut maintenant passer la socket en état d&#039;écoute, elle est prête à recevoir des connexions:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int listen(int sockfd, int backlog)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* backlog &amp;amp;rarr; taille maximum de la file d&#039;attente de la socket après laquelle le système répond avec &#039;&#039;ECONNREFUSED&#039;&#039;;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On peut maintenant attendre une connexion:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* addr &amp;amp;rarr; l&#039;adresse du client;&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de la structure addr;&lt;br /&gt;
* le code retour varie entre un entier positif qui correspond au descripteur de la socket cliente et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
== Côté client ==&lt;br /&gt;
Après avoir créé la socket avec &#039;&#039;socket&#039;&#039; on peut se connecter à la partie serveur:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* addr &amp;amp;rarr; une structure symbolisant l&#039;adresse (cf. ci-dessus)&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
= Lecture / écriture =&lt;br /&gt;
== Fonctions génériques ==&lt;br /&gt;
Tout d&#039;abord, on peut dire que la socket est un fichier et se manipule donc comme un fichier !&lt;br /&gt;
===Lecture===&lt;br /&gt;
Pour lire dans une socket on peut utiliser &#039;&#039;read&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t read(int fd, void *buf, size_t count);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères où mettre le message reçu;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à recevoir;&lt;br /&gt;
* le code retour correspond au nombre de caractères reçus ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
===Écriture===&lt;br /&gt;
Pour écrire dans une socket ou peut utiliser &#039;&#039;write&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t write(int fd, const void *buf, size_t count);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères contenant le message à envoyer;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à envoyer;&lt;br /&gt;
* le code retour correspond au nombre de caractères envoyés ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
===Modification===&lt;br /&gt;
Pour modifier un descripteur fichier de socket on utilise &#039;&#039;fcntl&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int fcntl(int fd, int cmd, ... /* int args */ );&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* cmd &amp;amp;rarr; la commande de modification (descripteur, drapeaux, ...);&lt;br /&gt;
* args &amp;amp;rarr; les arguments correspondant à la commande&lt;br /&gt;
&lt;br /&gt;
Cette commande va nous permettre de modifier le drapeau du descripteur de la socket (&#039;&#039;F_SETFL&#039;&#039;) pour le rendre non bloquant (&#039;&#039;O_NONBLOCK&#039;&#039;) !&lt;br /&gt;
&lt;br /&gt;
== Fonctions spécifiques ==&lt;br /&gt;
Il existe des fonctions spécifiques pour manipuler les sockets. Ces fonctions ressemble au précédentes mais acceptent des drapeaux en plus pour pouvoir modifier le descripteur &#039;&#039;à la volé&#039;&#039;.&lt;br /&gt;
===Lecture===&lt;br /&gt;
Pour lire dans une socket on peut utiliser &#039;&#039;recv&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t recv(int fd, void *buf, size_t count, int flags);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères ou mettre le message reçu;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à recevoir;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;O_NONBLOCK&#039;&#039;);&lt;br /&gt;
* le code retour correspond au nombre de caractères reçu ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
===Écriture===&lt;br /&gt;
Pour écrire dans une socket ou peut utiliser &#039;&#039;send&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t send(int fd, const void *buf, size_t count, int flags);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères contenant le message à envoyer;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à envoyer;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;MSG_CONFIRM&#039;&#039;, &#039;&#039;MSG_DONTWAIT &#039;&#039;, ...);&lt;br /&gt;
* le code retour correspond au nombre de caractères envoyer ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
= Cas d&#039;utilisation =&lt;br /&gt;
== Création ==&lt;br /&gt;
Pour commencer, il faut créer le descripteur de fichier:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int fdsocket;&lt;br /&gt;
if ((fdsocket = socket(AF_INET, SOCK_STREAM, 0)) == -1) {&lt;br /&gt;
	printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Paramétrage des options ==&lt;br /&gt;
Il faut maintenant configurer la réutilisation de l&#039;adresse et du port&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int opt = 1;&lt;br /&gt;
if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,	sizeof(opt)) != 0) {&lt;br /&gt;
	printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Attachement ==&lt;br /&gt;
On peut maintenant attacher la socket à un port et une adresse:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
struct sockaddr_in adresse;&lt;br /&gt;
&lt;br /&gt;
adresse.sin_family = AF_INET;&lt;br /&gt;
// Ecoute sur toutes les adresses (INADDR_ANY &amp;lt;=&amp;gt; 0.0.0.0)&lt;br /&gt;
adresse.sin_addr.s_addr = INADDR_ANY;&lt;br /&gt;
// Conversion du port en valeur réseaux (Host TO Network Short)&lt;br /&gt;
adresse.sin_port = htons(8080);&lt;br /&gt;
&lt;br /&gt;
if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
	printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mise en écoute ==&lt;br /&gt;
La socket est prête à passer à l&#039;écoute des connexions des clients:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
&lt;br /&gt;
if (listen(fdsocket, BACKLOG) != 0) {&lt;br /&gt;
	printf(&amp;quot;Echéc de la mise en écoute: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Acceptation des connexions ==&lt;br /&gt;
Il est possible d&#039;accepter les nouvelles connexions:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int clientSocket;&lt;br /&gt;
// Structure contenant l&#039;adresse du client&lt;br /&gt;
struct sockaddr_in clientAdresse;&lt;br /&gt;
int addrLen = sizeof(clientAdresse);&lt;br /&gt;
if ((clientSocket = accept(*serverSocket, (struct sockaddr*) &amp;amp;clientAdresse, (socklen_t*) &amp;amp;addrLen)) != -1) {&lt;br /&gt;
	// Convertion de l&#039;IP en texte&lt;br /&gt;
	char ip[INET_ADDRSTRLEN];&lt;br /&gt;
	inet_ntop(AF_INET, &amp;amp;(clientAdresse.sin_addr), ip, INET_ADDRSTRLEN);&lt;br /&gt;
	printf(&amp;quot;Connexion de %s:%i\n&amp;quot;, ip, clientAdresse.sin_port);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Il est possible d&#039;utiliser &#039;&#039;fcntl&#039;&#039; pour passer le descripteur de la socket en non bloquant, avant l&#039;appel à la fonction &#039;&#039;accept&#039;&#039;. Si le descripteur reste bloquant, la fonction &#039;&#039;accept&#039;&#039; ne rendra pas la main tant qu&#039;un client ne se sera connecté, ayant pour résultat le blocage complet du programme !&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Passage en mode non bloquant&lt;br /&gt;
fcntl(fdsocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
On peut remarquer, au passage, l&#039;utilisation de &#039;&#039;inet_ntop&#039;&#039; pour convertir l&#039;adresse du client du format binaire au format text.&lt;br /&gt;
&lt;br /&gt;
== Lecture / Écriture ==&lt;br /&gt;
=== Mode connecté (TCP) ===&lt;br /&gt;
On peut maintenant utiliser le descripteur de la socket du client pour lire et écrire.&lt;br /&gt;
* avec les fonctions génériques:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Descripteur de la socket du client&lt;br /&gt;
int clientSocket;&lt;br /&gt;
char buffer[BUFFER_LEN];&lt;br /&gt;
// Passage en mode non bloquant, sinon read attend&lt;br /&gt;
fcntl(clientSocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
int len = read(clientSocket, buffer, BUFFER_LEN);&lt;br /&gt;
&lt;br /&gt;
send(clientSocket, &amp;quot;Coucou\n&amp;quot;, strlen(&amp;quot;Coucou\n&amp;quot;));&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* avec les fonctions spécifiques:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Descripteur de la socket du client&lt;br /&gt;
int clientSocket;&lt;br /&gt;
char buffer[BUFFER_LEN];&lt;br /&gt;
&lt;br /&gt;
int len = recv(clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
&lt;br /&gt;
send(clientSocket, &amp;quot;Coucou\n&amp;quot;, strlen(&amp;quot;Coucou\n&amp;quot;), MSG_DONTWAIT);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Analysons le retour de la fonction &#039;&#039;read&#039;&#039; ou &#039;&#039;recv&#039;&#039; lorsque l&#039;on utilise un descripteur non bloquant d&#039;après la [http://man7.org/linux/man-pages/man2/recvmsg.2.html documentation] :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
NAME &lt;br /&gt;
       recv, recvfrom, recvmsg - receive a message from a socket&lt;br /&gt;
[...]&lt;br /&gt;
MSG_DONTWAIT (since Linux 2.2)&lt;br /&gt;
    Enables nonblocking operation; if the operation would block, the call fails with the error EAGAIN or EWOULDBLOCK (this can also be enabled using the O_NONBLOCK flag with the F_SETFL fcntl(2)). &lt;br /&gt;
[...]&lt;br /&gt;
RETURN VALUE&lt;br /&gt;
[...]&lt;br /&gt;
These calls return the number of bytes received, or -1 if an error&lt;br /&gt;
       occurred.  In the event of an error, errno is set to indicate the&lt;br /&gt;
       error.&lt;br /&gt;
When a stream socket peer has performed an orderly shutdown, the&lt;br /&gt;
       return value will be 0 (the traditional &amp;quot;end-of-file&amp;quot; return).&lt;br /&gt;
[...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Le retour est :&lt;br /&gt;
* lorsque le descripteur est vide (aucune donnée de la part du client) &#039;&#039;-1&#039;&#039; et &#039;&#039;errno&#039;&#039; sera positionné à la valeur &#039;&#039;EAGAIN&#039;&#039;, il ne faut donc pas fermer la socket si le retour est &#039;&#039;-1&#039;&#039; ! Il faut d&#039;abord vérifier que &#039;&#039;errno&#039;&#039; &#039;&#039;&#039;n&#039;a pas&#039;&#039;&#039; la valeur &#039;&#039;EAGAIN&#039;&#039;;&lt;br /&gt;
* lorsque le retour est &#039;&#039;0&#039;&#039;, cela signifie que l&#039;extrémité est fermée, il faut donc fermer le descripteur local;&lt;br /&gt;
* lorsque la valeur est supérieur à &#039;&#039;0&#039;&#039; elle correspond au nombre d&#039;octets reçu.&lt;br /&gt;
&lt;br /&gt;
On peut donc en déduire le morceau de code suivant pour gérer le retour de &#039;&#039;read&#039;&#039; ou &#039;&#039;recv&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int len = recv(clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
&lt;br /&gt;
if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
	// Une erreur est survenue&lt;br /&gt;
} else if (len == 0) {&lt;br /&gt;
	// Le client s&#039;est déconnecté (extrémité de la socket fermée)			&lt;br /&gt;
} else if (len &amp;gt; 0) {&lt;br /&gt;
	// Le client à envoyé des données	&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Mode datagramme (UDP) ===&lt;br /&gt;
===Lecture===&lt;br /&gt;
Pour lire dans une socket on peut utiliser &#039;&#039;recvfrom&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t recvfrom(int fd, void *buf, size_t count, int flags, const struct sockaddr *addr, socklen_t addrlen);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères ou mettre le message reçu;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à recevoir;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;O_NONBLOCK&#039;&#039;);&lt;br /&gt;
*  addr &amp;amp;rarr; une structure symbolisant l&#039;adresse du client (cf. ci-dessus)&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour correspond au nombre de caractères reçu ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
===Écriture===&lt;br /&gt;
Pour écrire dans une socket ou peut utiliser &#039;&#039;sendto&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t sendto(int fd, const void *buf, size_t count, const struct sockaddr *addr, socklen_t addrlen);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères contenant le message à envoyer;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à envoyer;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;MSG_DONTWAIT &#039;&#039;, ...);&lt;br /&gt;
* addr &amp;amp;rarr; une structure symbolisant l&#039;adresse du client (cf. ci-dessus)&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour correspond au nombre de caractères envoyer ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
= Exemples =&lt;br /&gt;
== Serveur TCP ==&lt;br /&gt;
Voici un exemple de serveur &#039;&#039;echo&#039;&#039; qui renvoie le message au client. Les connexions utilisent TCP et un simple client comme telnet suffit pour l&#039;utiliser.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;arpa/inet.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Port d&#039;écoute de la socket&lt;br /&gt;
#define PORT 8080&lt;br /&gt;
// Adresse d&#039;écoute (toutes les adresses)&lt;br /&gt;
#define IP INADDR_ANY&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
// Nombre de connexions clients&lt;br /&gt;
#define NB_CLIENTS 2&lt;br /&gt;
// Taille du tampon de lecture des messages&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Commande pour arrêter le serveur&lt;br /&gt;
#define EXIT_WORD &amp;quot;exit&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse);&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse);&lt;br /&gt;
int waitForClient(int * serverSocket);&lt;br /&gt;
void addClientToTab(int clientSocket, int clients[]);&lt;br /&gt;
void manageClient(int clients[]);&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
	// Création et initialisation du tableau contenant les descripteurs des sockets clients&lt;br /&gt;
	int clients[NB_CLIENTS];&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_CLIENTS; i++) {&lt;br /&gt;
		clients[i] = -1;&lt;br /&gt;
	}&lt;br /&gt;
	// Structure contenant l&#039;adresse&lt;br /&gt;
	struct sockaddr_in adresse;&lt;br /&gt;
	initAdresse(&amp;amp;adresse);&lt;br /&gt;
	// Descripteur de la socket du serveur&lt;br /&gt;
	int serverSocket = initSocket(&amp;amp;adresse);&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	while (1) {&lt;br /&gt;
		// Descripteur de la socket du client, on attend une connexion&lt;br /&gt;
		if ((clientSocket = waitForClient(&amp;amp;serverSocket)) != -1) {&lt;br /&gt;
			// On ajoute le nouveau client au tableau des descripteurs&lt;br /&gt;
			addClientToTab(clientSocket, clients);&lt;br /&gt;
		}&lt;br /&gt;
		manageClient(clients);&lt;br /&gt;
	}&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
// Initialisation de la structure sockaddr_in&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse) {&lt;br /&gt;
	(*adresse).sin_family = AF_INET;&lt;br /&gt;
	(*adresse).sin_addr.s_addr = IP;&lt;br /&gt;
	(*adresse).sin_port = htons( PORT);&lt;br /&gt;
}&lt;br /&gt;
// Démarrage de la socket serveur&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse) {&lt;br /&gt;
	// Descripteur de socket&lt;br /&gt;
	int fdsocket;&lt;br /&gt;
	// Nombre d&#039;options&lt;br /&gt;
	int opt = 1;&lt;br /&gt;
	// Création de la socket en TCP&lt;br /&gt;
	if ((fdsocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Création de la socket\n&amp;quot;);&lt;br /&gt;
	// Paramètrage de la socket&lt;br /&gt;
	if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,&lt;br /&gt;
			sizeof(opt)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Paramètrage de la socket\n&amp;quot;);&lt;br /&gt;
	// Attachement de la socket sur le port et l&#039;adresse IP&lt;br /&gt;
	if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Attachement de la socket sur le port %i\n&amp;quot;, PORT);&lt;br /&gt;
	// Passage en écoute de la socket&lt;br /&gt;
	if (listen(fdsocket, BACKLOG) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la mise en écoute: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	// Passage en mode non bloquant&lt;br /&gt;
	fcntl(fdsocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
	printf(&amp;quot;Mise en écoute de la socket\n&amp;quot;);&lt;br /&gt;
	return fdsocket;&lt;br /&gt;
}&lt;br /&gt;
// Attente de connexion d&#039;un client&lt;br /&gt;
int waitForClient(int * serverSocket) {&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	// Structure contenant l&#039;adresse du client&lt;br /&gt;
	struct sockaddr_in clientAdresse;&lt;br /&gt;
	int addrLen = sizeof(clientAdresse);&lt;br /&gt;
	if ((clientSocket = accept(*serverSocket, (struct sockaddr*) &amp;amp;clientAdresse,&lt;br /&gt;
			(socklen_t*) &amp;amp;addrLen)) != -1) {&lt;br /&gt;
		// Convertion de l&#039;IP en texte&lt;br /&gt;
		char ip[INET_ADDRSTRLEN];&lt;br /&gt;
		inet_ntop(AF_INET, &amp;amp;(clientAdresse.sin_addr), ip, INET_ADDRSTRLEN);&lt;br /&gt;
		printf(&amp;quot;Connexion de %s:%i\n&amp;quot;, ip, clientAdresse.sin_port);&lt;br /&gt;
		// Passage en mode non bloquant&lt;br /&gt;
		int opt = 1;&lt;br /&gt;
		setsockopt(clientSocket, SOL_SOCKET, SO_KEEPALIVE, &amp;amp;opt, sizeof(1));&lt;br /&gt;
	}&lt;br /&gt;
	return clientSocket;&lt;br /&gt;
}&lt;br /&gt;
// Ajoute les clients au tableau&lt;br /&gt;
void addClientToTab(int clientSocket, int clients[]) {&lt;br /&gt;
	// On vérifie si on à de la place de libre&lt;br /&gt;
	int found = -1;&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_CLIENTS; i++) {&lt;br /&gt;
		// On cherche de la place&lt;br /&gt;
		if (clients[i] == -1) {&lt;br /&gt;
			// On ajoute le client&lt;br /&gt;
			printf(&amp;quot;Ajout du client à l&#039;index %i\n&amp;quot;, i);&lt;br /&gt;
			clients[i] = clientSocket;&lt;br /&gt;
			// Nouvelle connexion, envoie du message de bienvenu&lt;br /&gt;
			send(clientSocket, &amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;, 	strlen(&amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;),&lt;br /&gt;
					MSG_DONTWAIT);&lt;br /&gt;
			found = 0;&lt;br /&gt;
			break;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	if (found == -1) {&lt;br /&gt;
		// On a plus de place de libre&lt;br /&gt;
		puts(&amp;quot;Plus de place, désolé&amp;quot;);&lt;br /&gt;
		close(clientSocket);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// On traite l&#039;input des clients&lt;br /&gt;
void manageClient(int clients[]) {&lt;br /&gt;
	// Création d&#039;un tampon pour stocker les messages des clients dans la heap&lt;br /&gt;
	static char buffer[BUFFER_LEN + 1];&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_CLIENTS; i++) {&lt;br /&gt;
		// On vérifie les messages&lt;br /&gt;
		int clientSocket = clients[i];&lt;br /&gt;
		if (clientSocket == -1) {&lt;br /&gt;
			continue;&lt;br /&gt;
		}&lt;br /&gt;
		int len = recv(clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
		// Booléen pour suivre l&#039;état de la socket&lt;br /&gt;
		int isClosed = 0;&lt;br /&gt;
		if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
			// Une erreur est survenue&lt;br /&gt;
			printf(&amp;quot;Errno [%i] : %s\n&amp;quot;, errno, strerror(errno));&lt;br /&gt;
			isClosed = 1;&lt;br /&gt;
		} else if (len == 0) {&lt;br /&gt;
			// Le client s&#039;est déconnecté (extrémité de la socket fermée)&lt;br /&gt;
			isClosed = 1;&lt;br /&gt;
		} else if (len &amp;gt; 0) {&lt;br /&gt;
			// Ajout du terminateur de chaîne&lt;br /&gt;
			buffer[len] = &#039;\0&#039;;&lt;br /&gt;
			if (strncmp(buffer, EXIT_WORD, 4) == 0) {&lt;br /&gt;
				// Le client veut se déconnecter&lt;br /&gt;
				send(clientSocket, &amp;quot;Bye\n&amp;quot;, strlen(&amp;quot;Bye\n&amp;quot;), 0);&lt;br /&gt;
				isClosed = 1;&lt;br /&gt;
			} else {&lt;br /&gt;
				// On renvoie le texte au client dans un buffer assez grand&lt;br /&gt;
				int len = strlen(&amp;quot;Vous avez dit : &amp;quot;) + strlen(buffer) + 1;&lt;br /&gt;
				char response[len];&lt;br /&gt;
				strcpy(response, &amp;quot;Vous avez dit : &amp;quot;);&lt;br /&gt;
				strcat(response, buffer);&lt;br /&gt;
				// Un seul envoie permet de ne pas surcharger le réseau&lt;br /&gt;
				send(clientSocket, response, strlen(response), 0);&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
		if (isClosed == 1) {&lt;br /&gt;
			// La socket est fermé ou le client veut quitter le serveur !&lt;br /&gt;
			printf(&amp;quot;Fermeture de la connexion avec le client\n&amp;quot;);&lt;br /&gt;
			// Fermeture de la socket&lt;br /&gt;
			close(clientSocket);&lt;br /&gt;
			// On fait de la place dans le tableau&lt;br /&gt;
			clients[i] = -1;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On peut interroger notre serveur avec &#039;&#039;telnet&#039;&#039; (&#039;&#039;yum -y install telnet&#039;&#039;):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# telnet 127.0.0.1 8080&lt;br /&gt;
Trying 127.0.0.1...&lt;br /&gt;
Connected to 127.0.0.1.&lt;br /&gt;
Escape character is &#039;^]&#039;.&lt;br /&gt;
Entrez &#039;exit&#039; pour quitter&lt;br /&gt;
coucou&lt;br /&gt;
Vous avez dit : coucou&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== Serveur UDP ==&lt;br /&gt;
Si on essaye de reproduire le même type de serveur mais cette fois-ci avec une socket en UDP, cela devient beaucoup plus simple. En effet, en UDP, plus besoin de tenir un tableau avec les descripteurs des sockets des clients car... on est en mode déconnecté (datagramme) ! Entendez par la que les clients envoie un message sans attendre de réponse et sans se soucier de savoir si le message est arrivé au serveur.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;arpa/inet.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Port d&#039;écoute de la socket&lt;br /&gt;
#define PORT 8080&lt;br /&gt;
// Adresse d&#039;écoute (toutes les adresses)&lt;br /&gt;
#define IP INADDR_ANY&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
// Taille du tampon de lecture des messages&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse);&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse);&lt;br /&gt;
void manageClient(int serverSocket);&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
	// Structure contenant l&#039;adresse&lt;br /&gt;
	struct sockaddr_in adresse;&lt;br /&gt;
	initAdresse(&amp;amp;adresse);&lt;br /&gt;
	// Descripteur de la socket du serveur&lt;br /&gt;
	int serverSocket = initSocket(&amp;amp;adresse);&lt;br /&gt;
	while (1) {&lt;br /&gt;
		// Descripteur de la socket du client, on attend une connexion&lt;br /&gt;
		manageClient(serverSocket);&lt;br /&gt;
	}&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
// Initialisation de la structure sockaddr_in&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse) {&lt;br /&gt;
	(*adresse).sin_family = AF_INET;&lt;br /&gt;
	(*adresse).sin_addr.s_addr = IP;&lt;br /&gt;
	(*adresse).sin_port = htons( PORT);&lt;br /&gt;
}&lt;br /&gt;
// Démarrage de la socket serveur&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse) {&lt;br /&gt;
	// Descripteur de socket&lt;br /&gt;
	int fdsocket;&lt;br /&gt;
	// Nombre d&#039;options&lt;br /&gt;
	int opt = 1;&lt;br /&gt;
	// Création de la socket en UDP&lt;br /&gt;
	if ((fdsocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Création de la socket\n&amp;quot;);&lt;br /&gt;
	// Paramètrage de la socket&lt;br /&gt;
	if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,&lt;br /&gt;
			sizeof(opt)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Paramètrage de la socket\n&amp;quot;);&lt;br /&gt;
	// Attachement de la socket sur le port et l&#039;adresse IP&lt;br /&gt;
	if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Attachement de la socket sur le port %i\n&amp;quot;, PORT);&lt;br /&gt;
	return fdsocket;&lt;br /&gt;
}&lt;br /&gt;
// On traite l&#039;input des clients&lt;br /&gt;
void manageClient(int serverSocket) {&lt;br /&gt;
	// Création d&#039;un tampon pour stocker les messages des clients dans la heap&lt;br /&gt;
	static char buffer[BUFFER_LEN + 1];&lt;br /&gt;
	// Structure contenant l&#039;adresse du client&lt;br /&gt;
	struct sockaddr_in clientAdresse;&lt;br /&gt;
	unsigned int addrLen = sizeof(clientAdresse);&lt;br /&gt;
	// On vérifie si on a reçu un message&lt;br /&gt;
	int len = recvfrom(serverSocket, buffer, BUFFER_LEN, MSG_DONTWAIT,&lt;br /&gt;
			(struct sockaddr*) &amp;amp;clientAdresse, &amp;amp;addrLen);&lt;br /&gt;
	if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
		printf(&amp;quot;Problème de socket %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	} else if (len &amp;gt; 0) {&lt;br /&gt;
		printf(&amp;quot;Message reçu de %s:%i\n&amp;quot;, inet_ntoa(clientAdresse.sin_addr),&lt;br /&gt;
				ntohs(clientAdresse.sin_port));&lt;br /&gt;
		// Ajout du terminateur de chaîne&lt;br /&gt;
		buffer[len] = &#039;\0&#039;;&lt;br /&gt;
		// On renvoie le texte au client dans un buffer assez grand&lt;br /&gt;
		len = strlen(&amp;quot;Vous avez dit : &amp;quot;) + strlen(buffer) + 1;&lt;br /&gt;
		char response[len];&lt;br /&gt;
		strcpy(response, &amp;quot;Vous avez dit : &amp;quot;);&lt;br /&gt;
		strcat(response, buffer);&lt;br /&gt;
		// Un seul envoie permet de ne pas surcharger le réseau&lt;br /&gt;
		sendto(serverSocket, response, strlen(response), MSG_DONTWAIT,&lt;br /&gt;
				(struct sockaddr*) &amp;amp;clientAdresse, addrLen);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
On peut interroger notre serveur avec &#039;&#039;netcat&#039;&#039; (&#039;&#039;yum -y install nc&#039;&#039;):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# nc -u 127.0.0.1 8080&lt;br /&gt;
coucou&lt;br /&gt;
Vous avez dit : coucou&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
= Notification évènementielle avec epoll =&lt;br /&gt;
Depuis la version 2.5 du noyau Linux il est possible d&#039;utiliser une série de fonctions, appartenant à l&#039;espace utilisateur (user-space), permettant de suivre l&#039;état d&#039;un descripteur de fichier.&lt;br /&gt;
La différence avec read, recv ou encore recvfrom c&#039;est que l&#039;on va pouvoir surveiller plusieurs descripteurs de fichiers en même temps, laissant le CPU libre pour effectuer autre chose !&lt;br /&gt;
&lt;br /&gt;
== Utilisation ==&lt;br /&gt;
Tout d&#039;abord il faut créer un descripteur de fichier de type &#039;&#039;epoll&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int epoll_create1(int flags);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* flags &amp;amp;rarr; EPOLL_CLOEXEC pour éviter les situation de compétition en environnement parallélisé;&lt;br /&gt;
* le code retour correspond au descripteur &#039;&#039;epoll&#039;&#039; ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée).&lt;br /&gt;
&lt;br /&gt;
Une fois le descripteur &#039;&#039;epoll&#039;&#039; créé, il faut lui associer un événement:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* epfd &amp;amp;rarr; le descripteur &#039;&#039;epoll&#039;&#039; à modifier;&lt;br /&gt;
* op &amp;amp;rarr; une opération parmis:&lt;br /&gt;
** EPOLL_CTL_ADD &amp;amp;rarr; pour ajouter un événement sur un descripteur de fichier;&lt;br /&gt;
** EPOLL_CTL_MOD &amp;amp;rarr; pour modifier un événement sur un descripteur de fichier;&lt;br /&gt;
** EPOLL_CTL_DEL &amp;amp;rarr; pour supprimer un événement sur un descripteur de fichier;&lt;br /&gt;
* fd &amp;amp;rarr; un descripteur de fichier à surveiller;&lt;br /&gt;
* event &amp;amp;rarr; la structure contenant l&#039;événement à surveiller (&#039;&#039;EPOLLIN&#039;&#039;, &#039;&#039;EPOLLOUT&#039;&#039;, EPOLLRDHUP&#039;&#039;, ...);&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; en cas de succès ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée).&lt;br /&gt;
&lt;br /&gt;
Voici la structure &#039;&#039;epoll_event&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
struct epoll_event {&lt;br /&gt;
	uint32_t events;    /* Epoll events */&lt;br /&gt;
	epoll_data_t data;  /* User data variable */&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
typedef union epoll_data {&lt;br /&gt;
	void    *ptr;&lt;br /&gt;
	int      fd;&lt;br /&gt;
	uint32_t u32;&lt;br /&gt;
	uint64_t u64;&lt;br /&gt;
} epoll_data_t;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Enfin, il ne reste plus qu&#039;à &#039;&#039;attendre&#039;&#039; des événements:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* epfd &amp;amp;rarr; le descripteur &#039;&#039;epoll&#039;&#039; à modifier;&lt;br /&gt;
* events &amp;amp;rarr; un tableau de &#039;&#039;epoll_event contenant les événements;&lt;br /&gt;
* maxevents &amp;amp;rarr; le nombre maximum d&#039;événements à traiter (selon la taille du tableau);&lt;br /&gt;
* timeout &amp;amp;rarr; le temps, en millisecondes que la fonction attend:&lt;br /&gt;
** &#039;&#039;-1&#039;&#039; &amp;amp;rarr; la fonction devient bloquante;&lt;br /&gt;
** &#039;&#039;0&#039;&#039; &amp;amp;rarr; retourne immédiatement;&lt;br /&gt;
** &#039;&#039;&amp;gt; 0&#039;&#039; &amp;amp;rarr; attend le nombre de millisecondes spécifiées;&lt;br /&gt;
* le code retour correspond au nombre d&#039;événements ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée).&lt;br /&gt;
&lt;br /&gt;
== Améliorations architecturales ==&lt;br /&gt;
=== Gestion des utilisateurs===&lt;br /&gt;
Tout d&#039;abord, le faite de laisser &#039;&#039;epoll&#039;&#039; gérer les descripteurs de socket nous permet de supprimer le tableau &#039;&#039;clients&#039;&#039; ainsi que la variable &#039;&#039;NB_CLIENTS&#039;&#039;. En effet, la référence vers le descripteur est maintenu par &#039;&#039;epoll&#039;&#039; et nous pouvons gérer maintenant un nombre &#039;&#039;illimité&#039;&#039; de clients.&lt;br /&gt;
=== Performances ===&lt;br /&gt;
La boucle &#039;&#039;while&#039;&#039; de notre serveur consomme énormément de CPU, environ 100% de CPU par client ! On aurais pu utiliser un temporisateur avec &#039;&#039;nanosleep&#039;&#039; pour baisser la consommation CPU entre 5% et 10%...&lt;br /&gt;
Le faîte de basculer sur une fonction bloquante comme &#039;&#039;epoll_wait&#039;&#039; qui utilise des signaux (interruptions) permet de réduire considérablement la consommation CPU. Gardez à l&#039;esprit qu&#039;il n&#039;y a rien de mieux que les interruptions, la preuve, lorsque l&#039;on fait un &#039;&#039;top&#039;&#039;, notre programme à complétement disparu de la liste !&lt;br /&gt;
&lt;br /&gt;
== Modification du serveur ==&lt;br /&gt;
Modifions le code du serveur TCP pour y intégrer &#039;&#039;epoll&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;arpa/inet.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Port d&#039;écoute de la socket&lt;br /&gt;
#define PORT 8080&lt;br /&gt;
// Adresse d&#039;écoute (toutes les adresses)&lt;br /&gt;
#define IP INADDR_ANY&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
// Nombre de connexions clients&lt;br /&gt;
#define NB_EVENTS 2&lt;br /&gt;
// Taille du tampon de lecture des messages&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Commande pour arrêter le serveur&lt;br /&gt;
#define EXIT_WORD &amp;quot;exit&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse);&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse);&lt;br /&gt;
int waitForClient(int * serverSocket);&lt;br /&gt;
void addClientToTab(int * epfd, int * clientSocket);&lt;br /&gt;
void manageClient(int * epfd, int * clientSocket);&lt;br /&gt;
void addEpollEvent(int * epfd, int * socket);&lt;br /&gt;
void delEpollEvent(int * epfd, int * socket);&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
	// Création du descripteur EPOLL&lt;br /&gt;
	int epfd = epoll_create1(0);&lt;br /&gt;
	// Structure contenant les événements EPOLL&lt;br /&gt;
	struct epoll_event events[NB_EVENTS];&lt;br /&gt;
	// Structure contenant l&#039;adresse&lt;br /&gt;
	struct sockaddr_in adresse;&lt;br /&gt;
	initAdresse(&amp;amp;adresse);&lt;br /&gt;
	// Descripteur de la socket du serveur&lt;br /&gt;
	int serverSocket = initSocket(&amp;amp;adresse);&lt;br /&gt;
	addEpollEvent(&amp;amp;epfd, &amp;amp;serverSocket);&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	while (1) {&lt;br /&gt;
		int nfds = epoll_wait(epfd, events, NB_EVENTS, -1);&lt;br /&gt;
		for (int i = 0; i &amp;lt; nfds; i++) {&lt;br /&gt;
			if (events[i].data.fd == serverSocket) {&lt;br /&gt;
				if ((clientSocket = waitForClient(&amp;amp;serverSocket)) != -1) {&lt;br /&gt;
					// On ajoute le nouveau client au tableau des descripteurs&lt;br /&gt;
					addClientToTab(&amp;amp;epfd, &amp;amp;clientSocket);&lt;br /&gt;
				}&lt;br /&gt;
			} else if (events[i].events &amp;amp; EPOLLIN) {&lt;br /&gt;
				// On a reçu quelque chose&lt;br /&gt;
				manageClient(&amp;amp;epfd, &amp;amp;events[i].data.fd);&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	close(serverSocket);&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
// Initialisation de la structure sockaddr_in&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse) {&lt;br /&gt;
	(*adresse).sin_family = AF_INET;&lt;br /&gt;
	(*adresse).sin_addr.s_addr = IP;&lt;br /&gt;
	(*adresse).sin_port = htons( PORT);&lt;br /&gt;
}&lt;br /&gt;
// Démarrage de la socket serveur&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse) {&lt;br /&gt;
	// Descripteur de socket&lt;br /&gt;
	int fdsocket;&lt;br /&gt;
	// Nombre d&#039;options&lt;br /&gt;
	int opt = 1;&lt;br /&gt;
	// Création de la socket en TCP&lt;br /&gt;
	if ((fdsocket = socket(AF_INET, SOCK_STREAM, 0)) == -1) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Création de la socket\n&amp;quot;);&lt;br /&gt;
	// Paramètrage de la socket&lt;br /&gt;
	if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,&lt;br /&gt;
			sizeof(opt)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Paramètrage de la socket\n&amp;quot;);&lt;br /&gt;
	// Attachement de la socket sur le port et l&#039;adresse IP&lt;br /&gt;
	if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Attachement de la socket sur le port %i\n&amp;quot;, PORT);&lt;br /&gt;
	// Passage en écoute de la socket&lt;br /&gt;
	if (listen(fdsocket, BACKLOG) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la mise en écoute: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	// Passage en mode non bloquant&lt;br /&gt;
	fcntl(fdsocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
	printf(&amp;quot;Mise en écoute de la socket\n&amp;quot;);&lt;br /&gt;
	return fdsocket;&lt;br /&gt;
}&lt;br /&gt;
// Attente de connexion d&#039;un client&lt;br /&gt;
int waitForClient(int * serverSocket) {&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	// Structure contenant l&#039;adresse du client&lt;br /&gt;
	struct sockaddr_in clientAdresse;&lt;br /&gt;
	int addrLen = sizeof(clientAdresse);&lt;br /&gt;
	if ((clientSocket = accept(*serverSocket, (struct sockaddr*) &amp;amp;clientAdresse,&lt;br /&gt;
			(socklen_t*) &amp;amp;addrLen)) != -1) {&lt;br /&gt;
		// Convertion de l&#039;IP en texte&lt;br /&gt;
		char ip[INET_ADDRSTRLEN];&lt;br /&gt;
		inet_ntop(AF_INET, &amp;amp;(clientAdresse.sin_addr), ip, INET_ADDRSTRLEN);&lt;br /&gt;
		printf(&amp;quot;Connexion de %s:%i\n&amp;quot;, ip, clientAdresse.sin_port);&lt;br /&gt;
		// Passage en mode non bloquant&lt;br /&gt;
		int opt = 1;&lt;br /&gt;
		setsockopt(clientSocket, SOL_SOCKET, SO_KEEPALIVE, &amp;amp;opt, sizeof(1));&lt;br /&gt;
	}&lt;br /&gt;
	return clientSocket;&lt;br /&gt;
}&lt;br /&gt;
// Ajoute les clients au tableau&lt;br /&gt;
void addClientToTab(int * epfd, int * clientSocket) {&lt;br /&gt;
	addEpollEvent(epfd, clientSocket);&lt;br /&gt;
	// Nouvelle connexion, envoie du message de bienvenu&lt;br /&gt;
	send(*clientSocket, &amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;,&lt;br /&gt;
			strlen(&amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;), MSG_DONTWAIT);&lt;br /&gt;
}&lt;br /&gt;
// On traite l&#039;input des clients&lt;br /&gt;
void manageClient(int * epfd, int * clientSocket) {&lt;br /&gt;
	// Création d&#039;un tampon pour stocker les messages des clients dans la heap&lt;br /&gt;
	static char buffer[BUFFER_LEN + 1];&lt;br /&gt;
	int len = recv(*clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
	// Booléen pour suivre l&#039;état de la socket&lt;br /&gt;
	int isClosed = 0;&lt;br /&gt;
	if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
		// Une erreur est survenue&lt;br /&gt;
		printf(&amp;quot;Errno [%i] : %s\n&amp;quot;, errno, strerror(errno));&lt;br /&gt;
		isClosed = 1;&lt;br /&gt;
	} else if (len == 0) {&lt;br /&gt;
		// Le client s&#039;est déconnecté (extrémité de la socket fermée)&lt;br /&gt;
		isClosed = 1;&lt;br /&gt;
	} else if (len &amp;gt; 0) {&lt;br /&gt;
		// Ajout du terminateur de chaîne&lt;br /&gt;
		buffer[len] = &#039;\0&#039;;&lt;br /&gt;
		if (strncmp(buffer, EXIT_WORD, 4) == 0) {&lt;br /&gt;
			// Le client veut se déconnecter&lt;br /&gt;
			send(*clientSocket, &amp;quot;Bye\n&amp;quot;, strlen(&amp;quot;Bye\n&amp;quot;), 0);&lt;br /&gt;
			isClosed = 1;&lt;br /&gt;
		} else {&lt;br /&gt;
			// On renvoie le texte au client dans un buffer assez grand&lt;br /&gt;
			int len = strlen(&amp;quot;Vous avez dit : &amp;quot;) + strlen(buffer) + 1;&lt;br /&gt;
			char response[len];&lt;br /&gt;
			strcpy(response, &amp;quot;Vous avez dit : &amp;quot;);&lt;br /&gt;
			strcat(response, buffer);&lt;br /&gt;
			// Un seul envoie permet de ne pas surcharger le réseau&lt;br /&gt;
			send(*clientSocket, response, strlen(response), 0);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	if (isClosed == 1) {&lt;br /&gt;
		// On enlève la socket des événements EPOLL&lt;br /&gt;
		delEpollEvent(epfd, clientSocket);&lt;br /&gt;
		// La socket est fermé ou le client veut quitter le serveur !&lt;br /&gt;
		printf(&amp;quot;Fermeture de la connexion avec le client\n&amp;quot;);&lt;br /&gt;
		// Fermeture de la socket&lt;br /&gt;
		close(*clientSocket);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// Ajoute un descripteur de socket aux événements EPOLL&lt;br /&gt;
void addEpollEvent(int * epfd, int * socket) {&lt;br /&gt;
	printf(&amp;quot;Ajout de la socket %i dans les événements EPOLL\n&amp;quot;, *socket);&lt;br /&gt;
	// Structure contenant les événements EPOLL&lt;br /&gt;
	struct epoll_event event;&lt;br /&gt;
	// On enregistre la socket du serveur comme descripteur EPOLL&lt;br /&gt;
	event.data.fd = *socket;&lt;br /&gt;
	// On surveille les message entrant, la fermeture de la socket&lt;br /&gt;
	event.events = EPOLLIN | EPOLLRDHUP;&lt;br /&gt;
	epoll_ctl(*epfd, EPOLL_CTL_ADD, *socket, &amp;amp;event);&lt;br /&gt;
}&lt;br /&gt;
// Efface un descripteur de socket des événements EPOLL&lt;br /&gt;
void delEpollEvent(int * epfd, int * socket) {&lt;br /&gt;
	printf(&amp;quot;Suppression de la socket %i des événements EPOLL\n&amp;quot;, *socket);&lt;br /&gt;
	// Structure contenant les événements EPOLL&lt;br /&gt;
	struct epoll_event event;&lt;br /&gt;
	// On enregistre la socket du serveur comme descripteur EPOLL&lt;br /&gt;
	event.data.fd = *socket;&lt;br /&gt;
	// On surveille les message entrant, la fermeture de la socket&lt;br /&gt;
	event.events = EPOLLIN | EPOLLRDHUP;&lt;br /&gt;
	epoll_ctl(*epfd, EPOLL_CTL_DEL, *socket, &amp;amp;event);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Voici ce que l&#039;on peut observer dans la console:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Création de la socket&lt;br /&gt;
Paramètrage de la socket&lt;br /&gt;
Attachement de la socket sur le port 8080&lt;br /&gt;
Mise en écoute de la socket&lt;br /&gt;
Ajout de la socket 4 dans les événements EPOLL&lt;br /&gt;
Connexion de 127.0.0.1:17607&lt;br /&gt;
Ajout de la socket 5 dans les événements EPOLL&lt;br /&gt;
Suppression de la socket 5 des événements EPOLL&lt;br /&gt;
Fermeture de la connexion avec le client&lt;br /&gt;
Connexion de 127.0.0.1:18119&lt;br /&gt;
Ajout de la socket 5 dans les événements EPOLL&lt;br /&gt;
Connexion de 127.0.0.1:18631&lt;br /&gt;
Ajout de la socket 6 dans les événements EPOLL&lt;br /&gt;
Suppression de la socket 5 des événements EPOLL&lt;br /&gt;
Fermeture de la connexion avec le client&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Magsss</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=C_socket&amp;diff=3277</id>
		<title>C socket</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=C_socket&amp;diff=3277"/>
		<updated>2018-11-01T18:19:07Z</updated>

		<summary type="html">&lt;p&gt;Magsss : /* Écriture */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
||&lt;br /&gt;
Les sockets permettent de connecter deux programmes qui s&#039;exécutent sur deux machines différentes. Cette connexion se fait à travers le réseau grâce à l&#039;utilisation d&#039;un port (TCP ou UDP) et d&#039;une adresse IP. &lt;br /&gt;
&lt;br /&gt;
Il y a deux types de sockets, une qui est démarrée par la partie serveur en &#039;&#039;écoute&#039;&#039; et l&#039;autre démarrée par la partie cliente qui se connecte à la première.&lt;br /&gt;
&lt;br /&gt;
Ci-contre une image résumant les différentes étapes pour arriver à l&#039;envoi de données.&lt;br /&gt;
||&lt;br /&gt;
[[Fichier:Socket workflow.png|centré|200px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Création  =&lt;br /&gt;
== Côté serveur ==&lt;br /&gt;
Tout d&#039;abord il faut créer l&#039;objet socket:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt; &lt;br /&gt;
&lt;br /&gt;
int socket(int domain, int type, int protocol)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*domain &amp;amp;rarr; integer, communication domain e.g., AF_INET (IPv4 protocol) , AF_INET6 (IPv6 protocol)&lt;br /&gt;
*type &amp;amp;rarr; type de communication&lt;br /&gt;
**SOCK_STREAM: TCP&lt;br /&gt;
**SOCK_DGRAM: UDP&lt;br /&gt;
**SOCK_RAW: socket à l&#039;état brut (bas niveau)&lt;br /&gt;
*protocol &amp;amp;rarr; valeur du champ &#039;&#039;protocol&#039;&#039; de l&#039;en-tête de niveau 3 (généralement &#039;&#039;0&#039;&#039;)&lt;br /&gt;
*la valeur de retour est le fichier descripteur de la socket ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Une fois le descripteur de socket créé, il est possible de le configurer:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt; &lt;br /&gt;
&lt;br /&gt;
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* level &amp;amp;rarr; niveau d&#039;application de l&#039;option (SOL_SOCKET, TCP, UDP, ...);&lt;br /&gt;
* optname &amp;amp;rarr; le nom de l&#039;option (&#039;&#039;SO_REUSEADDR&#039;&#039;, &#039;&#039;SO_REUSEPORT&#039;&#039;, &#039;&#039;SO_KEEPALIVE&#039;&#039;, ...)&lt;br /&gt;
* optval, optlen  &amp;amp;rarr; utilisé pour accéder aux options de la socket;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
Il existe plusieurs options de socket, les plus utilisées étant :&lt;br /&gt;
* &#039;&#039;SO_REUSEADDR&#039;&#039; &amp;amp;rarr; permet de réutiliser l&#039;adresse de la socket tout de suite même si cette dernière est dans l&#039;état &#039;&#039;wait&#039;&#039;;&lt;br /&gt;
* &#039;&#039;SO_REUSEPORT&#039;&#039; &amp;amp;rarr; permet de réutiliser le port de la socket tout de suite même si cette dernière est dans l&#039;état &#039;&#039;wait&#039;&#039;;&lt;br /&gt;
* &#039;&#039;SO_KEEPALIVE&#039;&#039; &amp;amp;rarr; envoie des informations périodiquement pour tester si l&#039;extrémité du tunnel est toujours présente;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Il faut maintenant attacher la socket à un port Internet et une adresse IP:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* addr &amp;amp;rarr; une structure symbolisant l&#039;adresse &lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
struct sockaddr_in {&lt;br /&gt;
    short            sin_family;   // e.g: AF_INET&lt;br /&gt;
    unsigned short   sin_port;     // e.g: htons(3490)&lt;br /&gt;
    struct in_addr   sin_addr;     // détaillé ci-dessous&lt;br /&gt;
    char             sin_zero[8];  // &#039;0&#039; habituellement&lt;br /&gt;
};&lt;br /&gt;
struct in_addr {&lt;br /&gt;
    unsigned long s_addr;  // initialiser avec inet_aton()&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On peut maintenant passer la socket en état d&#039;écoute, elle est prête à recevoir des connexions:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int listen(int sockfd, int backlog)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* backlog &amp;amp;rarr; taille maximum de la file d&#039;attente de la socket après laquelle le système répond avec &#039;&#039;ECONNREFUSED&#039;&#039;;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On peut maintenant attendre une connexion:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* addr &amp;amp;rarr; l&#039;adresse du client;&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de la structure addr;&lt;br /&gt;
* le code retour varie entre un entier positif qui correspond au descripteur de la socket cliente et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
== Côté client ==&lt;br /&gt;
Après avoir créé la socket avec &#039;&#039;socket&#039;&#039; on peut se connecter à la partie serveur:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* addr &amp;amp;rarr; une structure symbolisant l&#039;adresse (cf. ci-dessus)&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
= Lecture / écriture =&lt;br /&gt;
== Fonctions génériques ==&lt;br /&gt;
Tout d&#039;abord, on peut dire que la socket est un fichier et se manipule donc comme un fichier !&lt;br /&gt;
===Lecture===&lt;br /&gt;
Pour lire dans une socket on peut utiliser &#039;&#039;read&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t read(int fd, void *buf, size_t count);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères où mettre le message reçu;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à recevoir;&lt;br /&gt;
* le code retour correspond au nombre de caractères reçus ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
===Écriture===&lt;br /&gt;
Pour écrire dans une socket ou peut utiliser &#039;&#039;write&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t write(int fd, const void *buf, size_t count);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères contenant le message à envoyer;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à envoyer;&lt;br /&gt;
* le code retour correspond au nombre de caractères envoyés ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
===Modification===&lt;br /&gt;
Pour modifier un descripteur fichier de socket on utilise &#039;&#039;fcntl&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int fcntl(int fd, int cmd, ... /* int args */ );&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* cmd &amp;amp;rarr; la commande de modification (descripteur, drapeaux, ...);&lt;br /&gt;
* args &amp;amp;rarr; les arguments correspondants à la commande&lt;br /&gt;
&lt;br /&gt;
Cette commande va nous permettre de modifier le drapeau du descripteur de la socket (&#039;&#039;F_SETFL&#039;&#039;) pour le rendre non bloquant (&#039;&#039;O_NONBLOCK&#039;&#039;) !&lt;br /&gt;
&lt;br /&gt;
== Fonctions spécifiques ==&lt;br /&gt;
Il existe des fonctions spécifiques pour manipuler les sockets. Ces fonctions ressemble au précédentes mais acceptent des drapeaux en plus pour pouvoir modifier le descripteur &#039;&#039;à la volé&#039;&#039;.&lt;br /&gt;
===Lecture===&lt;br /&gt;
Pour lire dans une socket on peut utiliser &#039;&#039;recv&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t recv(int fd, void *buf, size_t count, int flags);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères ou mettre le message reçu;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à recevoir;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;O_NONBLOCK&#039;&#039;);&lt;br /&gt;
* le code retour correspond au nombre de caractères reçu ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
===Écriture===&lt;br /&gt;
Pour écrire dans une socket ou peut utiliser &#039;&#039;send&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t send(int fd, const void *buf, size_t count, int flags);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères contenant le message à envoyer;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à envoyer;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;MSG_CONFIRM&#039;&#039;, &#039;&#039;MSG_DONTWAIT &#039;&#039;, ...);&lt;br /&gt;
* le code retour correspond au nombre de caractères envoyer ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
= Cas d&#039;utilisation =&lt;br /&gt;
== Création ==&lt;br /&gt;
Pour commencer, il faut créer le descripteur de fichier:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int fdsocket;&lt;br /&gt;
if ((fdsocket = socket(AF_INET, SOCK_STREAM, 0)) == -1) {&lt;br /&gt;
	printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Paramétrage des options ==&lt;br /&gt;
Il faut maintenant configurer la réutilisation de l&#039;adresse et du port&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int opt = 1;&lt;br /&gt;
if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,	sizeof(opt)) != 0) {&lt;br /&gt;
	printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Attachement ==&lt;br /&gt;
On peut maintenant attacher la socket à un port et une adresse:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
struct sockaddr_in adresse;&lt;br /&gt;
&lt;br /&gt;
adresse.sin_family = AF_INET;&lt;br /&gt;
// Ecoute sur toutes les adresses (INADDR_ANY &amp;lt;=&amp;gt; 0.0.0.0)&lt;br /&gt;
adresse.sin_addr.s_addr = INADDR_ANY;&lt;br /&gt;
// Conversion du port en valeur réseaux (Host TO Network Short)&lt;br /&gt;
adresse.sin_port = htons(8080);&lt;br /&gt;
&lt;br /&gt;
if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
	printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mise en écoute ==&lt;br /&gt;
La socket est prête à passer à l&#039;écoute des connexions des clients:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
&lt;br /&gt;
if (listen(fdsocket, BACKLOG) != 0) {&lt;br /&gt;
	printf(&amp;quot;Echéc de la mise en écoute: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Acceptation des connexions ==&lt;br /&gt;
Il est possible d&#039;accepter les nouvelles connexions:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int clientSocket;&lt;br /&gt;
// Structure contenant l&#039;adresse du client&lt;br /&gt;
struct sockaddr_in clientAdresse;&lt;br /&gt;
int addrLen = sizeof(clientAdresse);&lt;br /&gt;
if ((clientSocket = accept(*serverSocket, (struct sockaddr*) &amp;amp;clientAdresse, (socklen_t*) &amp;amp;addrLen)) != -1) {&lt;br /&gt;
	// Convertion de l&#039;IP en texte&lt;br /&gt;
	char ip[INET_ADDRSTRLEN];&lt;br /&gt;
	inet_ntop(AF_INET, &amp;amp;(clientAdresse.sin_addr), ip, INET_ADDRSTRLEN);&lt;br /&gt;
	printf(&amp;quot;Connexion de %s:%i\n&amp;quot;, ip, clientAdresse.sin_port);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Il est possible d&#039;utiliser &#039;&#039;fcntl&#039;&#039; pour passer le descripteur de la socket en non bloquant, avant l&#039;appel à la fonction &#039;&#039;accept&#039;&#039;. Si le descripteur reste bloquant, la fonction &#039;&#039;accept&#039;&#039; ne rendra pas la main tant qu&#039;un client ne se sera connecté, ayant pour résultat le blocage complet du programme !&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Passage en mode non bloquant&lt;br /&gt;
fcntl(fdsocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
On peut remarquer, au passage, l&#039;utilisation de &#039;&#039;inet_ntop&#039;&#039; pour convertir l&#039;adresse du client du format binaire au format text.&lt;br /&gt;
&lt;br /&gt;
== Lecture / Écriture ==&lt;br /&gt;
=== Mode connecté (TCP) ===&lt;br /&gt;
On peut maintenant utiliser le descripteur de la socket du client pour lire et écrire.&lt;br /&gt;
* avec les fonctions génériques:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Descripteur de la socket du client&lt;br /&gt;
int clientSocket;&lt;br /&gt;
char buffer[BUFFER_LEN];&lt;br /&gt;
// Passage en mode non bloquant, sinon read attend&lt;br /&gt;
fcntl(clientSocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
int len = read(clientSocket, buffer, BUFFER_LEN);&lt;br /&gt;
&lt;br /&gt;
send(clientSocket, &amp;quot;Coucou\n&amp;quot;, strlen(&amp;quot;Coucou\n&amp;quot;));&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* avec les fonctions spécifiques:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Descripteur de la socket du client&lt;br /&gt;
int clientSocket;&lt;br /&gt;
char buffer[BUFFER_LEN];&lt;br /&gt;
&lt;br /&gt;
int len = recv(clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
&lt;br /&gt;
send(clientSocket, &amp;quot;Coucou\n&amp;quot;, strlen(&amp;quot;Coucou\n&amp;quot;), MSG_DONTWAIT);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Analysons le retour de la fonction &#039;&#039;read&#039;&#039; ou &#039;&#039;recv&#039;&#039; lorsque l&#039;on utilise un descripteur non bloquant d&#039;après la [http://man7.org/linux/man-pages/man2/recvmsg.2.html documentation] :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
NAME &lt;br /&gt;
       recv, recvfrom, recvmsg - receive a message from a socket&lt;br /&gt;
[...]&lt;br /&gt;
MSG_DONTWAIT (since Linux 2.2)&lt;br /&gt;
    Enables nonblocking operation; if the operation would block, the call fails with the error EAGAIN or EWOULDBLOCK (this can also be enabled using the O_NONBLOCK flag with the F_SETFL fcntl(2)). &lt;br /&gt;
[...]&lt;br /&gt;
RETURN VALUE&lt;br /&gt;
[...]&lt;br /&gt;
These calls return the number of bytes received, or -1 if an error&lt;br /&gt;
       occurred.  In the event of an error, errno is set to indicate the&lt;br /&gt;
       error.&lt;br /&gt;
When a stream socket peer has performed an orderly shutdown, the&lt;br /&gt;
       return value will be 0 (the traditional &amp;quot;end-of-file&amp;quot; return).&lt;br /&gt;
[...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Le retour est :&lt;br /&gt;
* lorsque le descripteur est vide (aucune donnée de la part du client) &#039;&#039;-1&#039;&#039; et &#039;&#039;errno&#039;&#039; sera positionné à la valeur &#039;&#039;EAGAIN&#039;&#039;, il ne faut donc pas fermer la socket si le retour est &#039;&#039;-1&#039;&#039; ! Il faut d&#039;abord vérifier que &#039;&#039;errno&#039;&#039; &#039;&#039;&#039;n&#039;a pas&#039;&#039;&#039; la valeur &#039;&#039;EAGAIN&#039;&#039;;&lt;br /&gt;
* lorsque le retour est &#039;&#039;0&#039;&#039;, cela signifie que l&#039;extrémité est fermée, il faut donc fermer le descripteur local;&lt;br /&gt;
* lorsque la valeur est supérieur à &#039;&#039;0&#039;&#039; elle correspond au nombre d&#039;octets reçu.&lt;br /&gt;
&lt;br /&gt;
On peut donc en déduire le morceau de code suivant pour gérer le retour de &#039;&#039;read&#039;&#039; ou &#039;&#039;recv&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int len = recv(clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
&lt;br /&gt;
if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
	// Une erreur est survenue&lt;br /&gt;
} else if (len == 0) {&lt;br /&gt;
	// Le client s&#039;est déconnecté (extrémité de la socket fermée)			&lt;br /&gt;
} else if (len &amp;gt; 0) {&lt;br /&gt;
	// Le client à envoyé des données	&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Mode datagramme (UDP) ===&lt;br /&gt;
===Lecture===&lt;br /&gt;
Pour lire dans une socket on peut utiliser &#039;&#039;recvfrom&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t recvfrom(int fd, void *buf, size_t count, int flags, const struct sockaddr *addr, socklen_t addrlen);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères ou mettre le message reçu;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à recevoir;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;O_NONBLOCK&#039;&#039;);&lt;br /&gt;
*  addr &amp;amp;rarr; une structure symbolisant l&#039;adresse du client (cf. ci-dessus)&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour correspond au nombre de caractères reçu ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
===Écriture===&lt;br /&gt;
Pour écrire dans une socket ou peut utiliser &#039;&#039;sendto&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t sendto(int fd, const void *buf, size_t count, const struct sockaddr *addr, socklen_t addrlen);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères contenant le message à envoyer;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à envoyer;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;MSG_DONTWAIT &#039;&#039;, ...);&lt;br /&gt;
* addr &amp;amp;rarr; une structure symbolisant l&#039;adresse du client (cf. ci-dessus)&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour correspond au nombre de caractères envoyer ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
= Exemples =&lt;br /&gt;
== Serveur TCP ==&lt;br /&gt;
Voici un exemple de serveur &#039;&#039;echo&#039;&#039; qui renvoie le message au client. Les connexions utilisent TCP et un simple client comme telnet suffit pour l&#039;utiliser.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;arpa/inet.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Port d&#039;écoute de la socket&lt;br /&gt;
#define PORT 8080&lt;br /&gt;
// Adresse d&#039;écoute (toutes les adresses)&lt;br /&gt;
#define IP INADDR_ANY&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
// Nombre de connexions clients&lt;br /&gt;
#define NB_CLIENTS 2&lt;br /&gt;
// Taille du tampon de lecture des messages&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Commande pour arrêter le serveur&lt;br /&gt;
#define EXIT_WORD &amp;quot;exit&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse);&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse);&lt;br /&gt;
int waitForClient(int * serverSocket);&lt;br /&gt;
void addClientToTab(int clientSocket, int clients[]);&lt;br /&gt;
void manageClient(int clients[]);&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
	// Création et initialisation du tableau contenant les descripteurs des sockets clients&lt;br /&gt;
	int clients[NB_CLIENTS];&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_CLIENTS; i++) {&lt;br /&gt;
		clients[i] = -1;&lt;br /&gt;
	}&lt;br /&gt;
	// Structure contenant l&#039;adresse&lt;br /&gt;
	struct sockaddr_in adresse;&lt;br /&gt;
	initAdresse(&amp;amp;adresse);&lt;br /&gt;
	// Descripteur de la socket du serveur&lt;br /&gt;
	int serverSocket = initSocket(&amp;amp;adresse);&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	while (1) {&lt;br /&gt;
		// Descripteur de la socket du client, on attend une connexion&lt;br /&gt;
		if ((clientSocket = waitForClient(&amp;amp;serverSocket)) != -1) {&lt;br /&gt;
			// On ajoute le nouveau client au tableau des descripteurs&lt;br /&gt;
			addClientToTab(clientSocket, clients);&lt;br /&gt;
		}&lt;br /&gt;
		manageClient(clients);&lt;br /&gt;
	}&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
// Initialisation de la structure sockaddr_in&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse) {&lt;br /&gt;
	(*adresse).sin_family = AF_INET;&lt;br /&gt;
	(*adresse).sin_addr.s_addr = IP;&lt;br /&gt;
	(*adresse).sin_port = htons( PORT);&lt;br /&gt;
}&lt;br /&gt;
// Démarrage de la socket serveur&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse) {&lt;br /&gt;
	// Descripteur de socket&lt;br /&gt;
	int fdsocket;&lt;br /&gt;
	// Nombre d&#039;options&lt;br /&gt;
	int opt = 1;&lt;br /&gt;
	// Création de la socket en TCP&lt;br /&gt;
	if ((fdsocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Création de la socket\n&amp;quot;);&lt;br /&gt;
	// Paramètrage de la socket&lt;br /&gt;
	if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,&lt;br /&gt;
			sizeof(opt)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Paramètrage de la socket\n&amp;quot;);&lt;br /&gt;
	// Attachement de la socket sur le port et l&#039;adresse IP&lt;br /&gt;
	if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Attachement de la socket sur le port %i\n&amp;quot;, PORT);&lt;br /&gt;
	// Passage en écoute de la socket&lt;br /&gt;
	if (listen(fdsocket, BACKLOG) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la mise en écoute: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	// Passage en mode non bloquant&lt;br /&gt;
	fcntl(fdsocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
	printf(&amp;quot;Mise en écoute de la socket\n&amp;quot;);&lt;br /&gt;
	return fdsocket;&lt;br /&gt;
}&lt;br /&gt;
// Attente de connexion d&#039;un client&lt;br /&gt;
int waitForClient(int * serverSocket) {&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	// Structure contenant l&#039;adresse du client&lt;br /&gt;
	struct sockaddr_in clientAdresse;&lt;br /&gt;
	int addrLen = sizeof(clientAdresse);&lt;br /&gt;
	if ((clientSocket = accept(*serverSocket, (struct sockaddr*) &amp;amp;clientAdresse,&lt;br /&gt;
			(socklen_t*) &amp;amp;addrLen)) != -1) {&lt;br /&gt;
		// Convertion de l&#039;IP en texte&lt;br /&gt;
		char ip[INET_ADDRSTRLEN];&lt;br /&gt;
		inet_ntop(AF_INET, &amp;amp;(clientAdresse.sin_addr), ip, INET_ADDRSTRLEN);&lt;br /&gt;
		printf(&amp;quot;Connexion de %s:%i\n&amp;quot;, ip, clientAdresse.sin_port);&lt;br /&gt;
		// Passage en mode non bloquant&lt;br /&gt;
		int opt = 1;&lt;br /&gt;
		setsockopt(clientSocket, SOL_SOCKET, SO_KEEPALIVE, &amp;amp;opt, sizeof(1));&lt;br /&gt;
	}&lt;br /&gt;
	return clientSocket;&lt;br /&gt;
}&lt;br /&gt;
// Ajoute les clients au tableau&lt;br /&gt;
void addClientToTab(int clientSocket, int clients[]) {&lt;br /&gt;
	// On vérifie si on à de la place de libre&lt;br /&gt;
	int found = -1;&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_CLIENTS; i++) {&lt;br /&gt;
		// On cherche de la place&lt;br /&gt;
		if (clients[i] == -1) {&lt;br /&gt;
			// On ajoute le client&lt;br /&gt;
			printf(&amp;quot;Ajout du client à l&#039;index %i\n&amp;quot;, i);&lt;br /&gt;
			clients[i] = clientSocket;&lt;br /&gt;
			// Nouvelle connexion, envoie du message de bienvenu&lt;br /&gt;
			send(clientSocket, &amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;, 	strlen(&amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;),&lt;br /&gt;
					MSG_DONTWAIT);&lt;br /&gt;
			found = 0;&lt;br /&gt;
			break;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	if (found == -1) {&lt;br /&gt;
		// On a plus de place de libre&lt;br /&gt;
		puts(&amp;quot;Plus de place, désolé&amp;quot;);&lt;br /&gt;
		close(clientSocket);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// On traite l&#039;input des clients&lt;br /&gt;
void manageClient(int clients[]) {&lt;br /&gt;
	// Création d&#039;un tampon pour stocker les messages des clients dans la heap&lt;br /&gt;
	static char buffer[BUFFER_LEN + 1];&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_CLIENTS; i++) {&lt;br /&gt;
		// On vérifie les messages&lt;br /&gt;
		int clientSocket = clients[i];&lt;br /&gt;
		if (clientSocket == -1) {&lt;br /&gt;
			continue;&lt;br /&gt;
		}&lt;br /&gt;
		int len = recv(clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
		// Booléen pour suivre l&#039;état de la socket&lt;br /&gt;
		int isClosed = 0;&lt;br /&gt;
		if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
			// Une erreur est survenue&lt;br /&gt;
			printf(&amp;quot;Errno [%i] : %s\n&amp;quot;, errno, strerror(errno));&lt;br /&gt;
			isClosed = 1;&lt;br /&gt;
		} else if (len == 0) {&lt;br /&gt;
			// Le client s&#039;est déconnecté (extrémité de la socket fermée)&lt;br /&gt;
			isClosed = 1;&lt;br /&gt;
		} else if (len &amp;gt; 0) {&lt;br /&gt;
			// Ajout du terminateur de chaîne&lt;br /&gt;
			buffer[len] = &#039;\0&#039;;&lt;br /&gt;
			if (strncmp(buffer, EXIT_WORD, 4) == 0) {&lt;br /&gt;
				// Le client veut se déconnecter&lt;br /&gt;
				send(clientSocket, &amp;quot;Bye\n&amp;quot;, strlen(&amp;quot;Bye\n&amp;quot;), 0);&lt;br /&gt;
				isClosed = 1;&lt;br /&gt;
			} else {&lt;br /&gt;
				// On renvoie le texte au client dans un buffer assez grand&lt;br /&gt;
				int len = strlen(&amp;quot;Vous avez dit : &amp;quot;) + strlen(buffer) + 1;&lt;br /&gt;
				char response[len];&lt;br /&gt;
				strcpy(response, &amp;quot;Vous avez dit : &amp;quot;);&lt;br /&gt;
				strcat(response, buffer);&lt;br /&gt;
				// Un seul envoie permet de ne pas surcharger le réseau&lt;br /&gt;
				send(clientSocket, response, strlen(response), 0);&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
		if (isClosed == 1) {&lt;br /&gt;
			// La socket est fermé ou le client veut quitter le serveur !&lt;br /&gt;
			printf(&amp;quot;Fermeture de la connexion avec le client\n&amp;quot;);&lt;br /&gt;
			// Fermeture de la socket&lt;br /&gt;
			close(clientSocket);&lt;br /&gt;
			// On fait de la place dans le tableau&lt;br /&gt;
			clients[i] = -1;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On peut interroger notre serveur avec &#039;&#039;telnet&#039;&#039; (&#039;&#039;yum -y install telnet&#039;&#039;):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# telnet 127.0.0.1 8080&lt;br /&gt;
Trying 127.0.0.1...&lt;br /&gt;
Connected to 127.0.0.1.&lt;br /&gt;
Escape character is &#039;^]&#039;.&lt;br /&gt;
Entrez &#039;exit&#039; pour quitter&lt;br /&gt;
coucou&lt;br /&gt;
Vous avez dit : coucou&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== Serveur UDP ==&lt;br /&gt;
Si on essaye de reproduire le même type de serveur mais cette fois-ci avec une socket en UDP, cela devient beaucoup plus simple. En effet, en UDP, plus besoin de tenir un tableau avec les descripteurs des sockets des clients car... on est en mode déconnecté (datagramme) ! Entendez par la que les clients envoie un message sans attendre de réponse et sans se soucier de savoir si le message est arrivé au serveur.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;arpa/inet.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Port d&#039;écoute de la socket&lt;br /&gt;
#define PORT 8080&lt;br /&gt;
// Adresse d&#039;écoute (toutes les adresses)&lt;br /&gt;
#define IP INADDR_ANY&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
// Taille du tampon de lecture des messages&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse);&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse);&lt;br /&gt;
void manageClient(int serverSocket);&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
	// Structure contenant l&#039;adresse&lt;br /&gt;
	struct sockaddr_in adresse;&lt;br /&gt;
	initAdresse(&amp;amp;adresse);&lt;br /&gt;
	// Descripteur de la socket du serveur&lt;br /&gt;
	int serverSocket = initSocket(&amp;amp;adresse);&lt;br /&gt;
	while (1) {&lt;br /&gt;
		// Descripteur de la socket du client, on attend une connexion&lt;br /&gt;
		manageClient(serverSocket);&lt;br /&gt;
	}&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
// Initialisation de la structure sockaddr_in&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse) {&lt;br /&gt;
	(*adresse).sin_family = AF_INET;&lt;br /&gt;
	(*adresse).sin_addr.s_addr = IP;&lt;br /&gt;
	(*adresse).sin_port = htons( PORT);&lt;br /&gt;
}&lt;br /&gt;
// Démarrage de la socket serveur&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse) {&lt;br /&gt;
	// Descripteur de socket&lt;br /&gt;
	int fdsocket;&lt;br /&gt;
	// Nombre d&#039;options&lt;br /&gt;
	int opt = 1;&lt;br /&gt;
	// Création de la socket en UDP&lt;br /&gt;
	if ((fdsocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Création de la socket\n&amp;quot;);&lt;br /&gt;
	// Paramètrage de la socket&lt;br /&gt;
	if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,&lt;br /&gt;
			sizeof(opt)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Paramètrage de la socket\n&amp;quot;);&lt;br /&gt;
	// Attachement de la socket sur le port et l&#039;adresse IP&lt;br /&gt;
	if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Attachement de la socket sur le port %i\n&amp;quot;, PORT);&lt;br /&gt;
	return fdsocket;&lt;br /&gt;
}&lt;br /&gt;
// On traite l&#039;input des clients&lt;br /&gt;
void manageClient(int serverSocket) {&lt;br /&gt;
	// Création d&#039;un tampon pour stocker les messages des clients dans la heap&lt;br /&gt;
	static char buffer[BUFFER_LEN + 1];&lt;br /&gt;
	// Structure contenant l&#039;adresse du client&lt;br /&gt;
	struct sockaddr_in clientAdresse;&lt;br /&gt;
	unsigned int addrLen = sizeof(clientAdresse);&lt;br /&gt;
	// On vérifie si on a reçu un message&lt;br /&gt;
	int len = recvfrom(serverSocket, buffer, BUFFER_LEN, MSG_DONTWAIT,&lt;br /&gt;
			(struct sockaddr*) &amp;amp;clientAdresse, &amp;amp;addrLen);&lt;br /&gt;
	if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
		printf(&amp;quot;Problème de socket %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	} else if (len &amp;gt; 0) {&lt;br /&gt;
		printf(&amp;quot;Message reçu de %s:%i\n&amp;quot;, inet_ntoa(clientAdresse.sin_addr),&lt;br /&gt;
				ntohs(clientAdresse.sin_port));&lt;br /&gt;
		// Ajout du terminateur de chaîne&lt;br /&gt;
		buffer[len] = &#039;\0&#039;;&lt;br /&gt;
		// On renvoie le texte au client dans un buffer assez grand&lt;br /&gt;
		len = strlen(&amp;quot;Vous avez dit : &amp;quot;) + strlen(buffer) + 1;&lt;br /&gt;
		char response[len];&lt;br /&gt;
		strcpy(response, &amp;quot;Vous avez dit : &amp;quot;);&lt;br /&gt;
		strcat(response, buffer);&lt;br /&gt;
		// Un seul envoie permet de ne pas surcharger le réseau&lt;br /&gt;
		sendto(serverSocket, response, strlen(response), MSG_DONTWAIT,&lt;br /&gt;
				(struct sockaddr*) &amp;amp;clientAdresse, addrLen);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
On peut interroger notre serveur avec &#039;&#039;netcat&#039;&#039; (&#039;&#039;yum -y install nc&#039;&#039;):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# nc -u 127.0.0.1 8080&lt;br /&gt;
coucou&lt;br /&gt;
Vous avez dit : coucou&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
= Notification évènementielle avec epoll =&lt;br /&gt;
Depuis la version 2.5 du noyau Linux il est possible d&#039;utiliser une série de fonctions, appartenant à l&#039;espace utilisateur (user-space), permettant de suivre l&#039;état d&#039;un descripteur de fichier.&lt;br /&gt;
La différence avec read, recv ou encore recvfrom c&#039;est que l&#039;on va pouvoir surveiller plusieurs descripteurs de fichiers en même temps, laissant le CPU libre pour effectuer autre chose !&lt;br /&gt;
&lt;br /&gt;
== Utilisation ==&lt;br /&gt;
Tout d&#039;abord il faut créer un descripteur de fichier de type &#039;&#039;epoll&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int epoll_create1(int flags);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* flags &amp;amp;rarr; EPOLL_CLOEXEC pour éviter les situation de compétition en environnement parallélisé;&lt;br /&gt;
* le code retour correspond au descripteur &#039;&#039;epoll&#039;&#039; ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée).&lt;br /&gt;
&lt;br /&gt;
Une fois le descripteur &#039;&#039;epoll&#039;&#039; créé, il faut lui associer un événement:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* epfd &amp;amp;rarr; le descripteur &#039;&#039;epoll&#039;&#039; à modifier;&lt;br /&gt;
* op &amp;amp;rarr; une opération parmis:&lt;br /&gt;
** EPOLL_CTL_ADD &amp;amp;rarr; pour ajouter un événement sur un descripteur de fichier;&lt;br /&gt;
** EPOLL_CTL_MOD &amp;amp;rarr; pour modifier un événement sur un descripteur de fichier;&lt;br /&gt;
** EPOLL_CTL_DEL &amp;amp;rarr; pour supprimer un événement sur un descripteur de fichier;&lt;br /&gt;
* fd &amp;amp;rarr; un descripteur de fichier à surveiller;&lt;br /&gt;
* event &amp;amp;rarr; la structure contenant l&#039;événement à surveiller (&#039;&#039;EPOLLIN&#039;&#039;, &#039;&#039;EPOLLOUT&#039;&#039;, EPOLLRDHUP&#039;&#039;, ...);&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; en cas de succès ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée).&lt;br /&gt;
&lt;br /&gt;
Voici la structure &#039;&#039;epoll_event&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
struct epoll_event {&lt;br /&gt;
	uint32_t events;    /* Epoll events */&lt;br /&gt;
	epoll_data_t data;  /* User data variable */&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
typedef union epoll_data {&lt;br /&gt;
	void    *ptr;&lt;br /&gt;
	int      fd;&lt;br /&gt;
	uint32_t u32;&lt;br /&gt;
	uint64_t u64;&lt;br /&gt;
} epoll_data_t;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Enfin, il ne reste plus qu&#039;à &#039;&#039;attendre&#039;&#039; des événements:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* epfd &amp;amp;rarr; le descripteur &#039;&#039;epoll&#039;&#039; à modifier;&lt;br /&gt;
* events &amp;amp;rarr; un tableau de &#039;&#039;epoll_event contenant les événements;&lt;br /&gt;
* maxevents &amp;amp;rarr; le nombre maximum d&#039;événements à traiter (selon la taille du tableau);&lt;br /&gt;
* timeout &amp;amp;rarr; le temps, en millisecondes que la fonction attend:&lt;br /&gt;
** &#039;&#039;-1&#039;&#039; &amp;amp;rarr; la fonction devient bloquante;&lt;br /&gt;
** &#039;&#039;0&#039;&#039; &amp;amp;rarr; retourne immédiatement;&lt;br /&gt;
** &#039;&#039;&amp;gt; 0&#039;&#039; &amp;amp;rarr; attend le nombre de millisecondes spécifiées;&lt;br /&gt;
* le code retour correspond au nombre d&#039;événements ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée).&lt;br /&gt;
&lt;br /&gt;
== Améliorations architecturales ==&lt;br /&gt;
=== Gestion des utilisateurs===&lt;br /&gt;
Tout d&#039;abord, le faite de laisser &#039;&#039;epoll&#039;&#039; gérer les descripteurs de socket nous permet de supprimer le tableau &#039;&#039;clients&#039;&#039; ainsi que la variable &#039;&#039;NB_CLIENTS&#039;&#039;. En effet, la référence vers le descripteur est maintenu par &#039;&#039;epoll&#039;&#039; et nous pouvons gérer maintenant un nombre &#039;&#039;illimité&#039;&#039; de clients.&lt;br /&gt;
=== Performances ===&lt;br /&gt;
La boucle &#039;&#039;while&#039;&#039; de notre serveur consomme énormément de CPU, environ 100% de CPU par client ! On aurais pu utiliser un temporisateur avec &#039;&#039;nanosleep&#039;&#039; pour baisser la consommation CPU entre 5% et 10%...&lt;br /&gt;
Le faîte de basculer sur une fonction bloquante comme &#039;&#039;epoll_wait&#039;&#039; qui utilise des signaux (interruptions) permet de réduire considérablement la consommation CPU. Gardez à l&#039;esprit qu&#039;il n&#039;y a rien de mieux que les interruptions, la preuve, lorsque l&#039;on fait un &#039;&#039;top&#039;&#039;, notre programme à complétement disparu de la liste !&lt;br /&gt;
&lt;br /&gt;
== Modification du serveur ==&lt;br /&gt;
Modifions le code du serveur TCP pour y intégrer &#039;&#039;epoll&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;arpa/inet.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Port d&#039;écoute de la socket&lt;br /&gt;
#define PORT 8080&lt;br /&gt;
// Adresse d&#039;écoute (toutes les adresses)&lt;br /&gt;
#define IP INADDR_ANY&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
// Nombre de connexions clients&lt;br /&gt;
#define NB_EVENTS 2&lt;br /&gt;
// Taille du tampon de lecture des messages&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Commande pour arrêter le serveur&lt;br /&gt;
#define EXIT_WORD &amp;quot;exit&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse);&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse);&lt;br /&gt;
int waitForClient(int * serverSocket);&lt;br /&gt;
void addClientToTab(int * epfd, int * clientSocket);&lt;br /&gt;
void manageClient(int * epfd, int * clientSocket);&lt;br /&gt;
void addEpollEvent(int * epfd, int * socket);&lt;br /&gt;
void delEpollEvent(int * epfd, int * socket);&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
	// Création du descripteur EPOLL&lt;br /&gt;
	int epfd = epoll_create1(0);&lt;br /&gt;
	// Structure contenant les événements EPOLL&lt;br /&gt;
	struct epoll_event events[NB_EVENTS];&lt;br /&gt;
	// Structure contenant l&#039;adresse&lt;br /&gt;
	struct sockaddr_in adresse;&lt;br /&gt;
	initAdresse(&amp;amp;adresse);&lt;br /&gt;
	// Descripteur de la socket du serveur&lt;br /&gt;
	int serverSocket = initSocket(&amp;amp;adresse);&lt;br /&gt;
	addEpollEvent(&amp;amp;epfd, &amp;amp;serverSocket);&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	while (1) {&lt;br /&gt;
		int nfds = epoll_wait(epfd, events, NB_EVENTS, -1);&lt;br /&gt;
		for (int i = 0; i &amp;lt; nfds; i++) {&lt;br /&gt;
			if (events[i].data.fd == serverSocket) {&lt;br /&gt;
				if ((clientSocket = waitForClient(&amp;amp;serverSocket)) != -1) {&lt;br /&gt;
					// On ajoute le nouveau client au tableau des descripteurs&lt;br /&gt;
					addClientToTab(&amp;amp;epfd, &amp;amp;clientSocket);&lt;br /&gt;
				}&lt;br /&gt;
			} else if (events[i].events &amp;amp; EPOLLIN) {&lt;br /&gt;
				// On a reçu quelque chose&lt;br /&gt;
				manageClient(&amp;amp;epfd, &amp;amp;events[i].data.fd);&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	close(serverSocket);&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
// Initialisation de la structure sockaddr_in&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse) {&lt;br /&gt;
	(*adresse).sin_family = AF_INET;&lt;br /&gt;
	(*adresse).sin_addr.s_addr = IP;&lt;br /&gt;
	(*adresse).sin_port = htons( PORT);&lt;br /&gt;
}&lt;br /&gt;
// Démarrage de la socket serveur&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse) {&lt;br /&gt;
	// Descripteur de socket&lt;br /&gt;
	int fdsocket;&lt;br /&gt;
	// Nombre d&#039;options&lt;br /&gt;
	int opt = 1;&lt;br /&gt;
	// Création de la socket en TCP&lt;br /&gt;
	if ((fdsocket = socket(AF_INET, SOCK_STREAM, 0)) == -1) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Création de la socket\n&amp;quot;);&lt;br /&gt;
	// Paramètrage de la socket&lt;br /&gt;
	if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,&lt;br /&gt;
			sizeof(opt)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Paramètrage de la socket\n&amp;quot;);&lt;br /&gt;
	// Attachement de la socket sur le port et l&#039;adresse IP&lt;br /&gt;
	if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Attachement de la socket sur le port %i\n&amp;quot;, PORT);&lt;br /&gt;
	// Passage en écoute de la socket&lt;br /&gt;
	if (listen(fdsocket, BACKLOG) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la mise en écoute: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	// Passage en mode non bloquant&lt;br /&gt;
	fcntl(fdsocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
	printf(&amp;quot;Mise en écoute de la socket\n&amp;quot;);&lt;br /&gt;
	return fdsocket;&lt;br /&gt;
}&lt;br /&gt;
// Attente de connexion d&#039;un client&lt;br /&gt;
int waitForClient(int * serverSocket) {&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	// Structure contenant l&#039;adresse du client&lt;br /&gt;
	struct sockaddr_in clientAdresse;&lt;br /&gt;
	int addrLen = sizeof(clientAdresse);&lt;br /&gt;
	if ((clientSocket = accept(*serverSocket, (struct sockaddr*) &amp;amp;clientAdresse,&lt;br /&gt;
			(socklen_t*) &amp;amp;addrLen)) != -1) {&lt;br /&gt;
		// Convertion de l&#039;IP en texte&lt;br /&gt;
		char ip[INET_ADDRSTRLEN];&lt;br /&gt;
		inet_ntop(AF_INET, &amp;amp;(clientAdresse.sin_addr), ip, INET_ADDRSTRLEN);&lt;br /&gt;
		printf(&amp;quot;Connexion de %s:%i\n&amp;quot;, ip, clientAdresse.sin_port);&lt;br /&gt;
		// Passage en mode non bloquant&lt;br /&gt;
		int opt = 1;&lt;br /&gt;
		setsockopt(clientSocket, SOL_SOCKET, SO_KEEPALIVE, &amp;amp;opt, sizeof(1));&lt;br /&gt;
	}&lt;br /&gt;
	return clientSocket;&lt;br /&gt;
}&lt;br /&gt;
// Ajoute les clients au tableau&lt;br /&gt;
void addClientToTab(int * epfd, int * clientSocket) {&lt;br /&gt;
	addEpollEvent(epfd, clientSocket);&lt;br /&gt;
	// Nouvelle connexion, envoie du message de bienvenu&lt;br /&gt;
	send(*clientSocket, &amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;,&lt;br /&gt;
			strlen(&amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;), MSG_DONTWAIT);&lt;br /&gt;
}&lt;br /&gt;
// On traite l&#039;input des clients&lt;br /&gt;
void manageClient(int * epfd, int * clientSocket) {&lt;br /&gt;
	// Création d&#039;un tampon pour stocker les messages des clients dans la heap&lt;br /&gt;
	static char buffer[BUFFER_LEN + 1];&lt;br /&gt;
	int len = recv(*clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
	// Booléen pour suivre l&#039;état de la socket&lt;br /&gt;
	int isClosed = 0;&lt;br /&gt;
	if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
		// Une erreur est survenue&lt;br /&gt;
		printf(&amp;quot;Errno [%i] : %s\n&amp;quot;, errno, strerror(errno));&lt;br /&gt;
		isClosed = 1;&lt;br /&gt;
	} else if (len == 0) {&lt;br /&gt;
		// Le client s&#039;est déconnecté (extrémité de la socket fermée)&lt;br /&gt;
		isClosed = 1;&lt;br /&gt;
	} else if (len &amp;gt; 0) {&lt;br /&gt;
		// Ajout du terminateur de chaîne&lt;br /&gt;
		buffer[len] = &#039;\0&#039;;&lt;br /&gt;
		if (strncmp(buffer, EXIT_WORD, 4) == 0) {&lt;br /&gt;
			// Le client veut se déconnecter&lt;br /&gt;
			send(*clientSocket, &amp;quot;Bye\n&amp;quot;, strlen(&amp;quot;Bye\n&amp;quot;), 0);&lt;br /&gt;
			isClosed = 1;&lt;br /&gt;
		} else {&lt;br /&gt;
			// On renvoie le texte au client dans un buffer assez grand&lt;br /&gt;
			int len = strlen(&amp;quot;Vous avez dit : &amp;quot;) + strlen(buffer) + 1;&lt;br /&gt;
			char response[len];&lt;br /&gt;
			strcpy(response, &amp;quot;Vous avez dit : &amp;quot;);&lt;br /&gt;
			strcat(response, buffer);&lt;br /&gt;
			// Un seul envoie permet de ne pas surcharger le réseau&lt;br /&gt;
			send(*clientSocket, response, strlen(response), 0);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	if (isClosed == 1) {&lt;br /&gt;
		// On enlève la socket des événements EPOLL&lt;br /&gt;
		delEpollEvent(epfd, clientSocket);&lt;br /&gt;
		// La socket est fermé ou le client veut quitter le serveur !&lt;br /&gt;
		printf(&amp;quot;Fermeture de la connexion avec le client\n&amp;quot;);&lt;br /&gt;
		// Fermeture de la socket&lt;br /&gt;
		close(*clientSocket);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// Ajoute un descripteur de socket aux événements EPOLL&lt;br /&gt;
void addEpollEvent(int * epfd, int * socket) {&lt;br /&gt;
	printf(&amp;quot;Ajout de la socket %i dans les événements EPOLL\n&amp;quot;, *socket);&lt;br /&gt;
	// Structure contenant les événements EPOLL&lt;br /&gt;
	struct epoll_event event;&lt;br /&gt;
	// On enregistre la socket du serveur comme descripteur EPOLL&lt;br /&gt;
	event.data.fd = *socket;&lt;br /&gt;
	// On surveille les message entrant, la fermeture de la socket&lt;br /&gt;
	event.events = EPOLLIN | EPOLLRDHUP;&lt;br /&gt;
	epoll_ctl(*epfd, EPOLL_CTL_ADD, *socket, &amp;amp;event);&lt;br /&gt;
}&lt;br /&gt;
// Efface un descripteur de socket des événements EPOLL&lt;br /&gt;
void delEpollEvent(int * epfd, int * socket) {&lt;br /&gt;
	printf(&amp;quot;Suppression de la socket %i des événements EPOLL\n&amp;quot;, *socket);&lt;br /&gt;
	// Structure contenant les événements EPOLL&lt;br /&gt;
	struct epoll_event event;&lt;br /&gt;
	// On enregistre la socket du serveur comme descripteur EPOLL&lt;br /&gt;
	event.data.fd = *socket;&lt;br /&gt;
	// On surveille les message entrant, la fermeture de la socket&lt;br /&gt;
	event.events = EPOLLIN | EPOLLRDHUP;&lt;br /&gt;
	epoll_ctl(*epfd, EPOLL_CTL_DEL, *socket, &amp;amp;event);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Voici ce que l&#039;on peut observer dans la console:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Création de la socket&lt;br /&gt;
Paramètrage de la socket&lt;br /&gt;
Attachement de la socket sur le port 8080&lt;br /&gt;
Mise en écoute de la socket&lt;br /&gt;
Ajout de la socket 4 dans les événements EPOLL&lt;br /&gt;
Connexion de 127.0.0.1:17607&lt;br /&gt;
Ajout de la socket 5 dans les événements EPOLL&lt;br /&gt;
Suppression de la socket 5 des événements EPOLL&lt;br /&gt;
Fermeture de la connexion avec le client&lt;br /&gt;
Connexion de 127.0.0.1:18119&lt;br /&gt;
Ajout de la socket 5 dans les événements EPOLL&lt;br /&gt;
Connexion de 127.0.0.1:18631&lt;br /&gt;
Ajout de la socket 6 dans les événements EPOLL&lt;br /&gt;
Suppression de la socket 5 des événements EPOLL&lt;br /&gt;
Fermeture de la connexion avec le client&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Magsss</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=C_socket&amp;diff=3276</id>
		<title>C socket</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=C_socket&amp;diff=3276"/>
		<updated>2018-11-01T18:18:41Z</updated>

		<summary type="html">&lt;p&gt;Magsss : /* Lecture */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
||&lt;br /&gt;
Les sockets permettent de connecter deux programmes qui s&#039;exécutent sur deux machines différentes. Cette connexion se fait à travers le réseau grâce à l&#039;utilisation d&#039;un port (TCP ou UDP) et d&#039;une adresse IP. &lt;br /&gt;
&lt;br /&gt;
Il y a deux types de sockets, une qui est démarrée par la partie serveur en &#039;&#039;écoute&#039;&#039; et l&#039;autre démarrée par la partie cliente qui se connecte à la première.&lt;br /&gt;
&lt;br /&gt;
Ci-contre une image résumant les différentes étapes pour arriver à l&#039;envoi de données.&lt;br /&gt;
||&lt;br /&gt;
[[Fichier:Socket workflow.png|centré|200px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Création  =&lt;br /&gt;
== Côté serveur ==&lt;br /&gt;
Tout d&#039;abord il faut créer l&#039;objet socket:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt; &lt;br /&gt;
&lt;br /&gt;
int socket(int domain, int type, int protocol)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*domain &amp;amp;rarr; integer, communication domain e.g., AF_INET (IPv4 protocol) , AF_INET6 (IPv6 protocol)&lt;br /&gt;
*type &amp;amp;rarr; type de communication&lt;br /&gt;
**SOCK_STREAM: TCP&lt;br /&gt;
**SOCK_DGRAM: UDP&lt;br /&gt;
**SOCK_RAW: socket à l&#039;état brut (bas niveau)&lt;br /&gt;
*protocol &amp;amp;rarr; valeur du champ &#039;&#039;protocol&#039;&#039; de l&#039;en-tête de niveau 3 (généralement &#039;&#039;0&#039;&#039;)&lt;br /&gt;
*la valeur de retour est le fichier descripteur de la socket ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Une fois le descripteur de socket créé, il est possible de le configurer:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt; &lt;br /&gt;
&lt;br /&gt;
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* level &amp;amp;rarr; niveau d&#039;application de l&#039;option (SOL_SOCKET, TCP, UDP, ...);&lt;br /&gt;
* optname &amp;amp;rarr; le nom de l&#039;option (&#039;&#039;SO_REUSEADDR&#039;&#039;, &#039;&#039;SO_REUSEPORT&#039;&#039;, &#039;&#039;SO_KEEPALIVE&#039;&#039;, ...)&lt;br /&gt;
* optval, optlen  &amp;amp;rarr; utilisé pour accéder aux options de la socket;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
Il existe plusieurs options de socket, les plus utilisées étant :&lt;br /&gt;
* &#039;&#039;SO_REUSEADDR&#039;&#039; &amp;amp;rarr; permet de réutiliser l&#039;adresse de la socket tout de suite même si cette dernière est dans l&#039;état &#039;&#039;wait&#039;&#039;;&lt;br /&gt;
* &#039;&#039;SO_REUSEPORT&#039;&#039; &amp;amp;rarr; permet de réutiliser le port de la socket tout de suite même si cette dernière est dans l&#039;état &#039;&#039;wait&#039;&#039;;&lt;br /&gt;
* &#039;&#039;SO_KEEPALIVE&#039;&#039; &amp;amp;rarr; envoie des informations périodiquement pour tester si l&#039;extrémité du tunnel est toujours présente;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Il faut maintenant attacher la socket à un port Internet et une adresse IP:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* addr &amp;amp;rarr; une structure symbolisant l&#039;adresse &lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
struct sockaddr_in {&lt;br /&gt;
    short            sin_family;   // e.g: AF_INET&lt;br /&gt;
    unsigned short   sin_port;     // e.g: htons(3490)&lt;br /&gt;
    struct in_addr   sin_addr;     // détaillé ci-dessous&lt;br /&gt;
    char             sin_zero[8];  // &#039;0&#039; habituellement&lt;br /&gt;
};&lt;br /&gt;
struct in_addr {&lt;br /&gt;
    unsigned long s_addr;  // initialiser avec inet_aton()&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On peut maintenant passer la socket en état d&#039;écoute, elle est prête à recevoir des connexions:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int listen(int sockfd, int backlog)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* backlog &amp;amp;rarr; taille maximum de la file d&#039;attente de la socket après laquelle le système répond avec &#039;&#039;ECONNREFUSED&#039;&#039;;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On peut maintenant attendre une connexion:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* addr &amp;amp;rarr; l&#039;adresse du client;&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de la structure addr;&lt;br /&gt;
* le code retour varie entre un entier positif qui correspond au descripteur de la socket cliente et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
== Côté client ==&lt;br /&gt;
Après avoir créé la socket avec &#039;&#039;socket&#039;&#039; on peut se connecter à la partie serveur:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* addr &amp;amp;rarr; une structure symbolisant l&#039;adresse (cf. ci-dessus)&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
= Lecture / écriture =&lt;br /&gt;
== Fonctions génériques ==&lt;br /&gt;
Tout d&#039;abord, on peut dire que la socket est un fichier et se manipule donc comme un fichier !&lt;br /&gt;
===Lecture===&lt;br /&gt;
Pour lire dans une socket on peut utiliser &#039;&#039;read&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t read(int fd, void *buf, size_t count);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères où mettre le message reçu;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à recevoir;&lt;br /&gt;
* le code retour correspond au nombre de caractères reçus ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
===Écriture===&lt;br /&gt;
Pour écrire dans une socket ou peut utiliser &#039;&#039;write&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t write(int fd, const void *buf, size_t count);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères contenant le message à envoyer;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à envoyer;&lt;br /&gt;
* le code retour correspond au nombre de caractères envoyer ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
===Modification===&lt;br /&gt;
Pour modifier un descripteur fichier de socket on utilise &#039;&#039;fcntl&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int fcntl(int fd, int cmd, ... /* int args */ );&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* cmd &amp;amp;rarr; la commande de modification (descripteur, drapeaux, ...);&lt;br /&gt;
* args &amp;amp;rarr; les arguments correspondants à la commande&lt;br /&gt;
&lt;br /&gt;
Cette commande va nous permettre de modifier le drapeau du descripteur de la socket (&#039;&#039;F_SETFL&#039;&#039;) pour le rendre non bloquant (&#039;&#039;O_NONBLOCK&#039;&#039;) !&lt;br /&gt;
&lt;br /&gt;
== Fonctions spécifiques ==&lt;br /&gt;
Il existe des fonctions spécifiques pour manipuler les sockets. Ces fonctions ressemble au précédentes mais acceptent des drapeaux en plus pour pouvoir modifier le descripteur &#039;&#039;à la volé&#039;&#039;.&lt;br /&gt;
===Lecture===&lt;br /&gt;
Pour lire dans une socket on peut utiliser &#039;&#039;recv&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t recv(int fd, void *buf, size_t count, int flags);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères ou mettre le message reçu;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à recevoir;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;O_NONBLOCK&#039;&#039;);&lt;br /&gt;
* le code retour correspond au nombre de caractères reçu ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
===Écriture===&lt;br /&gt;
Pour écrire dans une socket ou peut utiliser &#039;&#039;send&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t send(int fd, const void *buf, size_t count, int flags);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères contenant le message à envoyer;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à envoyer;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;MSG_CONFIRM&#039;&#039;, &#039;&#039;MSG_DONTWAIT &#039;&#039;, ...);&lt;br /&gt;
* le code retour correspond au nombre de caractères envoyer ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
= Cas d&#039;utilisation =&lt;br /&gt;
== Création ==&lt;br /&gt;
Pour commencer, il faut créer le descripteur de fichier:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int fdsocket;&lt;br /&gt;
if ((fdsocket = socket(AF_INET, SOCK_STREAM, 0)) == -1) {&lt;br /&gt;
	printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Paramétrage des options ==&lt;br /&gt;
Il faut maintenant configurer la réutilisation de l&#039;adresse et du port&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int opt = 1;&lt;br /&gt;
if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,	sizeof(opt)) != 0) {&lt;br /&gt;
	printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Attachement ==&lt;br /&gt;
On peut maintenant attacher la socket à un port et une adresse:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
struct sockaddr_in adresse;&lt;br /&gt;
&lt;br /&gt;
adresse.sin_family = AF_INET;&lt;br /&gt;
// Ecoute sur toutes les adresses (INADDR_ANY &amp;lt;=&amp;gt; 0.0.0.0)&lt;br /&gt;
adresse.sin_addr.s_addr = INADDR_ANY;&lt;br /&gt;
// Conversion du port en valeur réseaux (Host TO Network Short)&lt;br /&gt;
adresse.sin_port = htons(8080);&lt;br /&gt;
&lt;br /&gt;
if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
	printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mise en écoute ==&lt;br /&gt;
La socket est prête à passer à l&#039;écoute des connexions des clients:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
&lt;br /&gt;
if (listen(fdsocket, BACKLOG) != 0) {&lt;br /&gt;
	printf(&amp;quot;Echéc de la mise en écoute: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Acceptation des connexions ==&lt;br /&gt;
Il est possible d&#039;accepter les nouvelles connexions:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int clientSocket;&lt;br /&gt;
// Structure contenant l&#039;adresse du client&lt;br /&gt;
struct sockaddr_in clientAdresse;&lt;br /&gt;
int addrLen = sizeof(clientAdresse);&lt;br /&gt;
if ((clientSocket = accept(*serverSocket, (struct sockaddr*) &amp;amp;clientAdresse, (socklen_t*) &amp;amp;addrLen)) != -1) {&lt;br /&gt;
	// Convertion de l&#039;IP en texte&lt;br /&gt;
	char ip[INET_ADDRSTRLEN];&lt;br /&gt;
	inet_ntop(AF_INET, &amp;amp;(clientAdresse.sin_addr), ip, INET_ADDRSTRLEN);&lt;br /&gt;
	printf(&amp;quot;Connexion de %s:%i\n&amp;quot;, ip, clientAdresse.sin_port);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Il est possible d&#039;utiliser &#039;&#039;fcntl&#039;&#039; pour passer le descripteur de la socket en non bloquant, avant l&#039;appel à la fonction &#039;&#039;accept&#039;&#039;. Si le descripteur reste bloquant, la fonction &#039;&#039;accept&#039;&#039; ne rendra pas la main tant qu&#039;un client ne se sera connecté, ayant pour résultat le blocage complet du programme !&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Passage en mode non bloquant&lt;br /&gt;
fcntl(fdsocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
On peut remarquer, au passage, l&#039;utilisation de &#039;&#039;inet_ntop&#039;&#039; pour convertir l&#039;adresse du client du format binaire au format text.&lt;br /&gt;
&lt;br /&gt;
== Lecture / Écriture ==&lt;br /&gt;
=== Mode connecté (TCP) ===&lt;br /&gt;
On peut maintenant utiliser le descripteur de la socket du client pour lire et écrire.&lt;br /&gt;
* avec les fonctions génériques:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Descripteur de la socket du client&lt;br /&gt;
int clientSocket;&lt;br /&gt;
char buffer[BUFFER_LEN];&lt;br /&gt;
// Passage en mode non bloquant, sinon read attend&lt;br /&gt;
fcntl(clientSocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
int len = read(clientSocket, buffer, BUFFER_LEN);&lt;br /&gt;
&lt;br /&gt;
send(clientSocket, &amp;quot;Coucou\n&amp;quot;, strlen(&amp;quot;Coucou\n&amp;quot;));&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* avec les fonctions spécifiques:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Descripteur de la socket du client&lt;br /&gt;
int clientSocket;&lt;br /&gt;
char buffer[BUFFER_LEN];&lt;br /&gt;
&lt;br /&gt;
int len = recv(clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
&lt;br /&gt;
send(clientSocket, &amp;quot;Coucou\n&amp;quot;, strlen(&amp;quot;Coucou\n&amp;quot;), MSG_DONTWAIT);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Analysons le retour de la fonction &#039;&#039;read&#039;&#039; ou &#039;&#039;recv&#039;&#039; lorsque l&#039;on utilise un descripteur non bloquant d&#039;après la [http://man7.org/linux/man-pages/man2/recvmsg.2.html documentation] :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
NAME &lt;br /&gt;
       recv, recvfrom, recvmsg - receive a message from a socket&lt;br /&gt;
[...]&lt;br /&gt;
MSG_DONTWAIT (since Linux 2.2)&lt;br /&gt;
    Enables nonblocking operation; if the operation would block, the call fails with the error EAGAIN or EWOULDBLOCK (this can also be enabled using the O_NONBLOCK flag with the F_SETFL fcntl(2)). &lt;br /&gt;
[...]&lt;br /&gt;
RETURN VALUE&lt;br /&gt;
[...]&lt;br /&gt;
These calls return the number of bytes received, or -1 if an error&lt;br /&gt;
       occurred.  In the event of an error, errno is set to indicate the&lt;br /&gt;
       error.&lt;br /&gt;
When a stream socket peer has performed an orderly shutdown, the&lt;br /&gt;
       return value will be 0 (the traditional &amp;quot;end-of-file&amp;quot; return).&lt;br /&gt;
[...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Le retour est :&lt;br /&gt;
* lorsque le descripteur est vide (aucune donnée de la part du client) &#039;&#039;-1&#039;&#039; et &#039;&#039;errno&#039;&#039; sera positionné à la valeur &#039;&#039;EAGAIN&#039;&#039;, il ne faut donc pas fermer la socket si le retour est &#039;&#039;-1&#039;&#039; ! Il faut d&#039;abord vérifier que &#039;&#039;errno&#039;&#039; &#039;&#039;&#039;n&#039;a pas&#039;&#039;&#039; la valeur &#039;&#039;EAGAIN&#039;&#039;;&lt;br /&gt;
* lorsque le retour est &#039;&#039;0&#039;&#039;, cela signifie que l&#039;extrémité est fermée, il faut donc fermer le descripteur local;&lt;br /&gt;
* lorsque la valeur est supérieur à &#039;&#039;0&#039;&#039; elle correspond au nombre d&#039;octets reçu.&lt;br /&gt;
&lt;br /&gt;
On peut donc en déduire le morceau de code suivant pour gérer le retour de &#039;&#039;read&#039;&#039; ou &#039;&#039;recv&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int len = recv(clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
&lt;br /&gt;
if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
	// Une erreur est survenue&lt;br /&gt;
} else if (len == 0) {&lt;br /&gt;
	// Le client s&#039;est déconnecté (extrémité de la socket fermée)			&lt;br /&gt;
} else if (len &amp;gt; 0) {&lt;br /&gt;
	// Le client à envoyé des données	&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Mode datagramme (UDP) ===&lt;br /&gt;
===Lecture===&lt;br /&gt;
Pour lire dans une socket on peut utiliser &#039;&#039;recvfrom&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t recvfrom(int fd, void *buf, size_t count, int flags, const struct sockaddr *addr, socklen_t addrlen);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères ou mettre le message reçu;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à recevoir;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;O_NONBLOCK&#039;&#039;);&lt;br /&gt;
*  addr &amp;amp;rarr; une structure symbolisant l&#039;adresse du client (cf. ci-dessus)&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour correspond au nombre de caractères reçu ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
===Écriture===&lt;br /&gt;
Pour écrire dans une socket ou peut utiliser &#039;&#039;sendto&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t sendto(int fd, const void *buf, size_t count, const struct sockaddr *addr, socklen_t addrlen);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères contenant le message à envoyer;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à envoyer;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;MSG_DONTWAIT &#039;&#039;, ...);&lt;br /&gt;
* addr &amp;amp;rarr; une structure symbolisant l&#039;adresse du client (cf. ci-dessus)&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour correspond au nombre de caractères envoyer ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
= Exemples =&lt;br /&gt;
== Serveur TCP ==&lt;br /&gt;
Voici un exemple de serveur &#039;&#039;echo&#039;&#039; qui renvoie le message au client. Les connexions utilisent TCP et un simple client comme telnet suffit pour l&#039;utiliser.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;arpa/inet.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Port d&#039;écoute de la socket&lt;br /&gt;
#define PORT 8080&lt;br /&gt;
// Adresse d&#039;écoute (toutes les adresses)&lt;br /&gt;
#define IP INADDR_ANY&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
// Nombre de connexions clients&lt;br /&gt;
#define NB_CLIENTS 2&lt;br /&gt;
// Taille du tampon de lecture des messages&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Commande pour arrêter le serveur&lt;br /&gt;
#define EXIT_WORD &amp;quot;exit&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse);&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse);&lt;br /&gt;
int waitForClient(int * serverSocket);&lt;br /&gt;
void addClientToTab(int clientSocket, int clients[]);&lt;br /&gt;
void manageClient(int clients[]);&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
	// Création et initialisation du tableau contenant les descripteurs des sockets clients&lt;br /&gt;
	int clients[NB_CLIENTS];&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_CLIENTS; i++) {&lt;br /&gt;
		clients[i] = -1;&lt;br /&gt;
	}&lt;br /&gt;
	// Structure contenant l&#039;adresse&lt;br /&gt;
	struct sockaddr_in adresse;&lt;br /&gt;
	initAdresse(&amp;amp;adresse);&lt;br /&gt;
	// Descripteur de la socket du serveur&lt;br /&gt;
	int serverSocket = initSocket(&amp;amp;adresse);&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	while (1) {&lt;br /&gt;
		// Descripteur de la socket du client, on attend une connexion&lt;br /&gt;
		if ((clientSocket = waitForClient(&amp;amp;serverSocket)) != -1) {&lt;br /&gt;
			// On ajoute le nouveau client au tableau des descripteurs&lt;br /&gt;
			addClientToTab(clientSocket, clients);&lt;br /&gt;
		}&lt;br /&gt;
		manageClient(clients);&lt;br /&gt;
	}&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
// Initialisation de la structure sockaddr_in&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse) {&lt;br /&gt;
	(*adresse).sin_family = AF_INET;&lt;br /&gt;
	(*adresse).sin_addr.s_addr = IP;&lt;br /&gt;
	(*adresse).sin_port = htons( PORT);&lt;br /&gt;
}&lt;br /&gt;
// Démarrage de la socket serveur&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse) {&lt;br /&gt;
	// Descripteur de socket&lt;br /&gt;
	int fdsocket;&lt;br /&gt;
	// Nombre d&#039;options&lt;br /&gt;
	int opt = 1;&lt;br /&gt;
	// Création de la socket en TCP&lt;br /&gt;
	if ((fdsocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Création de la socket\n&amp;quot;);&lt;br /&gt;
	// Paramètrage de la socket&lt;br /&gt;
	if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,&lt;br /&gt;
			sizeof(opt)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Paramètrage de la socket\n&amp;quot;);&lt;br /&gt;
	// Attachement de la socket sur le port et l&#039;adresse IP&lt;br /&gt;
	if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Attachement de la socket sur le port %i\n&amp;quot;, PORT);&lt;br /&gt;
	// Passage en écoute de la socket&lt;br /&gt;
	if (listen(fdsocket, BACKLOG) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la mise en écoute: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	// Passage en mode non bloquant&lt;br /&gt;
	fcntl(fdsocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
	printf(&amp;quot;Mise en écoute de la socket\n&amp;quot;);&lt;br /&gt;
	return fdsocket;&lt;br /&gt;
}&lt;br /&gt;
// Attente de connexion d&#039;un client&lt;br /&gt;
int waitForClient(int * serverSocket) {&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	// Structure contenant l&#039;adresse du client&lt;br /&gt;
	struct sockaddr_in clientAdresse;&lt;br /&gt;
	int addrLen = sizeof(clientAdresse);&lt;br /&gt;
	if ((clientSocket = accept(*serverSocket, (struct sockaddr*) &amp;amp;clientAdresse,&lt;br /&gt;
			(socklen_t*) &amp;amp;addrLen)) != -1) {&lt;br /&gt;
		// Convertion de l&#039;IP en texte&lt;br /&gt;
		char ip[INET_ADDRSTRLEN];&lt;br /&gt;
		inet_ntop(AF_INET, &amp;amp;(clientAdresse.sin_addr), ip, INET_ADDRSTRLEN);&lt;br /&gt;
		printf(&amp;quot;Connexion de %s:%i\n&amp;quot;, ip, clientAdresse.sin_port);&lt;br /&gt;
		// Passage en mode non bloquant&lt;br /&gt;
		int opt = 1;&lt;br /&gt;
		setsockopt(clientSocket, SOL_SOCKET, SO_KEEPALIVE, &amp;amp;opt, sizeof(1));&lt;br /&gt;
	}&lt;br /&gt;
	return clientSocket;&lt;br /&gt;
}&lt;br /&gt;
// Ajoute les clients au tableau&lt;br /&gt;
void addClientToTab(int clientSocket, int clients[]) {&lt;br /&gt;
	// On vérifie si on à de la place de libre&lt;br /&gt;
	int found = -1;&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_CLIENTS; i++) {&lt;br /&gt;
		// On cherche de la place&lt;br /&gt;
		if (clients[i] == -1) {&lt;br /&gt;
			// On ajoute le client&lt;br /&gt;
			printf(&amp;quot;Ajout du client à l&#039;index %i\n&amp;quot;, i);&lt;br /&gt;
			clients[i] = clientSocket;&lt;br /&gt;
			// Nouvelle connexion, envoie du message de bienvenu&lt;br /&gt;
			send(clientSocket, &amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;, 	strlen(&amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;),&lt;br /&gt;
					MSG_DONTWAIT);&lt;br /&gt;
			found = 0;&lt;br /&gt;
			break;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	if (found == -1) {&lt;br /&gt;
		// On a plus de place de libre&lt;br /&gt;
		puts(&amp;quot;Plus de place, désolé&amp;quot;);&lt;br /&gt;
		close(clientSocket);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// On traite l&#039;input des clients&lt;br /&gt;
void manageClient(int clients[]) {&lt;br /&gt;
	// Création d&#039;un tampon pour stocker les messages des clients dans la heap&lt;br /&gt;
	static char buffer[BUFFER_LEN + 1];&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_CLIENTS; i++) {&lt;br /&gt;
		// On vérifie les messages&lt;br /&gt;
		int clientSocket = clients[i];&lt;br /&gt;
		if (clientSocket == -1) {&lt;br /&gt;
			continue;&lt;br /&gt;
		}&lt;br /&gt;
		int len = recv(clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
		// Booléen pour suivre l&#039;état de la socket&lt;br /&gt;
		int isClosed = 0;&lt;br /&gt;
		if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
			// Une erreur est survenue&lt;br /&gt;
			printf(&amp;quot;Errno [%i] : %s\n&amp;quot;, errno, strerror(errno));&lt;br /&gt;
			isClosed = 1;&lt;br /&gt;
		} else if (len == 0) {&lt;br /&gt;
			// Le client s&#039;est déconnecté (extrémité de la socket fermée)&lt;br /&gt;
			isClosed = 1;&lt;br /&gt;
		} else if (len &amp;gt; 0) {&lt;br /&gt;
			// Ajout du terminateur de chaîne&lt;br /&gt;
			buffer[len] = &#039;\0&#039;;&lt;br /&gt;
			if (strncmp(buffer, EXIT_WORD, 4) == 0) {&lt;br /&gt;
				// Le client veut se déconnecter&lt;br /&gt;
				send(clientSocket, &amp;quot;Bye\n&amp;quot;, strlen(&amp;quot;Bye\n&amp;quot;), 0);&lt;br /&gt;
				isClosed = 1;&lt;br /&gt;
			} else {&lt;br /&gt;
				// On renvoie le texte au client dans un buffer assez grand&lt;br /&gt;
				int len = strlen(&amp;quot;Vous avez dit : &amp;quot;) + strlen(buffer) + 1;&lt;br /&gt;
				char response[len];&lt;br /&gt;
				strcpy(response, &amp;quot;Vous avez dit : &amp;quot;);&lt;br /&gt;
				strcat(response, buffer);&lt;br /&gt;
				// Un seul envoie permet de ne pas surcharger le réseau&lt;br /&gt;
				send(clientSocket, response, strlen(response), 0);&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
		if (isClosed == 1) {&lt;br /&gt;
			// La socket est fermé ou le client veut quitter le serveur !&lt;br /&gt;
			printf(&amp;quot;Fermeture de la connexion avec le client\n&amp;quot;);&lt;br /&gt;
			// Fermeture de la socket&lt;br /&gt;
			close(clientSocket);&lt;br /&gt;
			// On fait de la place dans le tableau&lt;br /&gt;
			clients[i] = -1;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On peut interroger notre serveur avec &#039;&#039;telnet&#039;&#039; (&#039;&#039;yum -y install telnet&#039;&#039;):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# telnet 127.0.0.1 8080&lt;br /&gt;
Trying 127.0.0.1...&lt;br /&gt;
Connected to 127.0.0.1.&lt;br /&gt;
Escape character is &#039;^]&#039;.&lt;br /&gt;
Entrez &#039;exit&#039; pour quitter&lt;br /&gt;
coucou&lt;br /&gt;
Vous avez dit : coucou&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== Serveur UDP ==&lt;br /&gt;
Si on essaye de reproduire le même type de serveur mais cette fois-ci avec une socket en UDP, cela devient beaucoup plus simple. En effet, en UDP, plus besoin de tenir un tableau avec les descripteurs des sockets des clients car... on est en mode déconnecté (datagramme) ! Entendez par la que les clients envoie un message sans attendre de réponse et sans se soucier de savoir si le message est arrivé au serveur.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;arpa/inet.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Port d&#039;écoute de la socket&lt;br /&gt;
#define PORT 8080&lt;br /&gt;
// Adresse d&#039;écoute (toutes les adresses)&lt;br /&gt;
#define IP INADDR_ANY&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
// Taille du tampon de lecture des messages&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse);&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse);&lt;br /&gt;
void manageClient(int serverSocket);&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
	// Structure contenant l&#039;adresse&lt;br /&gt;
	struct sockaddr_in adresse;&lt;br /&gt;
	initAdresse(&amp;amp;adresse);&lt;br /&gt;
	// Descripteur de la socket du serveur&lt;br /&gt;
	int serverSocket = initSocket(&amp;amp;adresse);&lt;br /&gt;
	while (1) {&lt;br /&gt;
		// Descripteur de la socket du client, on attend une connexion&lt;br /&gt;
		manageClient(serverSocket);&lt;br /&gt;
	}&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
// Initialisation de la structure sockaddr_in&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse) {&lt;br /&gt;
	(*adresse).sin_family = AF_INET;&lt;br /&gt;
	(*adresse).sin_addr.s_addr = IP;&lt;br /&gt;
	(*adresse).sin_port = htons( PORT);&lt;br /&gt;
}&lt;br /&gt;
// Démarrage de la socket serveur&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse) {&lt;br /&gt;
	// Descripteur de socket&lt;br /&gt;
	int fdsocket;&lt;br /&gt;
	// Nombre d&#039;options&lt;br /&gt;
	int opt = 1;&lt;br /&gt;
	// Création de la socket en UDP&lt;br /&gt;
	if ((fdsocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Création de la socket\n&amp;quot;);&lt;br /&gt;
	// Paramètrage de la socket&lt;br /&gt;
	if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,&lt;br /&gt;
			sizeof(opt)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Paramètrage de la socket\n&amp;quot;);&lt;br /&gt;
	// Attachement de la socket sur le port et l&#039;adresse IP&lt;br /&gt;
	if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Attachement de la socket sur le port %i\n&amp;quot;, PORT);&lt;br /&gt;
	return fdsocket;&lt;br /&gt;
}&lt;br /&gt;
// On traite l&#039;input des clients&lt;br /&gt;
void manageClient(int serverSocket) {&lt;br /&gt;
	// Création d&#039;un tampon pour stocker les messages des clients dans la heap&lt;br /&gt;
	static char buffer[BUFFER_LEN + 1];&lt;br /&gt;
	// Structure contenant l&#039;adresse du client&lt;br /&gt;
	struct sockaddr_in clientAdresse;&lt;br /&gt;
	unsigned int addrLen = sizeof(clientAdresse);&lt;br /&gt;
	// On vérifie si on a reçu un message&lt;br /&gt;
	int len = recvfrom(serverSocket, buffer, BUFFER_LEN, MSG_DONTWAIT,&lt;br /&gt;
			(struct sockaddr*) &amp;amp;clientAdresse, &amp;amp;addrLen);&lt;br /&gt;
	if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
		printf(&amp;quot;Problème de socket %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	} else if (len &amp;gt; 0) {&lt;br /&gt;
		printf(&amp;quot;Message reçu de %s:%i\n&amp;quot;, inet_ntoa(clientAdresse.sin_addr),&lt;br /&gt;
				ntohs(clientAdresse.sin_port));&lt;br /&gt;
		// Ajout du terminateur de chaîne&lt;br /&gt;
		buffer[len] = &#039;\0&#039;;&lt;br /&gt;
		// On renvoie le texte au client dans un buffer assez grand&lt;br /&gt;
		len = strlen(&amp;quot;Vous avez dit : &amp;quot;) + strlen(buffer) + 1;&lt;br /&gt;
		char response[len];&lt;br /&gt;
		strcpy(response, &amp;quot;Vous avez dit : &amp;quot;);&lt;br /&gt;
		strcat(response, buffer);&lt;br /&gt;
		// Un seul envoie permet de ne pas surcharger le réseau&lt;br /&gt;
		sendto(serverSocket, response, strlen(response), MSG_DONTWAIT,&lt;br /&gt;
				(struct sockaddr*) &amp;amp;clientAdresse, addrLen);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
On peut interroger notre serveur avec &#039;&#039;netcat&#039;&#039; (&#039;&#039;yum -y install nc&#039;&#039;):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# nc -u 127.0.0.1 8080&lt;br /&gt;
coucou&lt;br /&gt;
Vous avez dit : coucou&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
= Notification évènementielle avec epoll =&lt;br /&gt;
Depuis la version 2.5 du noyau Linux il est possible d&#039;utiliser une série de fonctions, appartenant à l&#039;espace utilisateur (user-space), permettant de suivre l&#039;état d&#039;un descripteur de fichier.&lt;br /&gt;
La différence avec read, recv ou encore recvfrom c&#039;est que l&#039;on va pouvoir surveiller plusieurs descripteurs de fichiers en même temps, laissant le CPU libre pour effectuer autre chose !&lt;br /&gt;
&lt;br /&gt;
== Utilisation ==&lt;br /&gt;
Tout d&#039;abord il faut créer un descripteur de fichier de type &#039;&#039;epoll&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int epoll_create1(int flags);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* flags &amp;amp;rarr; EPOLL_CLOEXEC pour éviter les situation de compétition en environnement parallélisé;&lt;br /&gt;
* le code retour correspond au descripteur &#039;&#039;epoll&#039;&#039; ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée).&lt;br /&gt;
&lt;br /&gt;
Une fois le descripteur &#039;&#039;epoll&#039;&#039; créé, il faut lui associer un événement:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* epfd &amp;amp;rarr; le descripteur &#039;&#039;epoll&#039;&#039; à modifier;&lt;br /&gt;
* op &amp;amp;rarr; une opération parmis:&lt;br /&gt;
** EPOLL_CTL_ADD &amp;amp;rarr; pour ajouter un événement sur un descripteur de fichier;&lt;br /&gt;
** EPOLL_CTL_MOD &amp;amp;rarr; pour modifier un événement sur un descripteur de fichier;&lt;br /&gt;
** EPOLL_CTL_DEL &amp;amp;rarr; pour supprimer un événement sur un descripteur de fichier;&lt;br /&gt;
* fd &amp;amp;rarr; un descripteur de fichier à surveiller;&lt;br /&gt;
* event &amp;amp;rarr; la structure contenant l&#039;événement à surveiller (&#039;&#039;EPOLLIN&#039;&#039;, &#039;&#039;EPOLLOUT&#039;&#039;, EPOLLRDHUP&#039;&#039;, ...);&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; en cas de succès ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée).&lt;br /&gt;
&lt;br /&gt;
Voici la structure &#039;&#039;epoll_event&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
struct epoll_event {&lt;br /&gt;
	uint32_t events;    /* Epoll events */&lt;br /&gt;
	epoll_data_t data;  /* User data variable */&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
typedef union epoll_data {&lt;br /&gt;
	void    *ptr;&lt;br /&gt;
	int      fd;&lt;br /&gt;
	uint32_t u32;&lt;br /&gt;
	uint64_t u64;&lt;br /&gt;
} epoll_data_t;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Enfin, il ne reste plus qu&#039;à &#039;&#039;attendre&#039;&#039; des événements:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* epfd &amp;amp;rarr; le descripteur &#039;&#039;epoll&#039;&#039; à modifier;&lt;br /&gt;
* events &amp;amp;rarr; un tableau de &#039;&#039;epoll_event contenant les événements;&lt;br /&gt;
* maxevents &amp;amp;rarr; le nombre maximum d&#039;événements à traiter (selon la taille du tableau);&lt;br /&gt;
* timeout &amp;amp;rarr; le temps, en millisecondes que la fonction attend:&lt;br /&gt;
** &#039;&#039;-1&#039;&#039; &amp;amp;rarr; la fonction devient bloquante;&lt;br /&gt;
** &#039;&#039;0&#039;&#039; &amp;amp;rarr; retourne immédiatement;&lt;br /&gt;
** &#039;&#039;&amp;gt; 0&#039;&#039; &amp;amp;rarr; attend le nombre de millisecondes spécifiées;&lt;br /&gt;
* le code retour correspond au nombre d&#039;événements ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée).&lt;br /&gt;
&lt;br /&gt;
== Améliorations architecturales ==&lt;br /&gt;
=== Gestion des utilisateurs===&lt;br /&gt;
Tout d&#039;abord, le faite de laisser &#039;&#039;epoll&#039;&#039; gérer les descripteurs de socket nous permet de supprimer le tableau &#039;&#039;clients&#039;&#039; ainsi que la variable &#039;&#039;NB_CLIENTS&#039;&#039;. En effet, la référence vers le descripteur est maintenu par &#039;&#039;epoll&#039;&#039; et nous pouvons gérer maintenant un nombre &#039;&#039;illimité&#039;&#039; de clients.&lt;br /&gt;
=== Performances ===&lt;br /&gt;
La boucle &#039;&#039;while&#039;&#039; de notre serveur consomme énormément de CPU, environ 100% de CPU par client ! On aurais pu utiliser un temporisateur avec &#039;&#039;nanosleep&#039;&#039; pour baisser la consommation CPU entre 5% et 10%...&lt;br /&gt;
Le faîte de basculer sur une fonction bloquante comme &#039;&#039;epoll_wait&#039;&#039; qui utilise des signaux (interruptions) permet de réduire considérablement la consommation CPU. Gardez à l&#039;esprit qu&#039;il n&#039;y a rien de mieux que les interruptions, la preuve, lorsque l&#039;on fait un &#039;&#039;top&#039;&#039;, notre programme à complétement disparu de la liste !&lt;br /&gt;
&lt;br /&gt;
== Modification du serveur ==&lt;br /&gt;
Modifions le code du serveur TCP pour y intégrer &#039;&#039;epoll&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;arpa/inet.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Port d&#039;écoute de la socket&lt;br /&gt;
#define PORT 8080&lt;br /&gt;
// Adresse d&#039;écoute (toutes les adresses)&lt;br /&gt;
#define IP INADDR_ANY&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
// Nombre de connexions clients&lt;br /&gt;
#define NB_EVENTS 2&lt;br /&gt;
// Taille du tampon de lecture des messages&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Commande pour arrêter le serveur&lt;br /&gt;
#define EXIT_WORD &amp;quot;exit&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse);&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse);&lt;br /&gt;
int waitForClient(int * serverSocket);&lt;br /&gt;
void addClientToTab(int * epfd, int * clientSocket);&lt;br /&gt;
void manageClient(int * epfd, int * clientSocket);&lt;br /&gt;
void addEpollEvent(int * epfd, int * socket);&lt;br /&gt;
void delEpollEvent(int * epfd, int * socket);&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
	// Création du descripteur EPOLL&lt;br /&gt;
	int epfd = epoll_create1(0);&lt;br /&gt;
	// Structure contenant les événements EPOLL&lt;br /&gt;
	struct epoll_event events[NB_EVENTS];&lt;br /&gt;
	// Structure contenant l&#039;adresse&lt;br /&gt;
	struct sockaddr_in adresse;&lt;br /&gt;
	initAdresse(&amp;amp;adresse);&lt;br /&gt;
	// Descripteur de la socket du serveur&lt;br /&gt;
	int serverSocket = initSocket(&amp;amp;adresse);&lt;br /&gt;
	addEpollEvent(&amp;amp;epfd, &amp;amp;serverSocket);&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	while (1) {&lt;br /&gt;
		int nfds = epoll_wait(epfd, events, NB_EVENTS, -1);&lt;br /&gt;
		for (int i = 0; i &amp;lt; nfds; i++) {&lt;br /&gt;
			if (events[i].data.fd == serverSocket) {&lt;br /&gt;
				if ((clientSocket = waitForClient(&amp;amp;serverSocket)) != -1) {&lt;br /&gt;
					// On ajoute le nouveau client au tableau des descripteurs&lt;br /&gt;
					addClientToTab(&amp;amp;epfd, &amp;amp;clientSocket);&lt;br /&gt;
				}&lt;br /&gt;
			} else if (events[i].events &amp;amp; EPOLLIN) {&lt;br /&gt;
				// On a reçu quelque chose&lt;br /&gt;
				manageClient(&amp;amp;epfd, &amp;amp;events[i].data.fd);&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	close(serverSocket);&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
// Initialisation de la structure sockaddr_in&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse) {&lt;br /&gt;
	(*adresse).sin_family = AF_INET;&lt;br /&gt;
	(*adresse).sin_addr.s_addr = IP;&lt;br /&gt;
	(*adresse).sin_port = htons( PORT);&lt;br /&gt;
}&lt;br /&gt;
// Démarrage de la socket serveur&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse) {&lt;br /&gt;
	// Descripteur de socket&lt;br /&gt;
	int fdsocket;&lt;br /&gt;
	// Nombre d&#039;options&lt;br /&gt;
	int opt = 1;&lt;br /&gt;
	// Création de la socket en TCP&lt;br /&gt;
	if ((fdsocket = socket(AF_INET, SOCK_STREAM, 0)) == -1) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Création de la socket\n&amp;quot;);&lt;br /&gt;
	// Paramètrage de la socket&lt;br /&gt;
	if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,&lt;br /&gt;
			sizeof(opt)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Paramètrage de la socket\n&amp;quot;);&lt;br /&gt;
	// Attachement de la socket sur le port et l&#039;adresse IP&lt;br /&gt;
	if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Attachement de la socket sur le port %i\n&amp;quot;, PORT);&lt;br /&gt;
	// Passage en écoute de la socket&lt;br /&gt;
	if (listen(fdsocket, BACKLOG) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la mise en écoute: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	// Passage en mode non bloquant&lt;br /&gt;
	fcntl(fdsocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
	printf(&amp;quot;Mise en écoute de la socket\n&amp;quot;);&lt;br /&gt;
	return fdsocket;&lt;br /&gt;
}&lt;br /&gt;
// Attente de connexion d&#039;un client&lt;br /&gt;
int waitForClient(int * serverSocket) {&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	// Structure contenant l&#039;adresse du client&lt;br /&gt;
	struct sockaddr_in clientAdresse;&lt;br /&gt;
	int addrLen = sizeof(clientAdresse);&lt;br /&gt;
	if ((clientSocket = accept(*serverSocket, (struct sockaddr*) &amp;amp;clientAdresse,&lt;br /&gt;
			(socklen_t*) &amp;amp;addrLen)) != -1) {&lt;br /&gt;
		// Convertion de l&#039;IP en texte&lt;br /&gt;
		char ip[INET_ADDRSTRLEN];&lt;br /&gt;
		inet_ntop(AF_INET, &amp;amp;(clientAdresse.sin_addr), ip, INET_ADDRSTRLEN);&lt;br /&gt;
		printf(&amp;quot;Connexion de %s:%i\n&amp;quot;, ip, clientAdresse.sin_port);&lt;br /&gt;
		// Passage en mode non bloquant&lt;br /&gt;
		int opt = 1;&lt;br /&gt;
		setsockopt(clientSocket, SOL_SOCKET, SO_KEEPALIVE, &amp;amp;opt, sizeof(1));&lt;br /&gt;
	}&lt;br /&gt;
	return clientSocket;&lt;br /&gt;
}&lt;br /&gt;
// Ajoute les clients au tableau&lt;br /&gt;
void addClientToTab(int * epfd, int * clientSocket) {&lt;br /&gt;
	addEpollEvent(epfd, clientSocket);&lt;br /&gt;
	// Nouvelle connexion, envoie du message de bienvenu&lt;br /&gt;
	send(*clientSocket, &amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;,&lt;br /&gt;
			strlen(&amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;), MSG_DONTWAIT);&lt;br /&gt;
}&lt;br /&gt;
// On traite l&#039;input des clients&lt;br /&gt;
void manageClient(int * epfd, int * clientSocket) {&lt;br /&gt;
	// Création d&#039;un tampon pour stocker les messages des clients dans la heap&lt;br /&gt;
	static char buffer[BUFFER_LEN + 1];&lt;br /&gt;
	int len = recv(*clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
	// Booléen pour suivre l&#039;état de la socket&lt;br /&gt;
	int isClosed = 0;&lt;br /&gt;
	if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
		// Une erreur est survenue&lt;br /&gt;
		printf(&amp;quot;Errno [%i] : %s\n&amp;quot;, errno, strerror(errno));&lt;br /&gt;
		isClosed = 1;&lt;br /&gt;
	} else if (len == 0) {&lt;br /&gt;
		// Le client s&#039;est déconnecté (extrémité de la socket fermée)&lt;br /&gt;
		isClosed = 1;&lt;br /&gt;
	} else if (len &amp;gt; 0) {&lt;br /&gt;
		// Ajout du terminateur de chaîne&lt;br /&gt;
		buffer[len] = &#039;\0&#039;;&lt;br /&gt;
		if (strncmp(buffer, EXIT_WORD, 4) == 0) {&lt;br /&gt;
			// Le client veut se déconnecter&lt;br /&gt;
			send(*clientSocket, &amp;quot;Bye\n&amp;quot;, strlen(&amp;quot;Bye\n&amp;quot;), 0);&lt;br /&gt;
			isClosed = 1;&lt;br /&gt;
		} else {&lt;br /&gt;
			// On renvoie le texte au client dans un buffer assez grand&lt;br /&gt;
			int len = strlen(&amp;quot;Vous avez dit : &amp;quot;) + strlen(buffer) + 1;&lt;br /&gt;
			char response[len];&lt;br /&gt;
			strcpy(response, &amp;quot;Vous avez dit : &amp;quot;);&lt;br /&gt;
			strcat(response, buffer);&lt;br /&gt;
			// Un seul envoie permet de ne pas surcharger le réseau&lt;br /&gt;
			send(*clientSocket, response, strlen(response), 0);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	if (isClosed == 1) {&lt;br /&gt;
		// On enlève la socket des événements EPOLL&lt;br /&gt;
		delEpollEvent(epfd, clientSocket);&lt;br /&gt;
		// La socket est fermé ou le client veut quitter le serveur !&lt;br /&gt;
		printf(&amp;quot;Fermeture de la connexion avec le client\n&amp;quot;);&lt;br /&gt;
		// Fermeture de la socket&lt;br /&gt;
		close(*clientSocket);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// Ajoute un descripteur de socket aux événements EPOLL&lt;br /&gt;
void addEpollEvent(int * epfd, int * socket) {&lt;br /&gt;
	printf(&amp;quot;Ajout de la socket %i dans les événements EPOLL\n&amp;quot;, *socket);&lt;br /&gt;
	// Structure contenant les événements EPOLL&lt;br /&gt;
	struct epoll_event event;&lt;br /&gt;
	// On enregistre la socket du serveur comme descripteur EPOLL&lt;br /&gt;
	event.data.fd = *socket;&lt;br /&gt;
	// On surveille les message entrant, la fermeture de la socket&lt;br /&gt;
	event.events = EPOLLIN | EPOLLRDHUP;&lt;br /&gt;
	epoll_ctl(*epfd, EPOLL_CTL_ADD, *socket, &amp;amp;event);&lt;br /&gt;
}&lt;br /&gt;
// Efface un descripteur de socket des événements EPOLL&lt;br /&gt;
void delEpollEvent(int * epfd, int * socket) {&lt;br /&gt;
	printf(&amp;quot;Suppression de la socket %i des événements EPOLL\n&amp;quot;, *socket);&lt;br /&gt;
	// Structure contenant les événements EPOLL&lt;br /&gt;
	struct epoll_event event;&lt;br /&gt;
	// On enregistre la socket du serveur comme descripteur EPOLL&lt;br /&gt;
	event.data.fd = *socket;&lt;br /&gt;
	// On surveille les message entrant, la fermeture de la socket&lt;br /&gt;
	event.events = EPOLLIN | EPOLLRDHUP;&lt;br /&gt;
	epoll_ctl(*epfd, EPOLL_CTL_DEL, *socket, &amp;amp;event);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Voici ce que l&#039;on peut observer dans la console:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Création de la socket&lt;br /&gt;
Paramètrage de la socket&lt;br /&gt;
Attachement de la socket sur le port 8080&lt;br /&gt;
Mise en écoute de la socket&lt;br /&gt;
Ajout de la socket 4 dans les événements EPOLL&lt;br /&gt;
Connexion de 127.0.0.1:17607&lt;br /&gt;
Ajout de la socket 5 dans les événements EPOLL&lt;br /&gt;
Suppression de la socket 5 des événements EPOLL&lt;br /&gt;
Fermeture de la connexion avec le client&lt;br /&gt;
Connexion de 127.0.0.1:18119&lt;br /&gt;
Ajout de la socket 5 dans les événements EPOLL&lt;br /&gt;
Connexion de 127.0.0.1:18631&lt;br /&gt;
Ajout de la socket 6 dans les événements EPOLL&lt;br /&gt;
Suppression de la socket 5 des événements EPOLL&lt;br /&gt;
Fermeture de la connexion avec le client&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Magsss</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=C_socket&amp;diff=3275</id>
		<title>C socket</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=C_socket&amp;diff=3275"/>
		<updated>2018-11-01T18:09:07Z</updated>

		<summary type="html">&lt;p&gt;Magsss : /* Côté serveur */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
||&lt;br /&gt;
Les sockets permettent de connecter deux programmes qui s&#039;exécutent sur deux machines différentes. Cette connexion se fait à travers le réseau grâce à l&#039;utilisation d&#039;un port (TCP ou UDP) et d&#039;une adresse IP. &lt;br /&gt;
&lt;br /&gt;
Il y a deux types de sockets, une qui est démarrée par la partie serveur en &#039;&#039;écoute&#039;&#039; et l&#039;autre démarrée par la partie cliente qui se connecte à la première.&lt;br /&gt;
&lt;br /&gt;
Ci-contre une image résumant les différentes étapes pour arriver à l&#039;envoi de données.&lt;br /&gt;
||&lt;br /&gt;
[[Fichier:Socket workflow.png|centré|200px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Création  =&lt;br /&gt;
== Côté serveur ==&lt;br /&gt;
Tout d&#039;abord il faut créer l&#039;objet socket:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt; &lt;br /&gt;
&lt;br /&gt;
int socket(int domain, int type, int protocol)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*domain &amp;amp;rarr; integer, communication domain e.g., AF_INET (IPv4 protocol) , AF_INET6 (IPv6 protocol)&lt;br /&gt;
*type &amp;amp;rarr; type de communication&lt;br /&gt;
**SOCK_STREAM: TCP&lt;br /&gt;
**SOCK_DGRAM: UDP&lt;br /&gt;
**SOCK_RAW: socket à l&#039;état brut (bas niveau)&lt;br /&gt;
*protocol &amp;amp;rarr; valeur du champ &#039;&#039;protocol&#039;&#039; de l&#039;en-tête de niveau 3 (généralement &#039;&#039;0&#039;&#039;)&lt;br /&gt;
*la valeur de retour est le fichier descripteur de la socket ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Une fois le descripteur de socket créé, il est possible de le configurer:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt; &lt;br /&gt;
&lt;br /&gt;
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* level &amp;amp;rarr; niveau d&#039;application de l&#039;option (SOL_SOCKET, TCP, UDP, ...);&lt;br /&gt;
* optname &amp;amp;rarr; le nom de l&#039;option (&#039;&#039;SO_REUSEADDR&#039;&#039;, &#039;&#039;SO_REUSEPORT&#039;&#039;, &#039;&#039;SO_KEEPALIVE&#039;&#039;, ...)&lt;br /&gt;
* optval, optlen  &amp;amp;rarr; utilisé pour accéder aux options de la socket;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
Il existe plusieurs options de socket, les plus utilisées étant :&lt;br /&gt;
* &#039;&#039;SO_REUSEADDR&#039;&#039; &amp;amp;rarr; permet de réutiliser l&#039;adresse de la socket tout de suite même si cette dernière est dans l&#039;état &#039;&#039;wait&#039;&#039;;&lt;br /&gt;
* &#039;&#039;SO_REUSEPORT&#039;&#039; &amp;amp;rarr; permet de réutiliser le port de la socket tout de suite même si cette dernière est dans l&#039;état &#039;&#039;wait&#039;&#039;;&lt;br /&gt;
* &#039;&#039;SO_KEEPALIVE&#039;&#039; &amp;amp;rarr; envoie des informations périodiquement pour tester si l&#039;extrémité du tunnel est toujours présente;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Il faut maintenant attacher la socket à un port Internet et une adresse IP:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* addr &amp;amp;rarr; une structure symbolisant l&#039;adresse &lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
struct sockaddr_in {&lt;br /&gt;
    short            sin_family;   // e.g: AF_INET&lt;br /&gt;
    unsigned short   sin_port;     // e.g: htons(3490)&lt;br /&gt;
    struct in_addr   sin_addr;     // détaillé ci-dessous&lt;br /&gt;
    char             sin_zero[8];  // &#039;0&#039; habituellement&lt;br /&gt;
};&lt;br /&gt;
struct in_addr {&lt;br /&gt;
    unsigned long s_addr;  // initialiser avec inet_aton()&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On peut maintenant passer la socket en état d&#039;écoute, elle est prête à recevoir des connexions:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int listen(int sockfd, int backlog)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* backlog &amp;amp;rarr; taille maximum de la file d&#039;attente de la socket après laquelle le système répond avec &#039;&#039;ECONNREFUSED&#039;&#039;;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On peut maintenant attendre une connexion:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* addr &amp;amp;rarr; l&#039;adresse du client;&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de la structure addr;&lt;br /&gt;
* le code retour varie entre un entier positif qui correspond au descripteur de la socket cliente et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
== Côté client ==&lt;br /&gt;
Après avoir créé la socket avec &#039;&#039;socket&#039;&#039; on peut se connecter à la partie serveur:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* addr &amp;amp;rarr; une structure symbolisant l&#039;adresse (cf. ci-dessus)&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
= Lecture / écriture =&lt;br /&gt;
== Fonctions génériques ==&lt;br /&gt;
Tout d&#039;abord, on peut dire que la socket est un fichier et se manipule donc comme un fichier !&lt;br /&gt;
===Lecture===&lt;br /&gt;
Pour lire dans une socket on peut utiliser &#039;&#039;read&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t read(int fd, void *buf, size_t count);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères ou mettre le message reçu;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à recevoir;&lt;br /&gt;
* le code retour correspond au nombre de caractères reçu ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
===Écriture===&lt;br /&gt;
Pour écrire dans une socket ou peut utiliser &#039;&#039;write&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t write(int fd, const void *buf, size_t count);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères contenant le message à envoyer;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à envoyer;&lt;br /&gt;
* le code retour correspond au nombre de caractères envoyer ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
===Modification===&lt;br /&gt;
Pour modifier un descripteur fichier de socket on utilise &#039;&#039;fcntl&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int fcntl(int fd, int cmd, ... /* int args */ );&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* cmd &amp;amp;rarr; la commande de modification (descripteur, drapeaux, ...);&lt;br /&gt;
* args &amp;amp;rarr; les arguments correspondants à la commande&lt;br /&gt;
&lt;br /&gt;
Cette commande va nous permettre de modifier le drapeau du descripteur de la socket (&#039;&#039;F_SETFL&#039;&#039;) pour le rendre non bloquant (&#039;&#039;O_NONBLOCK&#039;&#039;) !&lt;br /&gt;
&lt;br /&gt;
== Fonctions spécifiques ==&lt;br /&gt;
Il existe des fonctions spécifiques pour manipuler les sockets. Ces fonctions ressemble au précédentes mais acceptent des drapeaux en plus pour pouvoir modifier le descripteur &#039;&#039;à la volé&#039;&#039;.&lt;br /&gt;
===Lecture===&lt;br /&gt;
Pour lire dans une socket on peut utiliser &#039;&#039;recv&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t recv(int fd, void *buf, size_t count, int flags);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères ou mettre le message reçu;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à recevoir;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;O_NONBLOCK&#039;&#039;);&lt;br /&gt;
* le code retour correspond au nombre de caractères reçu ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
===Écriture===&lt;br /&gt;
Pour écrire dans une socket ou peut utiliser &#039;&#039;send&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t send(int fd, const void *buf, size_t count, int flags);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères contenant le message à envoyer;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à envoyer;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;MSG_CONFIRM&#039;&#039;, &#039;&#039;MSG_DONTWAIT &#039;&#039;, ...);&lt;br /&gt;
* le code retour correspond au nombre de caractères envoyer ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
= Cas d&#039;utilisation =&lt;br /&gt;
== Création ==&lt;br /&gt;
Pour commencer, il faut créer le descripteur de fichier:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int fdsocket;&lt;br /&gt;
if ((fdsocket = socket(AF_INET, SOCK_STREAM, 0)) == -1) {&lt;br /&gt;
	printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Paramétrage des options ==&lt;br /&gt;
Il faut maintenant configurer la réutilisation de l&#039;adresse et du port&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int opt = 1;&lt;br /&gt;
if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,	sizeof(opt)) != 0) {&lt;br /&gt;
	printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Attachement ==&lt;br /&gt;
On peut maintenant attacher la socket à un port et une adresse:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
struct sockaddr_in adresse;&lt;br /&gt;
&lt;br /&gt;
adresse.sin_family = AF_INET;&lt;br /&gt;
// Ecoute sur toutes les adresses (INADDR_ANY &amp;lt;=&amp;gt; 0.0.0.0)&lt;br /&gt;
adresse.sin_addr.s_addr = INADDR_ANY;&lt;br /&gt;
// Conversion du port en valeur réseaux (Host TO Network Short)&lt;br /&gt;
adresse.sin_port = htons(8080);&lt;br /&gt;
&lt;br /&gt;
if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
	printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mise en écoute ==&lt;br /&gt;
La socket est prête à passer à l&#039;écoute des connexions des clients:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
&lt;br /&gt;
if (listen(fdsocket, BACKLOG) != 0) {&lt;br /&gt;
	printf(&amp;quot;Echéc de la mise en écoute: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Acceptation des connexions ==&lt;br /&gt;
Il est possible d&#039;accepter les nouvelles connexions:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int clientSocket;&lt;br /&gt;
// Structure contenant l&#039;adresse du client&lt;br /&gt;
struct sockaddr_in clientAdresse;&lt;br /&gt;
int addrLen = sizeof(clientAdresse);&lt;br /&gt;
if ((clientSocket = accept(*serverSocket, (struct sockaddr*) &amp;amp;clientAdresse, (socklen_t*) &amp;amp;addrLen)) != -1) {&lt;br /&gt;
	// Convertion de l&#039;IP en texte&lt;br /&gt;
	char ip[INET_ADDRSTRLEN];&lt;br /&gt;
	inet_ntop(AF_INET, &amp;amp;(clientAdresse.sin_addr), ip, INET_ADDRSTRLEN);&lt;br /&gt;
	printf(&amp;quot;Connexion de %s:%i\n&amp;quot;, ip, clientAdresse.sin_port);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Il est possible d&#039;utiliser &#039;&#039;fcntl&#039;&#039; pour passer le descripteur de la socket en non bloquant, avant l&#039;appel à la fonction &#039;&#039;accept&#039;&#039;. Si le descripteur reste bloquant, la fonction &#039;&#039;accept&#039;&#039; ne rendra pas la main tant qu&#039;un client ne se sera connecté, ayant pour résultat le blocage complet du programme !&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Passage en mode non bloquant&lt;br /&gt;
fcntl(fdsocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
On peut remarquer, au passage, l&#039;utilisation de &#039;&#039;inet_ntop&#039;&#039; pour convertir l&#039;adresse du client du format binaire au format text.&lt;br /&gt;
&lt;br /&gt;
== Lecture / Écriture ==&lt;br /&gt;
=== Mode connecté (TCP) ===&lt;br /&gt;
On peut maintenant utiliser le descripteur de la socket du client pour lire et écrire.&lt;br /&gt;
* avec les fonctions génériques:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Descripteur de la socket du client&lt;br /&gt;
int clientSocket;&lt;br /&gt;
char buffer[BUFFER_LEN];&lt;br /&gt;
// Passage en mode non bloquant, sinon read attend&lt;br /&gt;
fcntl(clientSocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
int len = read(clientSocket, buffer, BUFFER_LEN);&lt;br /&gt;
&lt;br /&gt;
send(clientSocket, &amp;quot;Coucou\n&amp;quot;, strlen(&amp;quot;Coucou\n&amp;quot;));&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* avec les fonctions spécifiques:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Descripteur de la socket du client&lt;br /&gt;
int clientSocket;&lt;br /&gt;
char buffer[BUFFER_LEN];&lt;br /&gt;
&lt;br /&gt;
int len = recv(clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
&lt;br /&gt;
send(clientSocket, &amp;quot;Coucou\n&amp;quot;, strlen(&amp;quot;Coucou\n&amp;quot;), MSG_DONTWAIT);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Analysons le retour de la fonction &#039;&#039;read&#039;&#039; ou &#039;&#039;recv&#039;&#039; lorsque l&#039;on utilise un descripteur non bloquant d&#039;après la [http://man7.org/linux/man-pages/man2/recvmsg.2.html documentation] :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
NAME &lt;br /&gt;
       recv, recvfrom, recvmsg - receive a message from a socket&lt;br /&gt;
[...]&lt;br /&gt;
MSG_DONTWAIT (since Linux 2.2)&lt;br /&gt;
    Enables nonblocking operation; if the operation would block, the call fails with the error EAGAIN or EWOULDBLOCK (this can also be enabled using the O_NONBLOCK flag with the F_SETFL fcntl(2)). &lt;br /&gt;
[...]&lt;br /&gt;
RETURN VALUE&lt;br /&gt;
[...]&lt;br /&gt;
These calls return the number of bytes received, or -1 if an error&lt;br /&gt;
       occurred.  In the event of an error, errno is set to indicate the&lt;br /&gt;
       error.&lt;br /&gt;
When a stream socket peer has performed an orderly shutdown, the&lt;br /&gt;
       return value will be 0 (the traditional &amp;quot;end-of-file&amp;quot; return).&lt;br /&gt;
[...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Le retour est :&lt;br /&gt;
* lorsque le descripteur est vide (aucune donnée de la part du client) &#039;&#039;-1&#039;&#039; et &#039;&#039;errno&#039;&#039; sera positionné à la valeur &#039;&#039;EAGAIN&#039;&#039;, il ne faut donc pas fermer la socket si le retour est &#039;&#039;-1&#039;&#039; ! Il faut d&#039;abord vérifier que &#039;&#039;errno&#039;&#039; &#039;&#039;&#039;n&#039;a pas&#039;&#039;&#039; la valeur &#039;&#039;EAGAIN&#039;&#039;;&lt;br /&gt;
* lorsque le retour est &#039;&#039;0&#039;&#039;, cela signifie que l&#039;extrémité est fermée, il faut donc fermer le descripteur local;&lt;br /&gt;
* lorsque la valeur est supérieur à &#039;&#039;0&#039;&#039; elle correspond au nombre d&#039;octets reçu.&lt;br /&gt;
&lt;br /&gt;
On peut donc en déduire le morceau de code suivant pour gérer le retour de &#039;&#039;read&#039;&#039; ou &#039;&#039;recv&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int len = recv(clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
&lt;br /&gt;
if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
	// Une erreur est survenue&lt;br /&gt;
} else if (len == 0) {&lt;br /&gt;
	// Le client s&#039;est déconnecté (extrémité de la socket fermée)			&lt;br /&gt;
} else if (len &amp;gt; 0) {&lt;br /&gt;
	// Le client à envoyé des données	&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Mode datagramme (UDP) ===&lt;br /&gt;
===Lecture===&lt;br /&gt;
Pour lire dans une socket on peut utiliser &#039;&#039;recvfrom&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t recvfrom(int fd, void *buf, size_t count, int flags, const struct sockaddr *addr, socklen_t addrlen);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères ou mettre le message reçu;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à recevoir;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;O_NONBLOCK&#039;&#039;);&lt;br /&gt;
*  addr &amp;amp;rarr; une structure symbolisant l&#039;adresse du client (cf. ci-dessus)&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour correspond au nombre de caractères reçu ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
===Écriture===&lt;br /&gt;
Pour écrire dans une socket ou peut utiliser &#039;&#039;sendto&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t sendto(int fd, const void *buf, size_t count, const struct sockaddr *addr, socklen_t addrlen);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères contenant le message à envoyer;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à envoyer;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;MSG_DONTWAIT &#039;&#039;, ...);&lt;br /&gt;
* addr &amp;amp;rarr; une structure symbolisant l&#039;adresse du client (cf. ci-dessus)&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour correspond au nombre de caractères envoyer ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
= Exemples =&lt;br /&gt;
== Serveur TCP ==&lt;br /&gt;
Voici un exemple de serveur &#039;&#039;echo&#039;&#039; qui renvoie le message au client. Les connexions utilisent TCP et un simple client comme telnet suffit pour l&#039;utiliser.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;arpa/inet.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Port d&#039;écoute de la socket&lt;br /&gt;
#define PORT 8080&lt;br /&gt;
// Adresse d&#039;écoute (toutes les adresses)&lt;br /&gt;
#define IP INADDR_ANY&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
// Nombre de connexions clients&lt;br /&gt;
#define NB_CLIENTS 2&lt;br /&gt;
// Taille du tampon de lecture des messages&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Commande pour arrêter le serveur&lt;br /&gt;
#define EXIT_WORD &amp;quot;exit&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse);&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse);&lt;br /&gt;
int waitForClient(int * serverSocket);&lt;br /&gt;
void addClientToTab(int clientSocket, int clients[]);&lt;br /&gt;
void manageClient(int clients[]);&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
	// Création et initialisation du tableau contenant les descripteurs des sockets clients&lt;br /&gt;
	int clients[NB_CLIENTS];&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_CLIENTS; i++) {&lt;br /&gt;
		clients[i] = -1;&lt;br /&gt;
	}&lt;br /&gt;
	// Structure contenant l&#039;adresse&lt;br /&gt;
	struct sockaddr_in adresse;&lt;br /&gt;
	initAdresse(&amp;amp;adresse);&lt;br /&gt;
	// Descripteur de la socket du serveur&lt;br /&gt;
	int serverSocket = initSocket(&amp;amp;adresse);&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	while (1) {&lt;br /&gt;
		// Descripteur de la socket du client, on attend une connexion&lt;br /&gt;
		if ((clientSocket = waitForClient(&amp;amp;serverSocket)) != -1) {&lt;br /&gt;
			// On ajoute le nouveau client au tableau des descripteurs&lt;br /&gt;
			addClientToTab(clientSocket, clients);&lt;br /&gt;
		}&lt;br /&gt;
		manageClient(clients);&lt;br /&gt;
	}&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
// Initialisation de la structure sockaddr_in&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse) {&lt;br /&gt;
	(*adresse).sin_family = AF_INET;&lt;br /&gt;
	(*adresse).sin_addr.s_addr = IP;&lt;br /&gt;
	(*adresse).sin_port = htons( PORT);&lt;br /&gt;
}&lt;br /&gt;
// Démarrage de la socket serveur&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse) {&lt;br /&gt;
	// Descripteur de socket&lt;br /&gt;
	int fdsocket;&lt;br /&gt;
	// Nombre d&#039;options&lt;br /&gt;
	int opt = 1;&lt;br /&gt;
	// Création de la socket en TCP&lt;br /&gt;
	if ((fdsocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Création de la socket\n&amp;quot;);&lt;br /&gt;
	// Paramètrage de la socket&lt;br /&gt;
	if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,&lt;br /&gt;
			sizeof(opt)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Paramètrage de la socket\n&amp;quot;);&lt;br /&gt;
	// Attachement de la socket sur le port et l&#039;adresse IP&lt;br /&gt;
	if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Attachement de la socket sur le port %i\n&amp;quot;, PORT);&lt;br /&gt;
	// Passage en écoute de la socket&lt;br /&gt;
	if (listen(fdsocket, BACKLOG) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la mise en écoute: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	// Passage en mode non bloquant&lt;br /&gt;
	fcntl(fdsocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
	printf(&amp;quot;Mise en écoute de la socket\n&amp;quot;);&lt;br /&gt;
	return fdsocket;&lt;br /&gt;
}&lt;br /&gt;
// Attente de connexion d&#039;un client&lt;br /&gt;
int waitForClient(int * serverSocket) {&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	// Structure contenant l&#039;adresse du client&lt;br /&gt;
	struct sockaddr_in clientAdresse;&lt;br /&gt;
	int addrLen = sizeof(clientAdresse);&lt;br /&gt;
	if ((clientSocket = accept(*serverSocket, (struct sockaddr*) &amp;amp;clientAdresse,&lt;br /&gt;
			(socklen_t*) &amp;amp;addrLen)) != -1) {&lt;br /&gt;
		// Convertion de l&#039;IP en texte&lt;br /&gt;
		char ip[INET_ADDRSTRLEN];&lt;br /&gt;
		inet_ntop(AF_INET, &amp;amp;(clientAdresse.sin_addr), ip, INET_ADDRSTRLEN);&lt;br /&gt;
		printf(&amp;quot;Connexion de %s:%i\n&amp;quot;, ip, clientAdresse.sin_port);&lt;br /&gt;
		// Passage en mode non bloquant&lt;br /&gt;
		int opt = 1;&lt;br /&gt;
		setsockopt(clientSocket, SOL_SOCKET, SO_KEEPALIVE, &amp;amp;opt, sizeof(1));&lt;br /&gt;
	}&lt;br /&gt;
	return clientSocket;&lt;br /&gt;
}&lt;br /&gt;
// Ajoute les clients au tableau&lt;br /&gt;
void addClientToTab(int clientSocket, int clients[]) {&lt;br /&gt;
	// On vérifie si on à de la place de libre&lt;br /&gt;
	int found = -1;&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_CLIENTS; i++) {&lt;br /&gt;
		// On cherche de la place&lt;br /&gt;
		if (clients[i] == -1) {&lt;br /&gt;
			// On ajoute le client&lt;br /&gt;
			printf(&amp;quot;Ajout du client à l&#039;index %i\n&amp;quot;, i);&lt;br /&gt;
			clients[i] = clientSocket;&lt;br /&gt;
			// Nouvelle connexion, envoie du message de bienvenu&lt;br /&gt;
			send(clientSocket, &amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;, 	strlen(&amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;),&lt;br /&gt;
					MSG_DONTWAIT);&lt;br /&gt;
			found = 0;&lt;br /&gt;
			break;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	if (found == -1) {&lt;br /&gt;
		// On a plus de place de libre&lt;br /&gt;
		puts(&amp;quot;Plus de place, désolé&amp;quot;);&lt;br /&gt;
		close(clientSocket);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// On traite l&#039;input des clients&lt;br /&gt;
void manageClient(int clients[]) {&lt;br /&gt;
	// Création d&#039;un tampon pour stocker les messages des clients dans la heap&lt;br /&gt;
	static char buffer[BUFFER_LEN + 1];&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_CLIENTS; i++) {&lt;br /&gt;
		// On vérifie les messages&lt;br /&gt;
		int clientSocket = clients[i];&lt;br /&gt;
		if (clientSocket == -1) {&lt;br /&gt;
			continue;&lt;br /&gt;
		}&lt;br /&gt;
		int len = recv(clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
		// Booléen pour suivre l&#039;état de la socket&lt;br /&gt;
		int isClosed = 0;&lt;br /&gt;
		if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
			// Une erreur est survenue&lt;br /&gt;
			printf(&amp;quot;Errno [%i] : %s\n&amp;quot;, errno, strerror(errno));&lt;br /&gt;
			isClosed = 1;&lt;br /&gt;
		} else if (len == 0) {&lt;br /&gt;
			// Le client s&#039;est déconnecté (extrémité de la socket fermée)&lt;br /&gt;
			isClosed = 1;&lt;br /&gt;
		} else if (len &amp;gt; 0) {&lt;br /&gt;
			// Ajout du terminateur de chaîne&lt;br /&gt;
			buffer[len] = &#039;\0&#039;;&lt;br /&gt;
			if (strncmp(buffer, EXIT_WORD, 4) == 0) {&lt;br /&gt;
				// Le client veut se déconnecter&lt;br /&gt;
				send(clientSocket, &amp;quot;Bye\n&amp;quot;, strlen(&amp;quot;Bye\n&amp;quot;), 0);&lt;br /&gt;
				isClosed = 1;&lt;br /&gt;
			} else {&lt;br /&gt;
				// On renvoie le texte au client dans un buffer assez grand&lt;br /&gt;
				int len = strlen(&amp;quot;Vous avez dit : &amp;quot;) + strlen(buffer) + 1;&lt;br /&gt;
				char response[len];&lt;br /&gt;
				strcpy(response, &amp;quot;Vous avez dit : &amp;quot;);&lt;br /&gt;
				strcat(response, buffer);&lt;br /&gt;
				// Un seul envoie permet de ne pas surcharger le réseau&lt;br /&gt;
				send(clientSocket, response, strlen(response), 0);&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
		if (isClosed == 1) {&lt;br /&gt;
			// La socket est fermé ou le client veut quitter le serveur !&lt;br /&gt;
			printf(&amp;quot;Fermeture de la connexion avec le client\n&amp;quot;);&lt;br /&gt;
			// Fermeture de la socket&lt;br /&gt;
			close(clientSocket);&lt;br /&gt;
			// On fait de la place dans le tableau&lt;br /&gt;
			clients[i] = -1;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On peut interroger notre serveur avec &#039;&#039;telnet&#039;&#039; (&#039;&#039;yum -y install telnet&#039;&#039;):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# telnet 127.0.0.1 8080&lt;br /&gt;
Trying 127.0.0.1...&lt;br /&gt;
Connected to 127.0.0.1.&lt;br /&gt;
Escape character is &#039;^]&#039;.&lt;br /&gt;
Entrez &#039;exit&#039; pour quitter&lt;br /&gt;
coucou&lt;br /&gt;
Vous avez dit : coucou&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== Serveur UDP ==&lt;br /&gt;
Si on essaye de reproduire le même type de serveur mais cette fois-ci avec une socket en UDP, cela devient beaucoup plus simple. En effet, en UDP, plus besoin de tenir un tableau avec les descripteurs des sockets des clients car... on est en mode déconnecté (datagramme) ! Entendez par la que les clients envoie un message sans attendre de réponse et sans se soucier de savoir si le message est arrivé au serveur.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;arpa/inet.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Port d&#039;écoute de la socket&lt;br /&gt;
#define PORT 8080&lt;br /&gt;
// Adresse d&#039;écoute (toutes les adresses)&lt;br /&gt;
#define IP INADDR_ANY&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
// Taille du tampon de lecture des messages&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse);&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse);&lt;br /&gt;
void manageClient(int serverSocket);&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
	// Structure contenant l&#039;adresse&lt;br /&gt;
	struct sockaddr_in adresse;&lt;br /&gt;
	initAdresse(&amp;amp;adresse);&lt;br /&gt;
	// Descripteur de la socket du serveur&lt;br /&gt;
	int serverSocket = initSocket(&amp;amp;adresse);&lt;br /&gt;
	while (1) {&lt;br /&gt;
		// Descripteur de la socket du client, on attend une connexion&lt;br /&gt;
		manageClient(serverSocket);&lt;br /&gt;
	}&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
// Initialisation de la structure sockaddr_in&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse) {&lt;br /&gt;
	(*adresse).sin_family = AF_INET;&lt;br /&gt;
	(*adresse).sin_addr.s_addr = IP;&lt;br /&gt;
	(*adresse).sin_port = htons( PORT);&lt;br /&gt;
}&lt;br /&gt;
// Démarrage de la socket serveur&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse) {&lt;br /&gt;
	// Descripteur de socket&lt;br /&gt;
	int fdsocket;&lt;br /&gt;
	// Nombre d&#039;options&lt;br /&gt;
	int opt = 1;&lt;br /&gt;
	// Création de la socket en UDP&lt;br /&gt;
	if ((fdsocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Création de la socket\n&amp;quot;);&lt;br /&gt;
	// Paramètrage de la socket&lt;br /&gt;
	if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,&lt;br /&gt;
			sizeof(opt)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Paramètrage de la socket\n&amp;quot;);&lt;br /&gt;
	// Attachement de la socket sur le port et l&#039;adresse IP&lt;br /&gt;
	if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Attachement de la socket sur le port %i\n&amp;quot;, PORT);&lt;br /&gt;
	return fdsocket;&lt;br /&gt;
}&lt;br /&gt;
// On traite l&#039;input des clients&lt;br /&gt;
void manageClient(int serverSocket) {&lt;br /&gt;
	// Création d&#039;un tampon pour stocker les messages des clients dans la heap&lt;br /&gt;
	static char buffer[BUFFER_LEN + 1];&lt;br /&gt;
	// Structure contenant l&#039;adresse du client&lt;br /&gt;
	struct sockaddr_in clientAdresse;&lt;br /&gt;
	unsigned int addrLen = sizeof(clientAdresse);&lt;br /&gt;
	// On vérifie si on a reçu un message&lt;br /&gt;
	int len = recvfrom(serverSocket, buffer, BUFFER_LEN, MSG_DONTWAIT,&lt;br /&gt;
			(struct sockaddr*) &amp;amp;clientAdresse, &amp;amp;addrLen);&lt;br /&gt;
	if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
		printf(&amp;quot;Problème de socket %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	} else if (len &amp;gt; 0) {&lt;br /&gt;
		printf(&amp;quot;Message reçu de %s:%i\n&amp;quot;, inet_ntoa(clientAdresse.sin_addr),&lt;br /&gt;
				ntohs(clientAdresse.sin_port));&lt;br /&gt;
		// Ajout du terminateur de chaîne&lt;br /&gt;
		buffer[len] = &#039;\0&#039;;&lt;br /&gt;
		// On renvoie le texte au client dans un buffer assez grand&lt;br /&gt;
		len = strlen(&amp;quot;Vous avez dit : &amp;quot;) + strlen(buffer) + 1;&lt;br /&gt;
		char response[len];&lt;br /&gt;
		strcpy(response, &amp;quot;Vous avez dit : &amp;quot;);&lt;br /&gt;
		strcat(response, buffer);&lt;br /&gt;
		// Un seul envoie permet de ne pas surcharger le réseau&lt;br /&gt;
		sendto(serverSocket, response, strlen(response), MSG_DONTWAIT,&lt;br /&gt;
				(struct sockaddr*) &amp;amp;clientAdresse, addrLen);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
On peut interroger notre serveur avec &#039;&#039;netcat&#039;&#039; (&#039;&#039;yum -y install nc&#039;&#039;):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# nc -u 127.0.0.1 8080&lt;br /&gt;
coucou&lt;br /&gt;
Vous avez dit : coucou&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
= Notification évènementielle avec epoll =&lt;br /&gt;
Depuis la version 2.5 du noyau Linux il est possible d&#039;utiliser une série de fonctions, appartenant à l&#039;espace utilisateur (user-space), permettant de suivre l&#039;état d&#039;un descripteur de fichier.&lt;br /&gt;
La différence avec read, recv ou encore recvfrom c&#039;est que l&#039;on va pouvoir surveiller plusieurs descripteurs de fichiers en même temps, laissant le CPU libre pour effectuer autre chose !&lt;br /&gt;
&lt;br /&gt;
== Utilisation ==&lt;br /&gt;
Tout d&#039;abord il faut créer un descripteur de fichier de type &#039;&#039;epoll&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int epoll_create1(int flags);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* flags &amp;amp;rarr; EPOLL_CLOEXEC pour éviter les situation de compétition en environnement parallélisé;&lt;br /&gt;
* le code retour correspond au descripteur &#039;&#039;epoll&#039;&#039; ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée).&lt;br /&gt;
&lt;br /&gt;
Une fois le descripteur &#039;&#039;epoll&#039;&#039; créé, il faut lui associer un événement:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* epfd &amp;amp;rarr; le descripteur &#039;&#039;epoll&#039;&#039; à modifier;&lt;br /&gt;
* op &amp;amp;rarr; une opération parmis:&lt;br /&gt;
** EPOLL_CTL_ADD &amp;amp;rarr; pour ajouter un événement sur un descripteur de fichier;&lt;br /&gt;
** EPOLL_CTL_MOD &amp;amp;rarr; pour modifier un événement sur un descripteur de fichier;&lt;br /&gt;
** EPOLL_CTL_DEL &amp;amp;rarr; pour supprimer un événement sur un descripteur de fichier;&lt;br /&gt;
* fd &amp;amp;rarr; un descripteur de fichier à surveiller;&lt;br /&gt;
* event &amp;amp;rarr; la structure contenant l&#039;événement à surveiller (&#039;&#039;EPOLLIN&#039;&#039;, &#039;&#039;EPOLLOUT&#039;&#039;, EPOLLRDHUP&#039;&#039;, ...);&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; en cas de succès ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée).&lt;br /&gt;
&lt;br /&gt;
Voici la structure &#039;&#039;epoll_event&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
struct epoll_event {&lt;br /&gt;
	uint32_t events;    /* Epoll events */&lt;br /&gt;
	epoll_data_t data;  /* User data variable */&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
typedef union epoll_data {&lt;br /&gt;
	void    *ptr;&lt;br /&gt;
	int      fd;&lt;br /&gt;
	uint32_t u32;&lt;br /&gt;
	uint64_t u64;&lt;br /&gt;
} epoll_data_t;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Enfin, il ne reste plus qu&#039;à &#039;&#039;attendre&#039;&#039; des événements:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* epfd &amp;amp;rarr; le descripteur &#039;&#039;epoll&#039;&#039; à modifier;&lt;br /&gt;
* events &amp;amp;rarr; un tableau de &#039;&#039;epoll_event contenant les événements;&lt;br /&gt;
* maxevents &amp;amp;rarr; le nombre maximum d&#039;événements à traiter (selon la taille du tableau);&lt;br /&gt;
* timeout &amp;amp;rarr; le temps, en millisecondes que la fonction attend:&lt;br /&gt;
** &#039;&#039;-1&#039;&#039; &amp;amp;rarr; la fonction devient bloquante;&lt;br /&gt;
** &#039;&#039;0&#039;&#039; &amp;amp;rarr; retourne immédiatement;&lt;br /&gt;
** &#039;&#039;&amp;gt; 0&#039;&#039; &amp;amp;rarr; attend le nombre de millisecondes spécifiées;&lt;br /&gt;
* le code retour correspond au nombre d&#039;événements ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée).&lt;br /&gt;
&lt;br /&gt;
== Améliorations architecturales ==&lt;br /&gt;
=== Gestion des utilisateurs===&lt;br /&gt;
Tout d&#039;abord, le faite de laisser &#039;&#039;epoll&#039;&#039; gérer les descripteurs de socket nous permet de supprimer le tableau &#039;&#039;clients&#039;&#039; ainsi que la variable &#039;&#039;NB_CLIENTS&#039;&#039;. En effet, la référence vers le descripteur est maintenu par &#039;&#039;epoll&#039;&#039; et nous pouvons gérer maintenant un nombre &#039;&#039;illimité&#039;&#039; de clients.&lt;br /&gt;
=== Performances ===&lt;br /&gt;
La boucle &#039;&#039;while&#039;&#039; de notre serveur consomme énormément de CPU, environ 100% de CPU par client ! On aurais pu utiliser un temporisateur avec &#039;&#039;nanosleep&#039;&#039; pour baisser la consommation CPU entre 5% et 10%...&lt;br /&gt;
Le faîte de basculer sur une fonction bloquante comme &#039;&#039;epoll_wait&#039;&#039; qui utilise des signaux (interruptions) permet de réduire considérablement la consommation CPU. Gardez à l&#039;esprit qu&#039;il n&#039;y a rien de mieux que les interruptions, la preuve, lorsque l&#039;on fait un &#039;&#039;top&#039;&#039;, notre programme à complétement disparu de la liste !&lt;br /&gt;
&lt;br /&gt;
== Modification du serveur ==&lt;br /&gt;
Modifions le code du serveur TCP pour y intégrer &#039;&#039;epoll&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;arpa/inet.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Port d&#039;écoute de la socket&lt;br /&gt;
#define PORT 8080&lt;br /&gt;
// Adresse d&#039;écoute (toutes les adresses)&lt;br /&gt;
#define IP INADDR_ANY&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
// Nombre de connexions clients&lt;br /&gt;
#define NB_EVENTS 2&lt;br /&gt;
// Taille du tampon de lecture des messages&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Commande pour arrêter le serveur&lt;br /&gt;
#define EXIT_WORD &amp;quot;exit&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse);&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse);&lt;br /&gt;
int waitForClient(int * serverSocket);&lt;br /&gt;
void addClientToTab(int * epfd, int * clientSocket);&lt;br /&gt;
void manageClient(int * epfd, int * clientSocket);&lt;br /&gt;
void addEpollEvent(int * epfd, int * socket);&lt;br /&gt;
void delEpollEvent(int * epfd, int * socket);&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
	// Création du descripteur EPOLL&lt;br /&gt;
	int epfd = epoll_create1(0);&lt;br /&gt;
	// Structure contenant les événements EPOLL&lt;br /&gt;
	struct epoll_event events[NB_EVENTS];&lt;br /&gt;
	// Structure contenant l&#039;adresse&lt;br /&gt;
	struct sockaddr_in adresse;&lt;br /&gt;
	initAdresse(&amp;amp;adresse);&lt;br /&gt;
	// Descripteur de la socket du serveur&lt;br /&gt;
	int serverSocket = initSocket(&amp;amp;adresse);&lt;br /&gt;
	addEpollEvent(&amp;amp;epfd, &amp;amp;serverSocket);&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	while (1) {&lt;br /&gt;
		int nfds = epoll_wait(epfd, events, NB_EVENTS, -1);&lt;br /&gt;
		for (int i = 0; i &amp;lt; nfds; i++) {&lt;br /&gt;
			if (events[i].data.fd == serverSocket) {&lt;br /&gt;
				if ((clientSocket = waitForClient(&amp;amp;serverSocket)) != -1) {&lt;br /&gt;
					// On ajoute le nouveau client au tableau des descripteurs&lt;br /&gt;
					addClientToTab(&amp;amp;epfd, &amp;amp;clientSocket);&lt;br /&gt;
				}&lt;br /&gt;
			} else if (events[i].events &amp;amp; EPOLLIN) {&lt;br /&gt;
				// On a reçu quelque chose&lt;br /&gt;
				manageClient(&amp;amp;epfd, &amp;amp;events[i].data.fd);&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	close(serverSocket);&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
// Initialisation de la structure sockaddr_in&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse) {&lt;br /&gt;
	(*adresse).sin_family = AF_INET;&lt;br /&gt;
	(*adresse).sin_addr.s_addr = IP;&lt;br /&gt;
	(*adresse).sin_port = htons( PORT);&lt;br /&gt;
}&lt;br /&gt;
// Démarrage de la socket serveur&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse) {&lt;br /&gt;
	// Descripteur de socket&lt;br /&gt;
	int fdsocket;&lt;br /&gt;
	// Nombre d&#039;options&lt;br /&gt;
	int opt = 1;&lt;br /&gt;
	// Création de la socket en TCP&lt;br /&gt;
	if ((fdsocket = socket(AF_INET, SOCK_STREAM, 0)) == -1) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Création de la socket\n&amp;quot;);&lt;br /&gt;
	// Paramètrage de la socket&lt;br /&gt;
	if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,&lt;br /&gt;
			sizeof(opt)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Paramètrage de la socket\n&amp;quot;);&lt;br /&gt;
	// Attachement de la socket sur le port et l&#039;adresse IP&lt;br /&gt;
	if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Attachement de la socket sur le port %i\n&amp;quot;, PORT);&lt;br /&gt;
	// Passage en écoute de la socket&lt;br /&gt;
	if (listen(fdsocket, BACKLOG) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la mise en écoute: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	// Passage en mode non bloquant&lt;br /&gt;
	fcntl(fdsocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
	printf(&amp;quot;Mise en écoute de la socket\n&amp;quot;);&lt;br /&gt;
	return fdsocket;&lt;br /&gt;
}&lt;br /&gt;
// Attente de connexion d&#039;un client&lt;br /&gt;
int waitForClient(int * serverSocket) {&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	// Structure contenant l&#039;adresse du client&lt;br /&gt;
	struct sockaddr_in clientAdresse;&lt;br /&gt;
	int addrLen = sizeof(clientAdresse);&lt;br /&gt;
	if ((clientSocket = accept(*serverSocket, (struct sockaddr*) &amp;amp;clientAdresse,&lt;br /&gt;
			(socklen_t*) &amp;amp;addrLen)) != -1) {&lt;br /&gt;
		// Convertion de l&#039;IP en texte&lt;br /&gt;
		char ip[INET_ADDRSTRLEN];&lt;br /&gt;
		inet_ntop(AF_INET, &amp;amp;(clientAdresse.sin_addr), ip, INET_ADDRSTRLEN);&lt;br /&gt;
		printf(&amp;quot;Connexion de %s:%i\n&amp;quot;, ip, clientAdresse.sin_port);&lt;br /&gt;
		// Passage en mode non bloquant&lt;br /&gt;
		int opt = 1;&lt;br /&gt;
		setsockopt(clientSocket, SOL_SOCKET, SO_KEEPALIVE, &amp;amp;opt, sizeof(1));&lt;br /&gt;
	}&lt;br /&gt;
	return clientSocket;&lt;br /&gt;
}&lt;br /&gt;
// Ajoute les clients au tableau&lt;br /&gt;
void addClientToTab(int * epfd, int * clientSocket) {&lt;br /&gt;
	addEpollEvent(epfd, clientSocket);&lt;br /&gt;
	// Nouvelle connexion, envoie du message de bienvenu&lt;br /&gt;
	send(*clientSocket, &amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;,&lt;br /&gt;
			strlen(&amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;), MSG_DONTWAIT);&lt;br /&gt;
}&lt;br /&gt;
// On traite l&#039;input des clients&lt;br /&gt;
void manageClient(int * epfd, int * clientSocket) {&lt;br /&gt;
	// Création d&#039;un tampon pour stocker les messages des clients dans la heap&lt;br /&gt;
	static char buffer[BUFFER_LEN + 1];&lt;br /&gt;
	int len = recv(*clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
	// Booléen pour suivre l&#039;état de la socket&lt;br /&gt;
	int isClosed = 0;&lt;br /&gt;
	if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
		// Une erreur est survenue&lt;br /&gt;
		printf(&amp;quot;Errno [%i] : %s\n&amp;quot;, errno, strerror(errno));&lt;br /&gt;
		isClosed = 1;&lt;br /&gt;
	} else if (len == 0) {&lt;br /&gt;
		// Le client s&#039;est déconnecté (extrémité de la socket fermée)&lt;br /&gt;
		isClosed = 1;&lt;br /&gt;
	} else if (len &amp;gt; 0) {&lt;br /&gt;
		// Ajout du terminateur de chaîne&lt;br /&gt;
		buffer[len] = &#039;\0&#039;;&lt;br /&gt;
		if (strncmp(buffer, EXIT_WORD, 4) == 0) {&lt;br /&gt;
			// Le client veut se déconnecter&lt;br /&gt;
			send(*clientSocket, &amp;quot;Bye\n&amp;quot;, strlen(&amp;quot;Bye\n&amp;quot;), 0);&lt;br /&gt;
			isClosed = 1;&lt;br /&gt;
		} else {&lt;br /&gt;
			// On renvoie le texte au client dans un buffer assez grand&lt;br /&gt;
			int len = strlen(&amp;quot;Vous avez dit : &amp;quot;) + strlen(buffer) + 1;&lt;br /&gt;
			char response[len];&lt;br /&gt;
			strcpy(response, &amp;quot;Vous avez dit : &amp;quot;);&lt;br /&gt;
			strcat(response, buffer);&lt;br /&gt;
			// Un seul envoie permet de ne pas surcharger le réseau&lt;br /&gt;
			send(*clientSocket, response, strlen(response), 0);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	if (isClosed == 1) {&lt;br /&gt;
		// On enlève la socket des événements EPOLL&lt;br /&gt;
		delEpollEvent(epfd, clientSocket);&lt;br /&gt;
		// La socket est fermé ou le client veut quitter le serveur !&lt;br /&gt;
		printf(&amp;quot;Fermeture de la connexion avec le client\n&amp;quot;);&lt;br /&gt;
		// Fermeture de la socket&lt;br /&gt;
		close(*clientSocket);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// Ajoute un descripteur de socket aux événements EPOLL&lt;br /&gt;
void addEpollEvent(int * epfd, int * socket) {&lt;br /&gt;
	printf(&amp;quot;Ajout de la socket %i dans les événements EPOLL\n&amp;quot;, *socket);&lt;br /&gt;
	// Structure contenant les événements EPOLL&lt;br /&gt;
	struct epoll_event event;&lt;br /&gt;
	// On enregistre la socket du serveur comme descripteur EPOLL&lt;br /&gt;
	event.data.fd = *socket;&lt;br /&gt;
	// On surveille les message entrant, la fermeture de la socket&lt;br /&gt;
	event.events = EPOLLIN | EPOLLRDHUP;&lt;br /&gt;
	epoll_ctl(*epfd, EPOLL_CTL_ADD, *socket, &amp;amp;event);&lt;br /&gt;
}&lt;br /&gt;
// Efface un descripteur de socket des événements EPOLL&lt;br /&gt;
void delEpollEvent(int * epfd, int * socket) {&lt;br /&gt;
	printf(&amp;quot;Suppression de la socket %i des événements EPOLL\n&amp;quot;, *socket);&lt;br /&gt;
	// Structure contenant les événements EPOLL&lt;br /&gt;
	struct epoll_event event;&lt;br /&gt;
	// On enregistre la socket du serveur comme descripteur EPOLL&lt;br /&gt;
	event.data.fd = *socket;&lt;br /&gt;
	// On surveille les message entrant, la fermeture de la socket&lt;br /&gt;
	event.events = EPOLLIN | EPOLLRDHUP;&lt;br /&gt;
	epoll_ctl(*epfd, EPOLL_CTL_DEL, *socket, &amp;amp;event);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Voici ce que l&#039;on peut observer dans la console:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Création de la socket&lt;br /&gt;
Paramètrage de la socket&lt;br /&gt;
Attachement de la socket sur le port 8080&lt;br /&gt;
Mise en écoute de la socket&lt;br /&gt;
Ajout de la socket 4 dans les événements EPOLL&lt;br /&gt;
Connexion de 127.0.0.1:17607&lt;br /&gt;
Ajout de la socket 5 dans les événements EPOLL&lt;br /&gt;
Suppression de la socket 5 des événements EPOLL&lt;br /&gt;
Fermeture de la connexion avec le client&lt;br /&gt;
Connexion de 127.0.0.1:18119&lt;br /&gt;
Ajout de la socket 5 dans les événements EPOLL&lt;br /&gt;
Connexion de 127.0.0.1:18631&lt;br /&gt;
Ajout de la socket 6 dans les événements EPOLL&lt;br /&gt;
Suppression de la socket 5 des événements EPOLL&lt;br /&gt;
Fermeture de la connexion avec le client&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Magsss</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=C_socket&amp;diff=3274</id>
		<title>C socket</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=C_socket&amp;diff=3274"/>
		<updated>2018-11-01T18:08:28Z</updated>

		<summary type="html">&lt;p&gt;Magsss : /* Introduction */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
{|&lt;br /&gt;
|-&lt;br /&gt;
||&lt;br /&gt;
Les sockets permettent de connecter deux programmes qui s&#039;exécutent sur deux machines différentes. Cette connexion se fait à travers le réseau grâce à l&#039;utilisation d&#039;un port (TCP ou UDP) et d&#039;une adresse IP. &lt;br /&gt;
&lt;br /&gt;
Il y a deux types de sockets, une qui est démarrée par la partie serveur en &#039;&#039;écoute&#039;&#039; et l&#039;autre démarrée par la partie cliente qui se connecte à la première.&lt;br /&gt;
&lt;br /&gt;
Ci-contre une image résumant les différentes étapes pour arriver à l&#039;envoi de données.&lt;br /&gt;
||&lt;br /&gt;
[[Fichier:Socket workflow.png|centré|200px]]&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
= Création  =&lt;br /&gt;
== Côté serveur ==&lt;br /&gt;
Tout d&#039;abord il faut créer l&#039;objet socket:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt; &lt;br /&gt;
&lt;br /&gt;
int socket(int domain, int type, int protocol)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*domain &amp;amp;rarr; integer, communication domain e.g., AF_INET (IPv4 protocol) , AF_INET6 (IPv6 protocol)&lt;br /&gt;
*type &amp;amp;rarr; type de communication&lt;br /&gt;
**SOCK_STREAM: TCP&lt;br /&gt;
**SOCK_DGRAM: UDP&lt;br /&gt;
**SOCK_RAW: socket à l&#039;état brut (bas niveau)&lt;br /&gt;
*protocol &amp;amp;rarr; valeur du champ &#039;&#039;protocol&#039;&#039; de l&#039;entête de niveau 3 (généralement &#039;&#039;0&#039;&#039;)&lt;br /&gt;
*la valeur de retour est le fichier descripteur de la socket ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Une fois le descripteur de socket créé, il est possible de le configurer:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt; &lt;br /&gt;
&lt;br /&gt;
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* level &amp;amp;rarr; niveau d&#039;application de l&#039;option (SOL_SOCKET, TCP, UDP, ...);&lt;br /&gt;
* optname &amp;amp;rarr; le nom de l&#039;option (&#039;&#039;SO_REUSEADDR&#039;&#039;, &#039;&#039;SO_REUSEPORT&#039;&#039;, &#039;&#039;SO_KEEPALIVE&#039;&#039;, ...)&lt;br /&gt;
* optval, optlen  &amp;amp;rarr; utilisé pour accéder aux options de la socket;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
Il existe plusieurs options de socket, les plus utilisées étant :&lt;br /&gt;
* &#039;&#039;SO_REUSEADDR&#039;&#039; &amp;amp;rarr; permet de réutiliser l&#039;adresse de la socket tout de suite même si cette dernière est dans l&#039;état &#039;&#039;wait&#039;&#039;;&lt;br /&gt;
* &#039;&#039;SO_REUSEPORT&#039;&#039; &amp;amp;rarr; permet de réutiliser le port de la socket tout de suite même si cette dernière est dans l&#039;état &#039;&#039;wait&#039;&#039;;&lt;br /&gt;
* &#039;&#039;SO_KEEPALIVE&#039;&#039; &amp;amp;rarr; envoie des informations périodiquement pour tester si l&#039;extrémité du tunnel est toujours présente;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Il faut maintenant attacher la socket à un port Internet et une adresse IP:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* addr &amp;amp;rarr; une structure symbolisant l&#039;adresse &lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
struct sockaddr_in {&lt;br /&gt;
    short            sin_family;   // e.g: AF_INET&lt;br /&gt;
    unsigned short   sin_port;     // e.g: htons(3490)&lt;br /&gt;
    struct in_addr   sin_addr;     // détaillé ci-dessous&lt;br /&gt;
    char             sin_zero[8];  // &#039;0&#039; habituellement&lt;br /&gt;
};&lt;br /&gt;
struct in_addr {&lt;br /&gt;
    unsigned long s_addr;  // initialiser avec inet_aton()&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On peut maintenant passer la socket en état d&#039;écoute, elle est prête à recevoir des connexions:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int listen(int sockfd, int backlog)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* backlog &amp;amp;rarr; taille maximum de la file d&#039;attente de la socket après laquelle le système répond avec &#039;&#039;ECONNREFUSED&#039;&#039;;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
On peut maintenant attendre une connexion:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* addr &amp;amp;rarr; l&#039;adresse du client;&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de la structure addr;&lt;br /&gt;
* le code retour varie entre un entier positif qui correspond au descripteur de la socket cliente et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
== Côté client ==&lt;br /&gt;
Après avoir créé la socket avec &#039;&#039;socket&#039;&#039; on peut se connecter à la partie serveur:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* addr &amp;amp;rarr; une structure symbolisant l&#039;adresse (cf. ci-dessus)&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; et &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée)&lt;br /&gt;
&lt;br /&gt;
= Lecture / écriture =&lt;br /&gt;
== Fonctions génériques ==&lt;br /&gt;
Tout d&#039;abord, on peut dire que la socket est un fichier et se manipule donc comme un fichier !&lt;br /&gt;
===Lecture===&lt;br /&gt;
Pour lire dans une socket on peut utiliser &#039;&#039;read&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t read(int fd, void *buf, size_t count);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères ou mettre le message reçu;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à recevoir;&lt;br /&gt;
* le code retour correspond au nombre de caractères reçu ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
===Écriture===&lt;br /&gt;
Pour écrire dans une socket ou peut utiliser &#039;&#039;write&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t write(int fd, const void *buf, size_t count);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères contenant le message à envoyer;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à envoyer;&lt;br /&gt;
* le code retour correspond au nombre de caractères envoyer ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
===Modification===&lt;br /&gt;
Pour modifier un descripteur fichier de socket on utilise &#039;&#039;fcntl&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int fcntl(int fd, int cmd, ... /* int args */ );&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* cmd &amp;amp;rarr; la commande de modification (descripteur, drapeaux, ...);&lt;br /&gt;
* args &amp;amp;rarr; les arguments correspondants à la commande&lt;br /&gt;
&lt;br /&gt;
Cette commande va nous permettre de modifier le drapeau du descripteur de la socket (&#039;&#039;F_SETFL&#039;&#039;) pour le rendre non bloquant (&#039;&#039;O_NONBLOCK&#039;&#039;) !&lt;br /&gt;
&lt;br /&gt;
== Fonctions spécifiques ==&lt;br /&gt;
Il existe des fonctions spécifiques pour manipuler les sockets. Ces fonctions ressemble au précédentes mais acceptent des drapeaux en plus pour pouvoir modifier le descripteur &#039;&#039;à la volé&#039;&#039;.&lt;br /&gt;
===Lecture===&lt;br /&gt;
Pour lire dans une socket on peut utiliser &#039;&#039;recv&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t recv(int fd, void *buf, size_t count, int flags);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères ou mettre le message reçu;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à recevoir;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;O_NONBLOCK&#039;&#039;);&lt;br /&gt;
* le code retour correspond au nombre de caractères reçu ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
===Écriture===&lt;br /&gt;
Pour écrire dans une socket ou peut utiliser &#039;&#039;send&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t send(int fd, const void *buf, size_t count, int flags);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères contenant le message à envoyer;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à envoyer;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;MSG_CONFIRM&#039;&#039;, &#039;&#039;MSG_DONTWAIT &#039;&#039;, ...);&lt;br /&gt;
* le code retour correspond au nombre de caractères envoyer ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
= Cas d&#039;utilisation =&lt;br /&gt;
== Création ==&lt;br /&gt;
Pour commencer, il faut créer le descripteur de fichier:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int fdsocket;&lt;br /&gt;
if ((fdsocket = socket(AF_INET, SOCK_STREAM, 0)) == -1) {&lt;br /&gt;
	printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Paramétrage des options ==&lt;br /&gt;
Il faut maintenant configurer la réutilisation de l&#039;adresse et du port&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int opt = 1;&lt;br /&gt;
if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,	sizeof(opt)) != 0) {&lt;br /&gt;
	printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Attachement ==&lt;br /&gt;
On peut maintenant attacher la socket à un port et une adresse:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
struct sockaddr_in adresse;&lt;br /&gt;
&lt;br /&gt;
adresse.sin_family = AF_INET;&lt;br /&gt;
// Ecoute sur toutes les adresses (INADDR_ANY &amp;lt;=&amp;gt; 0.0.0.0)&lt;br /&gt;
adresse.sin_addr.s_addr = INADDR_ANY;&lt;br /&gt;
// Conversion du port en valeur réseaux (Host TO Network Short)&lt;br /&gt;
adresse.sin_port = htons(8080);&lt;br /&gt;
&lt;br /&gt;
if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
	printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Mise en écoute ==&lt;br /&gt;
La socket est prête à passer à l&#039;écoute des connexions des clients:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
&lt;br /&gt;
if (listen(fdsocket, BACKLOG) != 0) {&lt;br /&gt;
	printf(&amp;quot;Echéc de la mise en écoute: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
	exit(EXIT_FAILURE);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Acceptation des connexions ==&lt;br /&gt;
Il est possible d&#039;accepter les nouvelles connexions:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int clientSocket;&lt;br /&gt;
// Structure contenant l&#039;adresse du client&lt;br /&gt;
struct sockaddr_in clientAdresse;&lt;br /&gt;
int addrLen = sizeof(clientAdresse);&lt;br /&gt;
if ((clientSocket = accept(*serverSocket, (struct sockaddr*) &amp;amp;clientAdresse, (socklen_t*) &amp;amp;addrLen)) != -1) {&lt;br /&gt;
	// Convertion de l&#039;IP en texte&lt;br /&gt;
	char ip[INET_ADDRSTRLEN];&lt;br /&gt;
	inet_ntop(AF_INET, &amp;amp;(clientAdresse.sin_addr), ip, INET_ADDRSTRLEN);&lt;br /&gt;
	printf(&amp;quot;Connexion de %s:%i\n&amp;quot;, ip, clientAdresse.sin_port);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Il est possible d&#039;utiliser &#039;&#039;fcntl&#039;&#039; pour passer le descripteur de la socket en non bloquant, avant l&#039;appel à la fonction &#039;&#039;accept&#039;&#039;. Si le descripteur reste bloquant, la fonction &#039;&#039;accept&#039;&#039; ne rendra pas la main tant qu&#039;un client ne se sera connecté, ayant pour résultat le blocage complet du programme !&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
// Passage en mode non bloquant&lt;br /&gt;
fcntl(fdsocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
On peut remarquer, au passage, l&#039;utilisation de &#039;&#039;inet_ntop&#039;&#039; pour convertir l&#039;adresse du client du format binaire au format text.&lt;br /&gt;
&lt;br /&gt;
== Lecture / Écriture ==&lt;br /&gt;
=== Mode connecté (TCP) ===&lt;br /&gt;
On peut maintenant utiliser le descripteur de la socket du client pour lire et écrire.&lt;br /&gt;
* avec les fonctions génériques:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Descripteur de la socket du client&lt;br /&gt;
int clientSocket;&lt;br /&gt;
char buffer[BUFFER_LEN];&lt;br /&gt;
// Passage en mode non bloquant, sinon read attend&lt;br /&gt;
fcntl(clientSocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
int len = read(clientSocket, buffer, BUFFER_LEN);&lt;br /&gt;
&lt;br /&gt;
send(clientSocket, &amp;quot;Coucou\n&amp;quot;, strlen(&amp;quot;Coucou\n&amp;quot;));&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* avec les fonctions spécifiques:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Descripteur de la socket du client&lt;br /&gt;
int clientSocket;&lt;br /&gt;
char buffer[BUFFER_LEN];&lt;br /&gt;
&lt;br /&gt;
int len = recv(clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
&lt;br /&gt;
send(clientSocket, &amp;quot;Coucou\n&amp;quot;, strlen(&amp;quot;Coucou\n&amp;quot;), MSG_DONTWAIT);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Analysons le retour de la fonction &#039;&#039;read&#039;&#039; ou &#039;&#039;recv&#039;&#039; lorsque l&#039;on utilise un descripteur non bloquant d&#039;après la [http://man7.org/linux/man-pages/man2/recvmsg.2.html documentation] :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
NAME &lt;br /&gt;
       recv, recvfrom, recvmsg - receive a message from a socket&lt;br /&gt;
[...]&lt;br /&gt;
MSG_DONTWAIT (since Linux 2.2)&lt;br /&gt;
    Enables nonblocking operation; if the operation would block, the call fails with the error EAGAIN or EWOULDBLOCK (this can also be enabled using the O_NONBLOCK flag with the F_SETFL fcntl(2)). &lt;br /&gt;
[...]&lt;br /&gt;
RETURN VALUE&lt;br /&gt;
[...]&lt;br /&gt;
These calls return the number of bytes received, or -1 if an error&lt;br /&gt;
       occurred.  In the event of an error, errno is set to indicate the&lt;br /&gt;
       error.&lt;br /&gt;
When a stream socket peer has performed an orderly shutdown, the&lt;br /&gt;
       return value will be 0 (the traditional &amp;quot;end-of-file&amp;quot; return).&lt;br /&gt;
[...]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Le retour est :&lt;br /&gt;
* lorsque le descripteur est vide (aucune donnée de la part du client) &#039;&#039;-1&#039;&#039; et &#039;&#039;errno&#039;&#039; sera positionné à la valeur &#039;&#039;EAGAIN&#039;&#039;, il ne faut donc pas fermer la socket si le retour est &#039;&#039;-1&#039;&#039; ! Il faut d&#039;abord vérifier que &#039;&#039;errno&#039;&#039; &#039;&#039;&#039;n&#039;a pas&#039;&#039;&#039; la valeur &#039;&#039;EAGAIN&#039;&#039;;&lt;br /&gt;
* lorsque le retour est &#039;&#039;0&#039;&#039;, cela signifie que l&#039;extrémité est fermée, il faut donc fermer le descripteur local;&lt;br /&gt;
* lorsque la valeur est supérieur à &#039;&#039;0&#039;&#039; elle correspond au nombre d&#039;octets reçu.&lt;br /&gt;
&lt;br /&gt;
On peut donc en déduire le morceau de code suivant pour gérer le retour de &#039;&#039;read&#039;&#039; ou &#039;&#039;recv&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int len = recv(clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
&lt;br /&gt;
if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
	// Une erreur est survenue&lt;br /&gt;
} else if (len == 0) {&lt;br /&gt;
	// Le client s&#039;est déconnecté (extrémité de la socket fermée)			&lt;br /&gt;
} else if (len &amp;gt; 0) {&lt;br /&gt;
	// Le client à envoyé des données	&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Mode datagramme (UDP) ===&lt;br /&gt;
===Lecture===&lt;br /&gt;
Pour lire dans une socket on peut utiliser &#039;&#039;recvfrom&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t recvfrom(int fd, void *buf, size_t count, int flags, const struct sockaddr *addr, socklen_t addrlen);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères ou mettre le message reçu;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à recevoir;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;O_NONBLOCK&#039;&#039;);&lt;br /&gt;
*  addr &amp;amp;rarr; une structure symbolisant l&#039;adresse du client (cf. ci-dessus)&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour correspond au nombre de caractères reçu ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
===Écriture===&lt;br /&gt;
Pour écrire dans une socket ou peut utiliser &#039;&#039;sendto&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t sendto(int fd, const void *buf, size_t count, const struct sockaddr *addr, socklen_t addrlen);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* sockfd &amp;amp;rarr; le fichier descripteur de la socket;&lt;br /&gt;
* buf &amp;amp;rarr; le tableau de caractères contenant le message à envoyer;&lt;br /&gt;
* count &amp;amp;rarr; le nombre de caractères à envoyer;&lt;br /&gt;
* flags &amp;amp;rarr; liste de drapeaux (eg. &#039;&#039;MSG_DONTWAIT &#039;&#039;, ...);&lt;br /&gt;
* addr &amp;amp;rarr; une structure symbolisant l&#039;adresse du client (cf. ci-dessus)&lt;br /&gt;
* addrlen &amp;amp;rarr; la taille de l&#039;objet addr;&lt;br /&gt;
* le code retour correspond au nombre de caractères envoyer ou &#039;&#039;-1&#039;&#039; si une erreur survient (errno est positionné);&lt;br /&gt;
&lt;br /&gt;
= Exemples =&lt;br /&gt;
== Serveur TCP ==&lt;br /&gt;
Voici un exemple de serveur &#039;&#039;echo&#039;&#039; qui renvoie le message au client. Les connexions utilisent TCP et un simple client comme telnet suffit pour l&#039;utiliser.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;arpa/inet.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Port d&#039;écoute de la socket&lt;br /&gt;
#define PORT 8080&lt;br /&gt;
// Adresse d&#039;écoute (toutes les adresses)&lt;br /&gt;
#define IP INADDR_ANY&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
// Nombre de connexions clients&lt;br /&gt;
#define NB_CLIENTS 2&lt;br /&gt;
// Taille du tampon de lecture des messages&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Commande pour arrêter le serveur&lt;br /&gt;
#define EXIT_WORD &amp;quot;exit&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse);&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse);&lt;br /&gt;
int waitForClient(int * serverSocket);&lt;br /&gt;
void addClientToTab(int clientSocket, int clients[]);&lt;br /&gt;
void manageClient(int clients[]);&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
	// Création et initialisation du tableau contenant les descripteurs des sockets clients&lt;br /&gt;
	int clients[NB_CLIENTS];&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_CLIENTS; i++) {&lt;br /&gt;
		clients[i] = -1;&lt;br /&gt;
	}&lt;br /&gt;
	// Structure contenant l&#039;adresse&lt;br /&gt;
	struct sockaddr_in adresse;&lt;br /&gt;
	initAdresse(&amp;amp;adresse);&lt;br /&gt;
	// Descripteur de la socket du serveur&lt;br /&gt;
	int serverSocket = initSocket(&amp;amp;adresse);&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	while (1) {&lt;br /&gt;
		// Descripteur de la socket du client, on attend une connexion&lt;br /&gt;
		if ((clientSocket = waitForClient(&amp;amp;serverSocket)) != -1) {&lt;br /&gt;
			// On ajoute le nouveau client au tableau des descripteurs&lt;br /&gt;
			addClientToTab(clientSocket, clients);&lt;br /&gt;
		}&lt;br /&gt;
		manageClient(clients);&lt;br /&gt;
	}&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
// Initialisation de la structure sockaddr_in&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse) {&lt;br /&gt;
	(*adresse).sin_family = AF_INET;&lt;br /&gt;
	(*adresse).sin_addr.s_addr = IP;&lt;br /&gt;
	(*adresse).sin_port = htons( PORT);&lt;br /&gt;
}&lt;br /&gt;
// Démarrage de la socket serveur&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse) {&lt;br /&gt;
	// Descripteur de socket&lt;br /&gt;
	int fdsocket;&lt;br /&gt;
	// Nombre d&#039;options&lt;br /&gt;
	int opt = 1;&lt;br /&gt;
	// Création de la socket en TCP&lt;br /&gt;
	if ((fdsocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) == -1) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Création de la socket\n&amp;quot;);&lt;br /&gt;
	// Paramètrage de la socket&lt;br /&gt;
	if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,&lt;br /&gt;
			sizeof(opt)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Paramètrage de la socket\n&amp;quot;);&lt;br /&gt;
	// Attachement de la socket sur le port et l&#039;adresse IP&lt;br /&gt;
	if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Attachement de la socket sur le port %i\n&amp;quot;, PORT);&lt;br /&gt;
	// Passage en écoute de la socket&lt;br /&gt;
	if (listen(fdsocket, BACKLOG) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la mise en écoute: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	// Passage en mode non bloquant&lt;br /&gt;
	fcntl(fdsocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
	printf(&amp;quot;Mise en écoute de la socket\n&amp;quot;);&lt;br /&gt;
	return fdsocket;&lt;br /&gt;
}&lt;br /&gt;
// Attente de connexion d&#039;un client&lt;br /&gt;
int waitForClient(int * serverSocket) {&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	// Structure contenant l&#039;adresse du client&lt;br /&gt;
	struct sockaddr_in clientAdresse;&lt;br /&gt;
	int addrLen = sizeof(clientAdresse);&lt;br /&gt;
	if ((clientSocket = accept(*serverSocket, (struct sockaddr*) &amp;amp;clientAdresse,&lt;br /&gt;
			(socklen_t*) &amp;amp;addrLen)) != -1) {&lt;br /&gt;
		// Convertion de l&#039;IP en texte&lt;br /&gt;
		char ip[INET_ADDRSTRLEN];&lt;br /&gt;
		inet_ntop(AF_INET, &amp;amp;(clientAdresse.sin_addr), ip, INET_ADDRSTRLEN);&lt;br /&gt;
		printf(&amp;quot;Connexion de %s:%i\n&amp;quot;, ip, clientAdresse.sin_port);&lt;br /&gt;
		// Passage en mode non bloquant&lt;br /&gt;
		int opt = 1;&lt;br /&gt;
		setsockopt(clientSocket, SOL_SOCKET, SO_KEEPALIVE, &amp;amp;opt, sizeof(1));&lt;br /&gt;
	}&lt;br /&gt;
	return clientSocket;&lt;br /&gt;
}&lt;br /&gt;
// Ajoute les clients au tableau&lt;br /&gt;
void addClientToTab(int clientSocket, int clients[]) {&lt;br /&gt;
	// On vérifie si on à de la place de libre&lt;br /&gt;
	int found = -1;&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_CLIENTS; i++) {&lt;br /&gt;
		// On cherche de la place&lt;br /&gt;
		if (clients[i] == -1) {&lt;br /&gt;
			// On ajoute le client&lt;br /&gt;
			printf(&amp;quot;Ajout du client à l&#039;index %i\n&amp;quot;, i);&lt;br /&gt;
			clients[i] = clientSocket;&lt;br /&gt;
			// Nouvelle connexion, envoie du message de bienvenu&lt;br /&gt;
			send(clientSocket, &amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;, 	strlen(&amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;),&lt;br /&gt;
					MSG_DONTWAIT);&lt;br /&gt;
			found = 0;&lt;br /&gt;
			break;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	if (found == -1) {&lt;br /&gt;
		// On a plus de place de libre&lt;br /&gt;
		puts(&amp;quot;Plus de place, désolé&amp;quot;);&lt;br /&gt;
		close(clientSocket);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// On traite l&#039;input des clients&lt;br /&gt;
void manageClient(int clients[]) {&lt;br /&gt;
	// Création d&#039;un tampon pour stocker les messages des clients dans la heap&lt;br /&gt;
	static char buffer[BUFFER_LEN + 1];&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_CLIENTS; i++) {&lt;br /&gt;
		// On vérifie les messages&lt;br /&gt;
		int clientSocket = clients[i];&lt;br /&gt;
		if (clientSocket == -1) {&lt;br /&gt;
			continue;&lt;br /&gt;
		}&lt;br /&gt;
		int len = recv(clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
		// Booléen pour suivre l&#039;état de la socket&lt;br /&gt;
		int isClosed = 0;&lt;br /&gt;
		if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
			// Une erreur est survenue&lt;br /&gt;
			printf(&amp;quot;Errno [%i] : %s\n&amp;quot;, errno, strerror(errno));&lt;br /&gt;
			isClosed = 1;&lt;br /&gt;
		} else if (len == 0) {&lt;br /&gt;
			// Le client s&#039;est déconnecté (extrémité de la socket fermée)&lt;br /&gt;
			isClosed = 1;&lt;br /&gt;
		} else if (len &amp;gt; 0) {&lt;br /&gt;
			// Ajout du terminateur de chaîne&lt;br /&gt;
			buffer[len] = &#039;\0&#039;;&lt;br /&gt;
			if (strncmp(buffer, EXIT_WORD, 4) == 0) {&lt;br /&gt;
				// Le client veut se déconnecter&lt;br /&gt;
				send(clientSocket, &amp;quot;Bye\n&amp;quot;, strlen(&amp;quot;Bye\n&amp;quot;), 0);&lt;br /&gt;
				isClosed = 1;&lt;br /&gt;
			} else {&lt;br /&gt;
				// On renvoie le texte au client dans un buffer assez grand&lt;br /&gt;
				int len = strlen(&amp;quot;Vous avez dit : &amp;quot;) + strlen(buffer) + 1;&lt;br /&gt;
				char response[len];&lt;br /&gt;
				strcpy(response, &amp;quot;Vous avez dit : &amp;quot;);&lt;br /&gt;
				strcat(response, buffer);&lt;br /&gt;
				// Un seul envoie permet de ne pas surcharger le réseau&lt;br /&gt;
				send(clientSocket, response, strlen(response), 0);&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
		if (isClosed == 1) {&lt;br /&gt;
			// La socket est fermé ou le client veut quitter le serveur !&lt;br /&gt;
			printf(&amp;quot;Fermeture de la connexion avec le client\n&amp;quot;);&lt;br /&gt;
			// Fermeture de la socket&lt;br /&gt;
			close(clientSocket);&lt;br /&gt;
			// On fait de la place dans le tableau&lt;br /&gt;
			clients[i] = -1;&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
On peut interroger notre serveur avec &#039;&#039;telnet&#039;&#039; (&#039;&#039;yum -y install telnet&#039;&#039;):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# telnet 127.0.0.1 8080&lt;br /&gt;
Trying 127.0.0.1...&lt;br /&gt;
Connected to 127.0.0.1.&lt;br /&gt;
Escape character is &#039;^]&#039;.&lt;br /&gt;
Entrez &#039;exit&#039; pour quitter&lt;br /&gt;
coucou&lt;br /&gt;
Vous avez dit : coucou&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
== Serveur UDP ==&lt;br /&gt;
Si on essaye de reproduire le même type de serveur mais cette fois-ci avec une socket en UDP, cela devient beaucoup plus simple. En effet, en UDP, plus besoin de tenir un tableau avec les descripteurs des sockets des clients car... on est en mode déconnecté (datagramme) ! Entendez par la que les clients envoie un message sans attendre de réponse et sans se soucier de savoir si le message est arrivé au serveur.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;arpa/inet.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Port d&#039;écoute de la socket&lt;br /&gt;
#define PORT 8080&lt;br /&gt;
// Adresse d&#039;écoute (toutes les adresses)&lt;br /&gt;
#define IP INADDR_ANY&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
// Taille du tampon de lecture des messages&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse);&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse);&lt;br /&gt;
void manageClient(int serverSocket);&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
	// Structure contenant l&#039;adresse&lt;br /&gt;
	struct sockaddr_in adresse;&lt;br /&gt;
	initAdresse(&amp;amp;adresse);&lt;br /&gt;
	// Descripteur de la socket du serveur&lt;br /&gt;
	int serverSocket = initSocket(&amp;amp;adresse);&lt;br /&gt;
	while (1) {&lt;br /&gt;
		// Descripteur de la socket du client, on attend une connexion&lt;br /&gt;
		manageClient(serverSocket);&lt;br /&gt;
	}&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
// Initialisation de la structure sockaddr_in&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse) {&lt;br /&gt;
	(*adresse).sin_family = AF_INET;&lt;br /&gt;
	(*adresse).sin_addr.s_addr = IP;&lt;br /&gt;
	(*adresse).sin_port = htons( PORT);&lt;br /&gt;
}&lt;br /&gt;
// Démarrage de la socket serveur&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse) {&lt;br /&gt;
	// Descripteur de socket&lt;br /&gt;
	int fdsocket;&lt;br /&gt;
	// Nombre d&#039;options&lt;br /&gt;
	int opt = 1;&lt;br /&gt;
	// Création de la socket en UDP&lt;br /&gt;
	if ((fdsocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Création de la socket\n&amp;quot;);&lt;br /&gt;
	// Paramètrage de la socket&lt;br /&gt;
	if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,&lt;br /&gt;
			sizeof(opt)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Paramètrage de la socket\n&amp;quot;);&lt;br /&gt;
	// Attachement de la socket sur le port et l&#039;adresse IP&lt;br /&gt;
	if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Attachement de la socket sur le port %i\n&amp;quot;, PORT);&lt;br /&gt;
	return fdsocket;&lt;br /&gt;
}&lt;br /&gt;
// On traite l&#039;input des clients&lt;br /&gt;
void manageClient(int serverSocket) {&lt;br /&gt;
	// Création d&#039;un tampon pour stocker les messages des clients dans la heap&lt;br /&gt;
	static char buffer[BUFFER_LEN + 1];&lt;br /&gt;
	// Structure contenant l&#039;adresse du client&lt;br /&gt;
	struct sockaddr_in clientAdresse;&lt;br /&gt;
	unsigned int addrLen = sizeof(clientAdresse);&lt;br /&gt;
	// On vérifie si on a reçu un message&lt;br /&gt;
	int len = recvfrom(serverSocket, buffer, BUFFER_LEN, MSG_DONTWAIT,&lt;br /&gt;
			(struct sockaddr*) &amp;amp;clientAdresse, &amp;amp;addrLen);&lt;br /&gt;
	if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
		printf(&amp;quot;Problème de socket %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	} else if (len &amp;gt; 0) {&lt;br /&gt;
		printf(&amp;quot;Message reçu de %s:%i\n&amp;quot;, inet_ntoa(clientAdresse.sin_addr),&lt;br /&gt;
				ntohs(clientAdresse.sin_port));&lt;br /&gt;
		// Ajout du terminateur de chaîne&lt;br /&gt;
		buffer[len] = &#039;\0&#039;;&lt;br /&gt;
		// On renvoie le texte au client dans un buffer assez grand&lt;br /&gt;
		len = strlen(&amp;quot;Vous avez dit : &amp;quot;) + strlen(buffer) + 1;&lt;br /&gt;
		char response[len];&lt;br /&gt;
		strcpy(response, &amp;quot;Vous avez dit : &amp;quot;);&lt;br /&gt;
		strcat(response, buffer);&lt;br /&gt;
		// Un seul envoie permet de ne pas surcharger le réseau&lt;br /&gt;
		sendto(serverSocket, response, strlen(response), MSG_DONTWAIT,&lt;br /&gt;
				(struct sockaddr*) &amp;amp;clientAdresse, addrLen);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
On peut interroger notre serveur avec &#039;&#039;netcat&#039;&#039; (&#039;&#039;yum -y install nc&#039;&#039;):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# nc -u 127.0.0.1 8080&lt;br /&gt;
coucou&lt;br /&gt;
Vous avez dit : coucou&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
= Notification évènementielle avec epoll =&lt;br /&gt;
Depuis la version 2.5 du noyau Linux il est possible d&#039;utiliser une série de fonctions, appartenant à l&#039;espace utilisateur (user-space), permettant de suivre l&#039;état d&#039;un descripteur de fichier.&lt;br /&gt;
La différence avec read, recv ou encore recvfrom c&#039;est que l&#039;on va pouvoir surveiller plusieurs descripteurs de fichiers en même temps, laissant le CPU libre pour effectuer autre chose !&lt;br /&gt;
&lt;br /&gt;
== Utilisation ==&lt;br /&gt;
Tout d&#039;abord il faut créer un descripteur de fichier de type &#039;&#039;epoll&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int epoll_create1(int flags);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* flags &amp;amp;rarr; EPOLL_CLOEXEC pour éviter les situation de compétition en environnement parallélisé;&lt;br /&gt;
* le code retour correspond au descripteur &#039;&#039;epoll&#039;&#039; ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée).&lt;br /&gt;
&lt;br /&gt;
Une fois le descripteur &#039;&#039;epoll&#039;&#039; créé, il faut lui associer un événement:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* epfd &amp;amp;rarr; le descripteur &#039;&#039;epoll&#039;&#039; à modifier;&lt;br /&gt;
* op &amp;amp;rarr; une opération parmis:&lt;br /&gt;
** EPOLL_CTL_ADD &amp;amp;rarr; pour ajouter un événement sur un descripteur de fichier;&lt;br /&gt;
** EPOLL_CTL_MOD &amp;amp;rarr; pour modifier un événement sur un descripteur de fichier;&lt;br /&gt;
** EPOLL_CTL_DEL &amp;amp;rarr; pour supprimer un événement sur un descripteur de fichier;&lt;br /&gt;
* fd &amp;amp;rarr; un descripteur de fichier à surveiller;&lt;br /&gt;
* event &amp;amp;rarr; la structure contenant l&#039;événement à surveiller (&#039;&#039;EPOLLIN&#039;&#039;, &#039;&#039;EPOLLOUT&#039;&#039;, EPOLLRDHUP&#039;&#039;, ...);&lt;br /&gt;
* le code retour varie entre &#039;&#039;0&#039;&#039; en cas de succès ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée).&lt;br /&gt;
&lt;br /&gt;
Voici la structure &#039;&#039;epoll_event&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
struct epoll_event {&lt;br /&gt;
	uint32_t events;    /* Epoll events */&lt;br /&gt;
	epoll_data_t data;  /* User data variable */&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
typedef union epoll_data {&lt;br /&gt;
	void    *ptr;&lt;br /&gt;
	int      fd;&lt;br /&gt;
	uint32_t u32;&lt;br /&gt;
	uint64_t u64;&lt;br /&gt;
} epoll_data_t;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Enfin, il ne reste plus qu&#039;à &#039;&#039;attendre&#039;&#039; des événements:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* epfd &amp;amp;rarr; le descripteur &#039;&#039;epoll&#039;&#039; à modifier;&lt;br /&gt;
* events &amp;amp;rarr; un tableau de &#039;&#039;epoll_event contenant les événements;&lt;br /&gt;
* maxevents &amp;amp;rarr; le nombre maximum d&#039;événements à traiter (selon la taille du tableau);&lt;br /&gt;
* timeout &amp;amp;rarr; le temps, en millisecondes que la fonction attend:&lt;br /&gt;
** &#039;&#039;-1&#039;&#039; &amp;amp;rarr; la fonction devient bloquante;&lt;br /&gt;
** &#039;&#039;0&#039;&#039; &amp;amp;rarr; retourne immédiatement;&lt;br /&gt;
** &#039;&#039;&amp;gt; 0&#039;&#039; &amp;amp;rarr; attend le nombre de millisecondes spécifiées;&lt;br /&gt;
* le code retour correspond au nombre d&#039;événements ou &#039;&#039;-1&#039;&#039; en cas d&#039;erreur (&#039;&#039;errno&#039;&#039; est positionnée).&lt;br /&gt;
&lt;br /&gt;
== Améliorations architecturales ==&lt;br /&gt;
=== Gestion des utilisateurs===&lt;br /&gt;
Tout d&#039;abord, le faite de laisser &#039;&#039;epoll&#039;&#039; gérer les descripteurs de socket nous permet de supprimer le tableau &#039;&#039;clients&#039;&#039; ainsi que la variable &#039;&#039;NB_CLIENTS&#039;&#039;. En effet, la référence vers le descripteur est maintenu par &#039;&#039;epoll&#039;&#039; et nous pouvons gérer maintenant un nombre &#039;&#039;illimité&#039;&#039; de clients.&lt;br /&gt;
=== Performances ===&lt;br /&gt;
La boucle &#039;&#039;while&#039;&#039; de notre serveur consomme énormément de CPU, environ 100% de CPU par client ! On aurais pu utiliser un temporisateur avec &#039;&#039;nanosleep&#039;&#039; pour baisser la consommation CPU entre 5% et 10%...&lt;br /&gt;
Le faîte de basculer sur une fonction bloquante comme &#039;&#039;epoll_wait&#039;&#039; qui utilise des signaux (interruptions) permet de réduire considérablement la consommation CPU. Gardez à l&#039;esprit qu&#039;il n&#039;y a rien de mieux que les interruptions, la preuve, lorsque l&#039;on fait un &#039;&#039;top&#039;&#039;, notre programme à complétement disparu de la liste !&lt;br /&gt;
&lt;br /&gt;
== Modification du serveur ==&lt;br /&gt;
Modifions le code du serveur TCP pour y intégrer &#039;&#039;epoll&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/socket.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;netinet/in.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;arpa/inet.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/epoll.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Port d&#039;écoute de la socket&lt;br /&gt;
#define PORT 8080&lt;br /&gt;
// Adresse d&#039;écoute (toutes les adresses)&lt;br /&gt;
#define IP INADDR_ANY&lt;br /&gt;
// Taille de la file d&#039;attente&lt;br /&gt;
#define BACKLOG 3&lt;br /&gt;
// Nombre de connexions clients&lt;br /&gt;
#define NB_EVENTS 2&lt;br /&gt;
// Taille du tampon de lecture des messages&lt;br /&gt;
#define BUFFER_LEN 200&lt;br /&gt;
// Commande pour arrêter le serveur&lt;br /&gt;
#define EXIT_WORD &amp;quot;exit&amp;quot;&lt;br /&gt;
&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse);&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse);&lt;br /&gt;
int waitForClient(int * serverSocket);&lt;br /&gt;
void addClientToTab(int * epfd, int * clientSocket);&lt;br /&gt;
void manageClient(int * epfd, int * clientSocket);&lt;br /&gt;
void addEpollEvent(int * epfd, int * socket);&lt;br /&gt;
void delEpollEvent(int * epfd, int * socket);&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
	// Création du descripteur EPOLL&lt;br /&gt;
	int epfd = epoll_create1(0);&lt;br /&gt;
	// Structure contenant les événements EPOLL&lt;br /&gt;
	struct epoll_event events[NB_EVENTS];&lt;br /&gt;
	// Structure contenant l&#039;adresse&lt;br /&gt;
	struct sockaddr_in adresse;&lt;br /&gt;
	initAdresse(&amp;amp;adresse);&lt;br /&gt;
	// Descripteur de la socket du serveur&lt;br /&gt;
	int serverSocket = initSocket(&amp;amp;adresse);&lt;br /&gt;
	addEpollEvent(&amp;amp;epfd, &amp;amp;serverSocket);&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	while (1) {&lt;br /&gt;
		int nfds = epoll_wait(epfd, events, NB_EVENTS, -1);&lt;br /&gt;
		for (int i = 0; i &amp;lt; nfds; i++) {&lt;br /&gt;
			if (events[i].data.fd == serverSocket) {&lt;br /&gt;
				if ((clientSocket = waitForClient(&amp;amp;serverSocket)) != -1) {&lt;br /&gt;
					// On ajoute le nouveau client au tableau des descripteurs&lt;br /&gt;
					addClientToTab(&amp;amp;epfd, &amp;amp;clientSocket);&lt;br /&gt;
				}&lt;br /&gt;
			} else if (events[i].events &amp;amp; EPOLLIN) {&lt;br /&gt;
				// On a reçu quelque chose&lt;br /&gt;
				manageClient(&amp;amp;epfd, &amp;amp;events[i].data.fd);&lt;br /&gt;
			}&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	close(serverSocket);&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
// Initialisation de la structure sockaddr_in&lt;br /&gt;
void initAdresse(struct sockaddr_in * adresse) {&lt;br /&gt;
	(*adresse).sin_family = AF_INET;&lt;br /&gt;
	(*adresse).sin_addr.s_addr = IP;&lt;br /&gt;
	(*adresse).sin_port = htons( PORT);&lt;br /&gt;
}&lt;br /&gt;
// Démarrage de la socket serveur&lt;br /&gt;
int initSocket(struct sockaddr_in * adresse) {&lt;br /&gt;
	// Descripteur de socket&lt;br /&gt;
	int fdsocket;&lt;br /&gt;
	// Nombre d&#039;options&lt;br /&gt;
	int opt = 1;&lt;br /&gt;
	// Création de la socket en TCP&lt;br /&gt;
	if ((fdsocket = socket(AF_INET, SOCK_STREAM, 0)) == -1) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la création: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Création de la socket\n&amp;quot;);&lt;br /&gt;
	// Paramètrage de la socket&lt;br /&gt;
	if (setsockopt(fdsocket, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &amp;amp;opt,&lt;br /&gt;
			sizeof(opt)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de paramètrage: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Paramètrage de la socket\n&amp;quot;);&lt;br /&gt;
	// Attachement de la socket sur le port et l&#039;adresse IP&lt;br /&gt;
	if (bind(fdsocket, (struct sockaddr *) adresse, sizeof(*adresse)) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc d&#039;attachement: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Attachement de la socket sur le port %i\n&amp;quot;, PORT);&lt;br /&gt;
	// Passage en écoute de la socket&lt;br /&gt;
	if (listen(fdsocket, BACKLOG) != 0) {&lt;br /&gt;
		printf(&amp;quot;Echéc de la mise en écoute: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	// Passage en mode non bloquant&lt;br /&gt;
	fcntl(fdsocket, F_SETFL, O_NONBLOCK);&lt;br /&gt;
	printf(&amp;quot;Mise en écoute de la socket\n&amp;quot;);&lt;br /&gt;
	return fdsocket;&lt;br /&gt;
}&lt;br /&gt;
// Attente de connexion d&#039;un client&lt;br /&gt;
int waitForClient(int * serverSocket) {&lt;br /&gt;
	int clientSocket;&lt;br /&gt;
	// Structure contenant l&#039;adresse du client&lt;br /&gt;
	struct sockaddr_in clientAdresse;&lt;br /&gt;
	int addrLen = sizeof(clientAdresse);&lt;br /&gt;
	if ((clientSocket = accept(*serverSocket, (struct sockaddr*) &amp;amp;clientAdresse,&lt;br /&gt;
			(socklen_t*) &amp;amp;addrLen)) != -1) {&lt;br /&gt;
		// Convertion de l&#039;IP en texte&lt;br /&gt;
		char ip[INET_ADDRSTRLEN];&lt;br /&gt;
		inet_ntop(AF_INET, &amp;amp;(clientAdresse.sin_addr), ip, INET_ADDRSTRLEN);&lt;br /&gt;
		printf(&amp;quot;Connexion de %s:%i\n&amp;quot;, ip, clientAdresse.sin_port);&lt;br /&gt;
		// Passage en mode non bloquant&lt;br /&gt;
		int opt = 1;&lt;br /&gt;
		setsockopt(clientSocket, SOL_SOCKET, SO_KEEPALIVE, &amp;amp;opt, sizeof(1));&lt;br /&gt;
	}&lt;br /&gt;
	return clientSocket;&lt;br /&gt;
}&lt;br /&gt;
// Ajoute les clients au tableau&lt;br /&gt;
void addClientToTab(int * epfd, int * clientSocket) {&lt;br /&gt;
	addEpollEvent(epfd, clientSocket);&lt;br /&gt;
	// Nouvelle connexion, envoie du message de bienvenu&lt;br /&gt;
	send(*clientSocket, &amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;,&lt;br /&gt;
			strlen(&amp;quot;Entrez &#039;exit&#039; pour quitter\n&amp;quot;), MSG_DONTWAIT);&lt;br /&gt;
}&lt;br /&gt;
// On traite l&#039;input des clients&lt;br /&gt;
void manageClient(int * epfd, int * clientSocket) {&lt;br /&gt;
	// Création d&#039;un tampon pour stocker les messages des clients dans la heap&lt;br /&gt;
	static char buffer[BUFFER_LEN + 1];&lt;br /&gt;
	int len = recv(*clientSocket, buffer, BUFFER_LEN, MSG_DONTWAIT);&lt;br /&gt;
	// Booléen pour suivre l&#039;état de la socket&lt;br /&gt;
	int isClosed = 0;&lt;br /&gt;
	if (len == -1 &amp;amp;&amp;amp; errno != EAGAIN) {&lt;br /&gt;
		// Une erreur est survenue&lt;br /&gt;
		printf(&amp;quot;Errno [%i] : %s\n&amp;quot;, errno, strerror(errno));&lt;br /&gt;
		isClosed = 1;&lt;br /&gt;
	} else if (len == 0) {&lt;br /&gt;
		// Le client s&#039;est déconnecté (extrémité de la socket fermée)&lt;br /&gt;
		isClosed = 1;&lt;br /&gt;
	} else if (len &amp;gt; 0) {&lt;br /&gt;
		// Ajout du terminateur de chaîne&lt;br /&gt;
		buffer[len] = &#039;\0&#039;;&lt;br /&gt;
		if (strncmp(buffer, EXIT_WORD, 4) == 0) {&lt;br /&gt;
			// Le client veut se déconnecter&lt;br /&gt;
			send(*clientSocket, &amp;quot;Bye\n&amp;quot;, strlen(&amp;quot;Bye\n&amp;quot;), 0);&lt;br /&gt;
			isClosed = 1;&lt;br /&gt;
		} else {&lt;br /&gt;
			// On renvoie le texte au client dans un buffer assez grand&lt;br /&gt;
			int len = strlen(&amp;quot;Vous avez dit : &amp;quot;) + strlen(buffer) + 1;&lt;br /&gt;
			char response[len];&lt;br /&gt;
			strcpy(response, &amp;quot;Vous avez dit : &amp;quot;);&lt;br /&gt;
			strcat(response, buffer);&lt;br /&gt;
			// Un seul envoie permet de ne pas surcharger le réseau&lt;br /&gt;
			send(*clientSocket, response, strlen(response), 0);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	if (isClosed == 1) {&lt;br /&gt;
		// On enlève la socket des événements EPOLL&lt;br /&gt;
		delEpollEvent(epfd, clientSocket);&lt;br /&gt;
		// La socket est fermé ou le client veut quitter le serveur !&lt;br /&gt;
		printf(&amp;quot;Fermeture de la connexion avec le client\n&amp;quot;);&lt;br /&gt;
		// Fermeture de la socket&lt;br /&gt;
		close(*clientSocket);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// Ajoute un descripteur de socket aux événements EPOLL&lt;br /&gt;
void addEpollEvent(int * epfd, int * socket) {&lt;br /&gt;
	printf(&amp;quot;Ajout de la socket %i dans les événements EPOLL\n&amp;quot;, *socket);&lt;br /&gt;
	// Structure contenant les événements EPOLL&lt;br /&gt;
	struct epoll_event event;&lt;br /&gt;
	// On enregistre la socket du serveur comme descripteur EPOLL&lt;br /&gt;
	event.data.fd = *socket;&lt;br /&gt;
	// On surveille les message entrant, la fermeture de la socket&lt;br /&gt;
	event.events = EPOLLIN | EPOLLRDHUP;&lt;br /&gt;
	epoll_ctl(*epfd, EPOLL_CTL_ADD, *socket, &amp;amp;event);&lt;br /&gt;
}&lt;br /&gt;
// Efface un descripteur de socket des événements EPOLL&lt;br /&gt;
void delEpollEvent(int * epfd, int * socket) {&lt;br /&gt;
	printf(&amp;quot;Suppression de la socket %i des événements EPOLL\n&amp;quot;, *socket);&lt;br /&gt;
	// Structure contenant les événements EPOLL&lt;br /&gt;
	struct epoll_event event;&lt;br /&gt;
	// On enregistre la socket du serveur comme descripteur EPOLL&lt;br /&gt;
	event.data.fd = *socket;&lt;br /&gt;
	// On surveille les message entrant, la fermeture de la socket&lt;br /&gt;
	event.events = EPOLLIN | EPOLLRDHUP;&lt;br /&gt;
	epoll_ctl(*epfd, EPOLL_CTL_DEL, *socket, &amp;amp;event);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Voici ce que l&#039;on peut observer dans la console:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Création de la socket&lt;br /&gt;
Paramètrage de la socket&lt;br /&gt;
Attachement de la socket sur le port 8080&lt;br /&gt;
Mise en écoute de la socket&lt;br /&gt;
Ajout de la socket 4 dans les événements EPOLL&lt;br /&gt;
Connexion de 127.0.0.1:17607&lt;br /&gt;
Ajout de la socket 5 dans les événements EPOLL&lt;br /&gt;
Suppression de la socket 5 des événements EPOLL&lt;br /&gt;
Fermeture de la connexion avec le client&lt;br /&gt;
Connexion de 127.0.0.1:18119&lt;br /&gt;
Ajout de la socket 5 dans les événements EPOLL&lt;br /&gt;
Connexion de 127.0.0.1:18631&lt;br /&gt;
Ajout de la socket 6 dans les événements EPOLL&lt;br /&gt;
Suppression de la socket 5 des événements EPOLL&lt;br /&gt;
Fermeture de la connexion avec le client&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Magsss</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=C_signals&amp;diff=3273</id>
		<title>C signals</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=C_signals&amp;diff=3273"/>
		<updated>2018-11-01T18:07:09Z</updated>

		<summary type="html">&lt;p&gt;Magsss : /* Utilisation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Il est intéressant de récupérer les signaux afin de pouvoir les interpréter dans un programme. Cela permet de simplifier l’interaction avec l&#039;utilisateur qui veut, par exemple, interrompre, redémarrer ou encore arrêter le programme.&lt;br /&gt;
&lt;br /&gt;
= Utilisation =&lt;br /&gt;
Pour enregistrer une fonction gestionnaire de signaux, il faut utiliser la fonction suivante:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;signal.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int signal (int signo, void * func);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* signo &amp;amp;rarr; le signal à attraper ;&lt;br /&gt;
* func &amp;amp;rarr; un pointeur vers la fonction à exécuter.&lt;br /&gt;
* le code retour varie entre :&lt;br /&gt;
**la valeur précédente du gestionnaire de signaux ;&lt;br /&gt;
**SIG_ERR en cas d&#039;erreur et &#039;&#039;errno&#039;&#039; est positionné.&lt;br /&gt;
&lt;br /&gt;
Le prototype de la fonction gestionnaire est le suivant:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
void * sig_handler(int signo);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Exemple =&lt;br /&gt;
Ci-dessous un exemple qui attrape le signal SIGINT (ctrl+c):&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;signal.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Indicateur de fin de programme&lt;br /&gt;
int end = 0;&lt;br /&gt;
&lt;br /&gt;
// Gestionnaire de signaux&lt;br /&gt;
void sig_handler(int sig) {&lt;br /&gt;
	printf(&amp;quot;\nSIGINT attrapé, on stop le programme %i\n&amp;quot;, getpid());&lt;br /&gt;
	end = 1;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
	puts(&amp;quot;Utilisez SIGINT pour interrompre l&#039;exécution !&amp;quot;);&lt;br /&gt;
	// Enregistrement du gestionnaire de signaux&lt;br /&gt;
	if(signal(SIGINT, sig_handler) == SIG_ERR){&lt;br /&gt;
		puts(&amp;quot;Erreur à l&#039;enregistrement du gestionnaire de signaux !&amp;quot;);&lt;br /&gt;
	}&lt;br /&gt;
	// Boucle infinie&lt;br /&gt;
	while(end == 0);&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Voici un exemple d&#039;output:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ ./signal.bin &lt;br /&gt;
Utilisez SIGINT pour interrompre l&#039;exécution !&lt;br /&gt;
^C&lt;br /&gt;
SIGINT attrapé, on stop le programme 3630&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Magsss</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=C_semaphore&amp;diff=3272</id>
		<title>C semaphore</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=C_semaphore&amp;diff=3272"/>
		<updated>2018-11-01T18:05:04Z</updated>

		<summary type="html">&lt;p&gt;Magsss : /* Initialisation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Les sémaphores permettent de gérer ce que l&#039;on appelle les sections critiques. Il y a des parties de code où l&#039;on souhaite que toutes les instructions soient exécutées en évitant les interblocages un peu comme avec un [[C_pthread#Exclusion_mutuelle | mutex]].&lt;br /&gt;
&lt;br /&gt;
On peut se demander quelle est la différence avec un [[C_pthread#Exclusion_mutuelle | mutex]] ?&lt;br /&gt;
&lt;br /&gt;
Un [[C_pthread#Exclusion_mutuelle | mutex]] est un mécanisme de verrouillage utilisé pour synchroniser l&#039;accès à une ressource alors qu&#039;un sémaphore est un mécanisme de signalisation utilisé pour sérialiser l&#039;exécution dans une portion de code. Un peu comme si les processus se passaient le mot: &amp;quot;J&#039;ai fini, tu peux y aller&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
= Utilisation=&lt;br /&gt;
== Création ==&lt;br /&gt;
Tout d&#039;abord, il faut créer un objet de type sémaphore:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;semaphore.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
sem_t semaphore;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Initialisation ==&lt;br /&gt;
Une fois l&#039;objet créé, on peut l&#039;initialiser:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;semaphore.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int sem_init(sem_t *semaphore, int pshared, unsigned int valeur);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*semaphore &amp;amp;rarr; pointeur vers le sémaphore à initialiser ;&lt;br /&gt;
*pshared &amp;amp;rarr; drapeau qui précise si le sémaphore est utilisé par des threads (valeur &#039;&#039;0&#039;&#039;) ;&lt;br /&gt;
*valeur  &amp;amp;rarr; valeur de départ du sémaphore.&lt;br /&gt;
* le code retour varie entre:&lt;br /&gt;
** &#039;0&#039; si tout s&#039;est bien passé&lt;br /&gt;
** &#039;1&#039; si une erreur survient et errno est positionné&lt;br /&gt;
&lt;br /&gt;
== Blocage ==&lt;br /&gt;
Une fois le sémaphore initialisé, on peut demander son blocage avec&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;semaphore.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int sem_wait(sem_t *semaphore);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*semaphore &amp;amp;rarr; pointeur vers le sémaphore à bloquer ;&lt;br /&gt;
* le code retour varie entre:&lt;br /&gt;
** &#039;0&#039; si tout s&#039;est bien passé&lt;br /&gt;
** &#039;-1&#039; si une erreur survient et errno est positionné&lt;br /&gt;
{|style=&amp;quot;width:350px&amp;quot; align=&amp;quot;center&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
[[Fichier:Warning-icon.png|40px]]&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
 Cette fonction est bloquante !&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;semaphore.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int sem_trywait(sem_t *semaphore);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*semaphore &amp;amp;rarr; pointeur vers le sémaphore à bloquer ;&lt;br /&gt;
* le code retour varie entre:&lt;br /&gt;
** &#039;0&#039; si tout s&#039;est bien passé&lt;br /&gt;
** &#039;-1&#039; si une erreur survient et errno est positionné&lt;br /&gt;
{|style=&amp;quot;width:380px&amp;quot; align=&amp;quot;center&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
[[Fichier:Warning-icon.png|40px]]&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
 Cette fonction est non bloquante !&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;semaphore.h&amp;gt;&lt;br /&gt;
#include &amp;lt;time.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int sem_timedwait(sem_t *semaphore, const struct timespec *abs_timeout);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*semaphore &amp;amp;rarr; pointeur vers le sémaphore à bloquer ;&lt;br /&gt;
* le code retour varie entre:&lt;br /&gt;
** &#039;0&#039; si tout s&#039;est bien passé&lt;br /&gt;
** &#039;-1&#039; si une erreur survient et errno est positionné&lt;br /&gt;
{|style=&amp;quot;width:380px&amp;quot; align=&amp;quot;center&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
[[Fichier:Warning-icon.png|40px]]&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
 Cette fonction est bloquante le temps spécifié !&lt;br /&gt;
|}&lt;br /&gt;
Voici le détail de la structure &#039;&#039;timespec&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
struct timespec {&lt;br /&gt;
   time_t tv_sec;      /* Seconds */&lt;br /&gt;
   long   tv_nsec;     /* Nanoseconds [0 .. 999999999] */&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Relâchement d&#039;un sémaphore ==&lt;br /&gt;
Une fois la section critique passée, on peut relâcher le sémaphore:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;semaphore.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int sem_post(sem_t *semaphore);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*semaphore &amp;amp;rarr; pointeur vers le sémaphore à bloquer ;&lt;br /&gt;
* le code retour varie entre:&lt;br /&gt;
** &#039;0&#039; si tout s&#039;est bien passé&lt;br /&gt;
** &#039;-1&#039; si une erreur survient et errno est positionné&lt;br /&gt;
=Exemple=&lt;br /&gt;
Ci-dessous un exemple d&#039;utilisation d&#039;un sémaphore:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/wait.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;pthread.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;semaphore.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define NB_THREAD 4&lt;br /&gt;
#define LIMIT 2&lt;br /&gt;
&lt;br /&gt;
// Création du sémaphore;&lt;br /&gt;
sem_t semaphore;&lt;br /&gt;
&lt;br /&gt;
void * job(void * args) {&lt;br /&gt;
	// Récupération de l&#039;identifiant du thread&lt;br /&gt;
	int tid = pthread_self();&lt;br /&gt;
	int i = 0;&lt;br /&gt;
	while (i &amp;lt; LIMIT) {&lt;br /&gt;
		// On attend la disponibilité du sémaphore&lt;br /&gt;
		sem_wait(&amp;amp;semaphore);&lt;br /&gt;
		// Section critique&lt;br /&gt;
		printf(&amp;quot;Je suis le thread [%i] et je vais dormir 1 seconde\n&amp;quot;, tid);&lt;br /&gt;
		sleep(1);&lt;br /&gt;
		printf(&amp;quot;Je suis le thread [%i] et j&#039;ai fini ma sieste\n&amp;quot;, tid);&lt;br /&gt;
		// On relache le sémaphore&lt;br /&gt;
		sem_post(&amp;amp;semaphore);&lt;br /&gt;
		i++;&lt;br /&gt;
	}&lt;br /&gt;
	pthread_exit(EXIT_SUCCESS);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
	// Création d&#039;un tableau de thread&lt;br /&gt;
	pthread_t threads[NB_THREAD];&lt;br /&gt;
	// Initialisation du sémaphore&lt;br /&gt;
	sem_init(&amp;amp;semaphore, PTHREAD_PROCESS_SHARED, 1);&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_THREAD; i++) {&lt;br /&gt;
		int err;&lt;br /&gt;
		if ((err = pthread_create(&amp;amp;threads[i], NULL, job, NULL)) != 0) {&lt;br /&gt;
			printf(&amp;quot;Echec de la création du thread: [%s]&amp;quot;, strerror(err));&lt;br /&gt;
			return EXIT_FAILURE;;&lt;br /&gt;
		}&lt;br /&gt;
		printf(&amp;quot;Création du thread numéro %i\n&amp;quot;, i);&lt;br /&gt;
	}&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_THREAD; i++) {&lt;br /&gt;
		pthread_join(threads[i], NULL);&lt;br /&gt;
	}&lt;br /&gt;
	sem_destroy(&amp;amp;semaphore);&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Ce programme donne, par exemple, la sortie suivante:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Création du thread numéro 0&lt;br /&gt;
Création du thread numéro 1&lt;br /&gt;
Création du thread numéro 2&lt;br /&gt;
Création du thread numéro 3&lt;br /&gt;
Je suis le thread [-1171347712] et je vais dormir 1 seconde&lt;br /&gt;
Je suis le thread [-1171347712] et j&#039;ai fini ma sieste&lt;br /&gt;
Je suis le thread [-1171347712] et je vais dormir 1 seconde&lt;br /&gt;
Je suis le thread [-1171347712] et j&#039;ai fini ma sieste&lt;br /&gt;
Je suis le thread [-1192327424] et je vais dormir 1 seconde&lt;br /&gt;
Je suis le thread [-1192327424] et j&#039;ai fini ma sieste&lt;br /&gt;
Je suis le thread [-1192327424] et je vais dormir 1 seconde&lt;br /&gt;
Je suis le thread [-1192327424] et j&#039;ai fini ma sieste&lt;br /&gt;
Je suis le thread [-1181837568] et je vais dormir 1 seconde&lt;br /&gt;
Je suis le thread [-1181837568] et j&#039;ai fini ma sieste&lt;br /&gt;
Je suis le thread [-1181837568] et je vais dormir 1 seconde&lt;br /&gt;
Je suis le thread [-1181837568] et j&#039;ai fini ma sieste&lt;br /&gt;
Je suis le thread [-1160857856] et je vais dormir 1 seconde&lt;br /&gt;
Je suis le thread [-1160857856] et j&#039;ai fini ma sieste&lt;br /&gt;
Je suis le thread [-1160857856] et je vais dormir 1 seconde&lt;br /&gt;
Je suis le thread [-1160857856] et j&#039;ai fini ma sieste&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On voit bien les threads exécuter la section critique les uns après les autres !&lt;/div&gt;</summary>
		<author><name>Magsss</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=C_semaphore&amp;diff=3271</id>
		<title>C semaphore</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=C_semaphore&amp;diff=3271"/>
		<updated>2018-11-01T18:04:21Z</updated>

		<summary type="html">&lt;p&gt;Magsss : /* Introduction */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Les sémaphores permettent de gérer ce que l&#039;on appelle les sections critiques. Il y a des parties de code où l&#039;on souhaite que toutes les instructions soient exécutées en évitant les interblocages un peu comme avec un [[C_pthread#Exclusion_mutuelle | mutex]].&lt;br /&gt;
&lt;br /&gt;
On peut se demander quelle est la différence avec un [[C_pthread#Exclusion_mutuelle | mutex]] ?&lt;br /&gt;
&lt;br /&gt;
Un [[C_pthread#Exclusion_mutuelle | mutex]] est un mécanisme de verrouillage utilisé pour synchroniser l&#039;accès à une ressource alors qu&#039;un sémaphore est un mécanisme de signalisation utilisé pour sérialiser l&#039;exécution dans une portion de code. Un peu comme si les processus se passaient le mot: &amp;quot;J&#039;ai fini, tu peux y aller&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
= Utilisation=&lt;br /&gt;
== Création ==&lt;br /&gt;
Tout d&#039;abord, il faut créer un objet de type sémaphore:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;semaphore.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
sem_t semaphore;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
== Initialisation ==&lt;br /&gt;
Une fois l&#039;objet créé, on peut l&#039;initialiser:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;semaphore.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int sem_init(sem_t *semaphore, int pshared, unsigned int valeur);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*semaphore &amp;amp;rarr; pointeur vers le sémaphore à initialiser ;&lt;br /&gt;
*pshared &amp;amp;rarr; drapeaux qui précise si le sémaphore est utilisé par des threads (valeur &#039;&#039;0&#039;&#039;) ;&lt;br /&gt;
*valeur  &amp;amp;rarr; valeur de départ du sémaphore.&lt;br /&gt;
* le code retour varie entre:&lt;br /&gt;
** &#039;0&#039; si tout s&#039;est bien passé&lt;br /&gt;
** &#039;1&#039; si une erreur survient et errno est positionné&lt;br /&gt;
== Blocage ==&lt;br /&gt;
Une fois le sémaphore initialisé, on peut demander son blocage avec&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;semaphore.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int sem_wait(sem_t *semaphore);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*semaphore &amp;amp;rarr; pointeur vers le sémaphore à bloquer ;&lt;br /&gt;
* le code retour varie entre:&lt;br /&gt;
** &#039;0&#039; si tout s&#039;est bien passé&lt;br /&gt;
** &#039;-1&#039; si une erreur survient et errno est positionné&lt;br /&gt;
{|style=&amp;quot;width:350px&amp;quot; align=&amp;quot;center&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
[[Fichier:Warning-icon.png|40px]]&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
 Cette fonction est bloquante !&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;semaphore.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int sem_trywait(sem_t *semaphore);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*semaphore &amp;amp;rarr; pointeur vers le sémaphore à bloquer ;&lt;br /&gt;
* le code retour varie entre:&lt;br /&gt;
** &#039;0&#039; si tout s&#039;est bien passé&lt;br /&gt;
** &#039;-1&#039; si une erreur survient et errno est positionné&lt;br /&gt;
{|style=&amp;quot;width:380px&amp;quot; align=&amp;quot;center&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
[[Fichier:Warning-icon.png|40px]]&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
 Cette fonction est non bloquante !&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;semaphore.h&amp;gt;&lt;br /&gt;
#include &amp;lt;time.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int sem_timedwait(sem_t *semaphore, const struct timespec *abs_timeout);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*semaphore &amp;amp;rarr; pointeur vers le sémaphore à bloquer ;&lt;br /&gt;
* le code retour varie entre:&lt;br /&gt;
** &#039;0&#039; si tout s&#039;est bien passé&lt;br /&gt;
** &#039;-1&#039; si une erreur survient et errno est positionné&lt;br /&gt;
{|style=&amp;quot;width:380px&amp;quot; align=&amp;quot;center&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
[[Fichier:Warning-icon.png|40px]]&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
 Cette fonction est bloquante le temps spécifié !&lt;br /&gt;
|}&lt;br /&gt;
Voici le détail de la structure &#039;&#039;timespec&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
struct timespec {&lt;br /&gt;
   time_t tv_sec;      /* Seconds */&lt;br /&gt;
   long   tv_nsec;     /* Nanoseconds [0 .. 999999999] */&lt;br /&gt;
};&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Relâchement d&#039;un sémaphore ==&lt;br /&gt;
Une fois la section critique passée, on peut relâcher le sémaphore:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;semaphore.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int sem_post(sem_t *semaphore);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*semaphore &amp;amp;rarr; pointeur vers le sémaphore à bloquer ;&lt;br /&gt;
* le code retour varie entre:&lt;br /&gt;
** &#039;0&#039; si tout s&#039;est bien passé&lt;br /&gt;
** &#039;-1&#039; si une erreur survient et errno est positionné&lt;br /&gt;
=Exemple=&lt;br /&gt;
Ci-dessous un exemple d&#039;utilisation d&#039;un sémaphore:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/wait.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;pthread.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
#include &amp;lt;semaphore.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
#define NB_THREAD 4&lt;br /&gt;
#define LIMIT 2&lt;br /&gt;
&lt;br /&gt;
// Création du sémaphore;&lt;br /&gt;
sem_t semaphore;&lt;br /&gt;
&lt;br /&gt;
void * job(void * args) {&lt;br /&gt;
	// Récupération de l&#039;identifiant du thread&lt;br /&gt;
	int tid = pthread_self();&lt;br /&gt;
	int i = 0;&lt;br /&gt;
	while (i &amp;lt; LIMIT) {&lt;br /&gt;
		// On attend la disponibilité du sémaphore&lt;br /&gt;
		sem_wait(&amp;amp;semaphore);&lt;br /&gt;
		// Section critique&lt;br /&gt;
		printf(&amp;quot;Je suis le thread [%i] et je vais dormir 1 seconde\n&amp;quot;, tid);&lt;br /&gt;
		sleep(1);&lt;br /&gt;
		printf(&amp;quot;Je suis le thread [%i] et j&#039;ai fini ma sieste\n&amp;quot;, tid);&lt;br /&gt;
		// On relache le sémaphore&lt;br /&gt;
		sem_post(&amp;amp;semaphore);&lt;br /&gt;
		i++;&lt;br /&gt;
	}&lt;br /&gt;
	pthread_exit(EXIT_SUCCESS);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
	// Création d&#039;un tableau de thread&lt;br /&gt;
	pthread_t threads[NB_THREAD];&lt;br /&gt;
	// Initialisation du sémaphore&lt;br /&gt;
	sem_init(&amp;amp;semaphore, PTHREAD_PROCESS_SHARED, 1);&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_THREAD; i++) {&lt;br /&gt;
		int err;&lt;br /&gt;
		if ((err = pthread_create(&amp;amp;threads[i], NULL, job, NULL)) != 0) {&lt;br /&gt;
			printf(&amp;quot;Echec de la création du thread: [%s]&amp;quot;, strerror(err));&lt;br /&gt;
			return EXIT_FAILURE;;&lt;br /&gt;
		}&lt;br /&gt;
		printf(&amp;quot;Création du thread numéro %i\n&amp;quot;, i);&lt;br /&gt;
	}&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_THREAD; i++) {&lt;br /&gt;
		pthread_join(threads[i], NULL);&lt;br /&gt;
	}&lt;br /&gt;
	sem_destroy(&amp;amp;semaphore);&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Ce programme donne, par exemple, la sortie suivante:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Création du thread numéro 0&lt;br /&gt;
Création du thread numéro 1&lt;br /&gt;
Création du thread numéro 2&lt;br /&gt;
Création du thread numéro 3&lt;br /&gt;
Je suis le thread [-1171347712] et je vais dormir 1 seconde&lt;br /&gt;
Je suis le thread [-1171347712] et j&#039;ai fini ma sieste&lt;br /&gt;
Je suis le thread [-1171347712] et je vais dormir 1 seconde&lt;br /&gt;
Je suis le thread [-1171347712] et j&#039;ai fini ma sieste&lt;br /&gt;
Je suis le thread [-1192327424] et je vais dormir 1 seconde&lt;br /&gt;
Je suis le thread [-1192327424] et j&#039;ai fini ma sieste&lt;br /&gt;
Je suis le thread [-1192327424] et je vais dormir 1 seconde&lt;br /&gt;
Je suis le thread [-1192327424] et j&#039;ai fini ma sieste&lt;br /&gt;
Je suis le thread [-1181837568] et je vais dormir 1 seconde&lt;br /&gt;
Je suis le thread [-1181837568] et j&#039;ai fini ma sieste&lt;br /&gt;
Je suis le thread [-1181837568] et je vais dormir 1 seconde&lt;br /&gt;
Je suis le thread [-1181837568] et j&#039;ai fini ma sieste&lt;br /&gt;
Je suis le thread [-1160857856] et je vais dormir 1 seconde&lt;br /&gt;
Je suis le thread [-1160857856] et j&#039;ai fini ma sieste&lt;br /&gt;
Je suis le thread [-1160857856] et je vais dormir 1 seconde&lt;br /&gt;
Je suis le thread [-1160857856] et j&#039;ai fini ma sieste&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On voit bien les threads exécuter la section critique les uns après les autres !&lt;/div&gt;</summary>
		<author><name>Magsss</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=C_pipe&amp;diff=3270</id>
		<title>C pipe</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=C_pipe&amp;diff=3270"/>
		<updated>2018-11-01T18:02:47Z</updated>

		<summary type="html">&lt;p&gt;Magsss : /* Fonctions utilisées */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Un tube est un canal par lequel les informations circulent de manière uni-directionnelle. Un processus écrit dans l&#039;entrée du tube et un autre processus lit les informations en sortie.&lt;br /&gt;
&lt;br /&gt;
= Manipulation des tubes =&lt;br /&gt;
== Création ==&lt;br /&gt;
La première étape est la création d&#039;un tube:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int tube[2];&lt;br /&gt;
&lt;br /&gt;
int pipe(int tube[2]);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*tube[0] &amp;amp;rarr; contiendra le fichier descripteur de l&#039;extrémité de lecture&lt;br /&gt;
*tube[1] &amp;amp;rarr; contiendra le fichier descripteur de l&#039;extrémité d&#039;écriture&lt;br /&gt;
*Le retour sera :&lt;br /&gt;
** &#039;0&#039; si tout s&#039;est bien passé&lt;br /&gt;
** &#039;1&#039; si une erreur survient et errno est positionné&lt;br /&gt;
&lt;br /&gt;
== Écriture ==&lt;br /&gt;
Pour écrire dans un tube :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t write(int tube[1], const void *message, size_t longueur);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*tube[0] &amp;amp;rarr; le fichier descripteur de l&#039;extrémité d&#039;écriture&lt;br /&gt;
*message &amp;amp;rarr; le message à écrire&lt;br /&gt;
*longueur &amp;amp;rarr; la longueur du message&lt;br /&gt;
*ssize_t &amp;amp;rarr; le nombre d&#039;octets écrits&lt;br /&gt;
&lt;br /&gt;
== Lecture ==&lt;br /&gt;
Pour lire dans un tube :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t read(int tube[0], void *message, size_t longueur);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*tube[1] &amp;amp;rarr; le fichier descripteur de l&#039;extrémité de lecture&lt;br /&gt;
*message &amp;amp;rarr; un tableau de caractères qui contiendra le message à lire&lt;br /&gt;
*longueur &amp;amp;rarr; la longueur du message à lire&lt;br /&gt;
*ssize_t &amp;amp;rarr; le nombre d&#039;octets lus&lt;br /&gt;
&lt;br /&gt;
==fermeture==&lt;br /&gt;
Un fois la lecture terminée on ferme le tube:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int close(int descripteur);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*descripteur : une extrémité du tube&lt;br /&gt;
&lt;br /&gt;
= Utilisation dans un contexte d&#039;exécution parallélisé =&lt;br /&gt;
== Utilisation uni-directionnelle ==&lt;br /&gt;
Ci-dessous un exemple qui permet au père de communiquer avec ses fils:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/wait.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Nombre total de thread&lt;br /&gt;
#define NB_FORK 2&lt;br /&gt;
// Taille du message&lt;br /&gt;
#define LENGTH_MSG 30&lt;br /&gt;
// Tableau contenant le message&lt;br /&gt;
char message[LENGTH_MSG] = &amp;quot;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// Fonction exécutée dans le fork&lt;br /&gt;
void job(int * tube) {&lt;br /&gt;
	int tid = getpid();&lt;br /&gt;
	// timer pour attendre maximum 5 secondes&lt;br /&gt;
	int i = 5;&lt;br /&gt;
	while (i &amp;gt; 0) {&lt;br /&gt;
		// lecture dans le tube&lt;br /&gt;
		if (read(*tube, message, LENGTH_MSG) &amp;gt; 0) {&lt;br /&gt;
			printf(&amp;quot;Message du processus [%i] : %s&amp;quot;, tid, message);&lt;br /&gt;
			break;&lt;br /&gt;
		}&lt;br /&gt;
		sleep(1);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// Fonction qui attend chacun des processus fils&lt;br /&gt;
void waitForAll() {&lt;br /&gt;
	int status;&lt;br /&gt;
	pid_t pid;&lt;br /&gt;
	int n = 0;&lt;br /&gt;
	while (n &amp;lt; NB_FORK) {&lt;br /&gt;
		pid = wait(&amp;amp;status);&lt;br /&gt;
		n++;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_FORK; i++) {&lt;br /&gt;
		int tube[2];&lt;br /&gt;
		pipe(tube);&lt;br /&gt;
		pid_t pid = fork();&lt;br /&gt;
		if (pid == -1) {&lt;br /&gt;
			// Il y a une erreur&lt;br /&gt;
			perror(&amp;quot;fork&amp;quot;);&lt;br /&gt;
			return EXIT_FAILURE;&lt;br /&gt;
		} else if (pid == 0) {&lt;br /&gt;
			// On est dans le fils&lt;br /&gt;
			close(tube[1]);&lt;br /&gt;
			job(&amp;amp;tube[0]);&lt;br /&gt;
			close(tube[0]);&lt;br /&gt;
			exit(EXIT_SUCCESS);&lt;br /&gt;
		} else {&lt;br /&gt;
			// On est dans le père&lt;br /&gt;
			// Ecriture du message dans le tableau&lt;br /&gt;
			sprintf(message, &amp;quot;Fork [%i], je suis ton père !\n&amp;quot;, pid);&lt;br /&gt;
			// Ecriture du message dans le tube&lt;br /&gt;
			write(tube[1], message, LENGTH_MSG);&lt;br /&gt;
			close(tube[0]);&lt;br /&gt;
			close(tube[1]);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	waitForAll();&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Utilisation bidirectionnelle ==&lt;br /&gt;
Dans cet exemple, on crée une structure qui va contenir les deux tubes pour plus de facilité:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/wait.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Structure avec des tubes full-duplex&lt;br /&gt;
typedef struct fdpipe {&lt;br /&gt;
	int father[2];&lt;br /&gt;
	int son[2];&lt;br /&gt;
} fdpipe;&lt;br /&gt;
&lt;br /&gt;
// Nombre total de thread&lt;br /&gt;
#define NB_FORK 10&lt;br /&gt;
// Taille du message&lt;br /&gt;
#define LENGTH_MSG 50&lt;br /&gt;
// Tableau contenant le message&lt;br /&gt;
char message[LENGTH_MSG] = &amp;quot;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// Fonction exécutée dans le fork&lt;br /&gt;
void job(fdpipe * pipes) {&lt;br /&gt;
	int tid = getpid();&lt;br /&gt;
	// timer pour attendre maximum 5 secondes&lt;br /&gt;
	int i = 5;&lt;br /&gt;
	while (i &amp;gt; 0) {&lt;br /&gt;
		// lecture dans le tube&lt;br /&gt;
		if (read((*pipes).father[0], message, LENGTH_MSG) &amp;gt; 0) {&lt;br /&gt;
			printf(&amp;quot;Message du processus [%i] : %s\n&amp;quot;, tid, message);&lt;br /&gt;
			// Ecriture du message dans le tableau&lt;br /&gt;
			sprintf(message, &amp;quot;je suis [%i] et j&#039;ai bien reçu ton message !\n&amp;quot;, tid);&lt;br /&gt;
			write((*pipes).son[1], message, LENGTH_MSG);&lt;br /&gt;
			break;&lt;br /&gt;
		}&lt;br /&gt;
		sleep(1);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// Fonction qui attend chacun des processus fils&lt;br /&gt;
void waitForAll() {&lt;br /&gt;
	int status;&lt;br /&gt;
	pid_t pid;&lt;br /&gt;
	int n = 0;&lt;br /&gt;
	while (n &amp;lt; NB_FORK) {&lt;br /&gt;
		pid = wait(&amp;amp;status);&lt;br /&gt;
		n++;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_FORK; i++) {&lt;br /&gt;
		fdpipe pipes;&lt;br /&gt;
		pipe(pipes.father);&lt;br /&gt;
		pipe(pipes.son);&lt;br /&gt;
		pid_t pid = fork();&lt;br /&gt;
		if (pid == -1) {&lt;br /&gt;
			// Il y a une erreur&lt;br /&gt;
			perror(&amp;quot;fork&amp;quot;);&lt;br /&gt;
			return EXIT_FAILURE;&lt;br /&gt;
		} else if (pid == 0) {&lt;br /&gt;
			// On est dans le fils&lt;br /&gt;
			close(pipes.father[1]);&lt;br /&gt;
			job(&amp;amp;pipes);&lt;br /&gt;
			close(pipes.father[0]);&lt;br /&gt;
			close(pipes.son[0]);&lt;br /&gt;
			close(pipes.son[1]);&lt;br /&gt;
			exit(EXIT_SUCCESS);&lt;br /&gt;
		} else {&lt;br /&gt;
			// On est dans le père&lt;br /&gt;
			// Ecriture du message dans le tableau&lt;br /&gt;
			sprintf(message, &amp;quot;Fork [%i], je suis ton père !\n&amp;quot;, pid);&lt;br /&gt;
			// Ecriture du message dans le tube&lt;br /&gt;
			write(pipes.father[1], message, LENGTH_MSG);&lt;br /&gt;
			while (i &amp;gt; 0) {&lt;br /&gt;
				// lecture dans le tube&lt;br /&gt;
				if (read(pipes.son[0], message, LENGTH_MSG) &amp;gt; 0) {&lt;br /&gt;
					printf(&amp;quot;Réponse du fils : %s\n&amp;quot;, message);&lt;br /&gt;
					break;&lt;br /&gt;
				}&lt;br /&gt;
				sleep(1);&lt;br /&gt;
			}&lt;br /&gt;
			close(pipes.father[1]);&lt;br /&gt;
			close(pipes.father[0]);&lt;br /&gt;
			close(pipes.son[0]);&lt;br /&gt;
			close(pipes.son[1]);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	waitForAll();&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Ce qui nous donne le résultat suivant :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Message du processus [18523] : Fork [18523], je suis ton père !&lt;br /&gt;
Réponse du fils : je suis [18523] et j&#039;ai bien reçu ton message !&lt;br /&gt;
Message du processus [18522] : Fork [18522], je suis ton père !&lt;br /&gt;
Message du processus [18524] : Fork [18524], je suis ton père !&lt;br /&gt;
Réponse du fils : je suis [18524] et j&#039;ai bien reçu ton message !&lt;br /&gt;
Message du processus [18525] : Fork [18525], je suis ton père !&lt;br /&gt;
Réponse du fils : je suis [18525] et j&#039;ai bien reçu ton message !&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Tube nommé =&lt;br /&gt;
== Fonctions utilisées ==&lt;br /&gt;
Il est possible de nommer un tube pour pouvoir y accéder depuis un autre processus. Pour ce faire nous allons utiliser les fonctions suivantes :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/stat.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int mkfifo (const char* name, mode_t mode);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* name est le nom du tube ; &lt;br /&gt;
* mode correspond aux permissions du tube (idem &#039;&#039;chmod&#039;&#039;) ;&lt;br /&gt;
* le code retour de la fonction varie entre : &lt;br /&gt;
** EACCES : droits insuffisants pour créer le tube ;&lt;br /&gt;
** EEXIST : un tube de même nom existe déjà ;&lt;br /&gt;
** ENAMETOOLONG : nom trop long ;&lt;br /&gt;
** ENOENT : le chemin n&#039;existe pas ;&lt;br /&gt;
** ENOSPC : espace insuffisant sur le système de fichiers.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int open (const char* name, int options);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* name est le nom du tube ; &lt;br /&gt;
* options permet de désigner l&#039;extrémité du tube:&lt;br /&gt;
** O_WRONLY: pour l&#039;entrée ;&lt;br /&gt;
** O_RDONLY: pour la sortie.&lt;br /&gt;
* le retour est le descripteur pour la lecture ou l&#039;écriture ou -1 en cas d&#039;échec.&lt;br /&gt;
&lt;br /&gt;
== Exemple d&#039;utilisation ==&lt;br /&gt;
Ci-dessous un exemple d&#039;utilisation avec un thread qui lit dans un tube nommé:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/wait.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;pthread.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/stat.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Nom du tube&lt;br /&gt;
#define FIFO_NAME &amp;quot;/tmp/test.fifo&amp;quot;&lt;br /&gt;
// Longeur du buffer de lecture&lt;br /&gt;
#define BUFFER_LENGTH 30;&lt;br /&gt;
// Mode de lecture du tube&lt;br /&gt;
const mode_t FIFO_MODE = 0760;&lt;br /&gt;
&lt;br /&gt;
// Fonction exécutée par le thread&lt;br /&gt;
void * job(void * args) {&lt;br /&gt;
	// Récupération du descripteur en lecture&lt;br /&gt;
	int fdread;&lt;br /&gt;
	if ((fdread = open(FIFO_NAME, O_RDONLY)) == -1) {&lt;br /&gt;
		fprintf(stderr, &amp;quot;Impossible d&#039;ouvrir le tube en lecture: %s\n&amp;quot;,&lt;br /&gt;
				strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	// Création d&#039;un tampon pour stocker le contenu du tube&lt;br /&gt;
	char buffer[30];&lt;br /&gt;
	// Lecture du contenu du tube&lt;br /&gt;
	read(fdread, buffer, 30);&lt;br /&gt;
	// Affichage du contenu&lt;br /&gt;
	printf(&amp;quot;Le tube contient : %s&amp;quot;, buffer);&lt;br /&gt;
	// Fin de l&#039;exécution du thread&lt;br /&gt;
	pthread_exit(EXIT_SUCCESS);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
	// On supprime le tube s&#039;il existe déjà&lt;br /&gt;
	if (mkfifo(FIFO_NAME, FIFO_MODE) == -1) {&lt;br /&gt;
		printf(&amp;quot;Suppression du tube existant: %s\n&amp;quot;, FIFO_NAME);&lt;br /&gt;
		unlink(FIFO_NAME);&lt;br /&gt;
	}&lt;br /&gt;
	// Création de la variable qui va contenir le thread&lt;br /&gt;
	pthread_t thread;&lt;br /&gt;
	// Création du tube FIFO&lt;br /&gt;
	if (mkfifo(FIFO_NAME, FIFO_MODE) == -1) {&lt;br /&gt;
		printf(&amp;quot;Erreur de création du tube: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}else{&lt;br /&gt;
		printf(&amp;quot;Création du tube: %s\n&amp;quot;, FIFO_NAME);&lt;br /&gt;
	}&lt;br /&gt;
	// Création du thread&lt;br /&gt;
	pthread_create(&amp;amp;thread, NULL, job, NULL);&lt;br /&gt;
	// Récupération du descripteur en écriture&lt;br /&gt;
	int fdwrite;&lt;br /&gt;
	if ((fdwrite = open(FIFO_NAME, O_WRONLY)) == -1) {&lt;br /&gt;
		printf(&amp;quot;Impossible d&#039;ouvrir le tube en écriture: %s\n&amp;quot;,&lt;br /&gt;
				strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	// Ecriture dans le tube&lt;br /&gt;
	char message[] = &amp;quot;Bonjour thread&amp;quot;;&lt;br /&gt;
	write(fdwrite,  message, sizeof(message));&lt;br /&gt;
	// En attente de l&#039;éxécution du thread&lt;br /&gt;
	pthread_join(thread, NULL);&lt;br /&gt;
	// Fin de l&#039;exécution&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
{|style=&amp;quot;width:800px&amp;quot; align=&amp;quot;center&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
[[Fichier:Warning manual.jpg|centré|300px]]&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
Dans l&#039;exemple précédent, vous remarquerez que le thread est créé avant l&#039;ouverture du tube. C&#039;est absolument important de démarrer le thread avant, car la fonction &#039;&#039;open&#039;&#039; va bloquer le fil d&#039;exécution principal tant que le thread n&#039;ouvre pas le tube en lecture ! Pour eviter cela il faut utiliser l&#039;option &#039;&#039;&#039;O_NONBLOCK&#039;&#039;&#039;&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Magsss</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=C_pipe&amp;diff=3269</id>
		<title>C pipe</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=C_pipe&amp;diff=3269"/>
		<updated>2018-11-01T18:01:44Z</updated>

		<summary type="html">&lt;p&gt;Magsss : /* Utilisation bidirectionnel */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Un tube est un canal par lequel les informations circulent de manière uni-directionnelle. Un processus écrit dans l&#039;entrée du tube et un autre processus lit les informations en sortie.&lt;br /&gt;
&lt;br /&gt;
= Manipulation des tubes =&lt;br /&gt;
== Création ==&lt;br /&gt;
La première étape est la création d&#039;un tube:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int tube[2];&lt;br /&gt;
&lt;br /&gt;
int pipe(int tube[2]);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*tube[0] &amp;amp;rarr; contiendra le fichier descripteur de l&#039;extrémité de lecture&lt;br /&gt;
*tube[1] &amp;amp;rarr; contiendra le fichier descripteur de l&#039;extrémité d&#039;écriture&lt;br /&gt;
*Le retour sera :&lt;br /&gt;
** &#039;0&#039; si tout s&#039;est bien passé&lt;br /&gt;
** &#039;1&#039; si une erreur survient et errno est positionné&lt;br /&gt;
&lt;br /&gt;
== Écriture ==&lt;br /&gt;
Pour écrire dans un tube :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t write(int tube[1], const void *message, size_t longueur);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*tube[0] &amp;amp;rarr; le fichier descripteur de l&#039;extrémité d&#039;écriture&lt;br /&gt;
*message &amp;amp;rarr; le message à écrire&lt;br /&gt;
*longueur &amp;amp;rarr; la longueur du message&lt;br /&gt;
*ssize_t &amp;amp;rarr; le nombre d&#039;octets écrits&lt;br /&gt;
&lt;br /&gt;
== Lecture ==&lt;br /&gt;
Pour lire dans un tube :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t read(int tube[0], void *message, size_t longueur);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*tube[1] &amp;amp;rarr; le fichier descripteur de l&#039;extrémité de lecture&lt;br /&gt;
*message &amp;amp;rarr; un tableau de caractères qui contiendra le message à lire&lt;br /&gt;
*longueur &amp;amp;rarr; la longueur du message à lire&lt;br /&gt;
*ssize_t &amp;amp;rarr; le nombre d&#039;octets lus&lt;br /&gt;
&lt;br /&gt;
==fermeture==&lt;br /&gt;
Un fois la lecture terminée on ferme le tube:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int close(int descripteur);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*descripteur : une extrémité du tube&lt;br /&gt;
&lt;br /&gt;
= Utilisation dans un contexte d&#039;exécution parallélisé =&lt;br /&gt;
== Utilisation uni-directionnelle ==&lt;br /&gt;
Ci-dessous un exemple qui permet au père de communiquer avec ses fils:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/wait.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Nombre total de thread&lt;br /&gt;
#define NB_FORK 2&lt;br /&gt;
// Taille du message&lt;br /&gt;
#define LENGTH_MSG 30&lt;br /&gt;
// Tableau contenant le message&lt;br /&gt;
char message[LENGTH_MSG] = &amp;quot;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// Fonction exécutée dans le fork&lt;br /&gt;
void job(int * tube) {&lt;br /&gt;
	int tid = getpid();&lt;br /&gt;
	// timer pour attendre maximum 5 secondes&lt;br /&gt;
	int i = 5;&lt;br /&gt;
	while (i &amp;gt; 0) {&lt;br /&gt;
		// lecture dans le tube&lt;br /&gt;
		if (read(*tube, message, LENGTH_MSG) &amp;gt; 0) {&lt;br /&gt;
			printf(&amp;quot;Message du processus [%i] : %s&amp;quot;, tid, message);&lt;br /&gt;
			break;&lt;br /&gt;
		}&lt;br /&gt;
		sleep(1);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// Fonction qui attend chacun des processus fils&lt;br /&gt;
void waitForAll() {&lt;br /&gt;
	int status;&lt;br /&gt;
	pid_t pid;&lt;br /&gt;
	int n = 0;&lt;br /&gt;
	while (n &amp;lt; NB_FORK) {&lt;br /&gt;
		pid = wait(&amp;amp;status);&lt;br /&gt;
		n++;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_FORK; i++) {&lt;br /&gt;
		int tube[2];&lt;br /&gt;
		pipe(tube);&lt;br /&gt;
		pid_t pid = fork();&lt;br /&gt;
		if (pid == -1) {&lt;br /&gt;
			// Il y a une erreur&lt;br /&gt;
			perror(&amp;quot;fork&amp;quot;);&lt;br /&gt;
			return EXIT_FAILURE;&lt;br /&gt;
		} else if (pid == 0) {&lt;br /&gt;
			// On est dans le fils&lt;br /&gt;
			close(tube[1]);&lt;br /&gt;
			job(&amp;amp;tube[0]);&lt;br /&gt;
			close(tube[0]);&lt;br /&gt;
			exit(EXIT_SUCCESS);&lt;br /&gt;
		} else {&lt;br /&gt;
			// On est dans le père&lt;br /&gt;
			// Ecriture du message dans le tableau&lt;br /&gt;
			sprintf(message, &amp;quot;Fork [%i], je suis ton père !\n&amp;quot;, pid);&lt;br /&gt;
			// Ecriture du message dans le tube&lt;br /&gt;
			write(tube[1], message, LENGTH_MSG);&lt;br /&gt;
			close(tube[0]);&lt;br /&gt;
			close(tube[1]);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	waitForAll();&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Utilisation bidirectionnelle ==&lt;br /&gt;
Dans cet exemple, on crée une structure qui va contenir les deux tubes pour plus de facilité:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/wait.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Structure avec des tubes full-duplex&lt;br /&gt;
typedef struct fdpipe {&lt;br /&gt;
	int father[2];&lt;br /&gt;
	int son[2];&lt;br /&gt;
} fdpipe;&lt;br /&gt;
&lt;br /&gt;
// Nombre total de thread&lt;br /&gt;
#define NB_FORK 10&lt;br /&gt;
// Taille du message&lt;br /&gt;
#define LENGTH_MSG 50&lt;br /&gt;
// Tableau contenant le message&lt;br /&gt;
char message[LENGTH_MSG] = &amp;quot;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// Fonction exécutée dans le fork&lt;br /&gt;
void job(fdpipe * pipes) {&lt;br /&gt;
	int tid = getpid();&lt;br /&gt;
	// timer pour attendre maximum 5 secondes&lt;br /&gt;
	int i = 5;&lt;br /&gt;
	while (i &amp;gt; 0) {&lt;br /&gt;
		// lecture dans le tube&lt;br /&gt;
		if (read((*pipes).father[0], message, LENGTH_MSG) &amp;gt; 0) {&lt;br /&gt;
			printf(&amp;quot;Message du processus [%i] : %s\n&amp;quot;, tid, message);&lt;br /&gt;
			// Ecriture du message dans le tableau&lt;br /&gt;
			sprintf(message, &amp;quot;je suis [%i] et j&#039;ai bien reçu ton message !\n&amp;quot;, tid);&lt;br /&gt;
			write((*pipes).son[1], message, LENGTH_MSG);&lt;br /&gt;
			break;&lt;br /&gt;
		}&lt;br /&gt;
		sleep(1);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// Fonction qui attend chacun des processus fils&lt;br /&gt;
void waitForAll() {&lt;br /&gt;
	int status;&lt;br /&gt;
	pid_t pid;&lt;br /&gt;
	int n = 0;&lt;br /&gt;
	while (n &amp;lt; NB_FORK) {&lt;br /&gt;
		pid = wait(&amp;amp;status);&lt;br /&gt;
		n++;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_FORK; i++) {&lt;br /&gt;
		fdpipe pipes;&lt;br /&gt;
		pipe(pipes.father);&lt;br /&gt;
		pipe(pipes.son);&lt;br /&gt;
		pid_t pid = fork();&lt;br /&gt;
		if (pid == -1) {&lt;br /&gt;
			// Il y a une erreur&lt;br /&gt;
			perror(&amp;quot;fork&amp;quot;);&lt;br /&gt;
			return EXIT_FAILURE;&lt;br /&gt;
		} else if (pid == 0) {&lt;br /&gt;
			// On est dans le fils&lt;br /&gt;
			close(pipes.father[1]);&lt;br /&gt;
			job(&amp;amp;pipes);&lt;br /&gt;
			close(pipes.father[0]);&lt;br /&gt;
			close(pipes.son[0]);&lt;br /&gt;
			close(pipes.son[1]);&lt;br /&gt;
			exit(EXIT_SUCCESS);&lt;br /&gt;
		} else {&lt;br /&gt;
			// On est dans le père&lt;br /&gt;
			// Ecriture du message dans le tableau&lt;br /&gt;
			sprintf(message, &amp;quot;Fork [%i], je suis ton père !\n&amp;quot;, pid);&lt;br /&gt;
			// Ecriture du message dans le tube&lt;br /&gt;
			write(pipes.father[1], message, LENGTH_MSG);&lt;br /&gt;
			while (i &amp;gt; 0) {&lt;br /&gt;
				// lecture dans le tube&lt;br /&gt;
				if (read(pipes.son[0], message, LENGTH_MSG) &amp;gt; 0) {&lt;br /&gt;
					printf(&amp;quot;Réponse du fils : %s\n&amp;quot;, message);&lt;br /&gt;
					break;&lt;br /&gt;
				}&lt;br /&gt;
				sleep(1);&lt;br /&gt;
			}&lt;br /&gt;
			close(pipes.father[1]);&lt;br /&gt;
			close(pipes.father[0]);&lt;br /&gt;
			close(pipes.son[0]);&lt;br /&gt;
			close(pipes.son[1]);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	waitForAll();&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Ce qui nous donne le résultat suivant :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Message du processus [18523] : Fork [18523], je suis ton père !&lt;br /&gt;
Réponse du fils : je suis [18523] et j&#039;ai bien reçu ton message !&lt;br /&gt;
Message du processus [18522] : Fork [18522], je suis ton père !&lt;br /&gt;
Message du processus [18524] : Fork [18524], je suis ton père !&lt;br /&gt;
Réponse du fils : je suis [18524] et j&#039;ai bien reçu ton message !&lt;br /&gt;
Message du processus [18525] : Fork [18525], je suis ton père !&lt;br /&gt;
Réponse du fils : je suis [18525] et j&#039;ai bien reçu ton message !&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Tube nommé =&lt;br /&gt;
== Fonctions utilisées ==&lt;br /&gt;
Il est possible de nommer un tube pour pouvoir y accéder depuis un autre processus. Pour ce faire nous allons utiliser les fonctions suivantes :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/stat.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int mkfifo (const char* name, mode_t mode);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* name est le nom du tube ; &lt;br /&gt;
* mode correspond aux permission du tube (idem &#039;&#039;chmod&#039;&#039;) ;&lt;br /&gt;
* le code retour de la fonction varie entre : &lt;br /&gt;
** EACCES : droits insuffisants pour créer le tube ;&lt;br /&gt;
** EEXIST : un tube de même nom existe déjà ;&lt;br /&gt;
** ENAMETOOLONG : nom trop long ;&lt;br /&gt;
** ENOENT : le chemin n&#039;existe pas ;&lt;br /&gt;
** ENOSPC : espace insuffisant sur le système de fichiers.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int open (const char* name, int options);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* name est le nom du tube ; &lt;br /&gt;
* options permet de désigner l&#039;extrémité du tube:&lt;br /&gt;
** O_WRONLY: pour l&#039;entrée ;&lt;br /&gt;
** O_RDONLY: pour la sortie.&lt;br /&gt;
* le retour est le descripteur pour la lecture ou l&#039;écriture ou -1 en cas d&#039;échec.&lt;br /&gt;
&lt;br /&gt;
== Exemple d&#039;utilisation ==&lt;br /&gt;
Ci-dessous un exemple d&#039;utilisation avec un thread qui lit dans un tube nommé:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/wait.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;pthread.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/stat.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Nom du tube&lt;br /&gt;
#define FIFO_NAME &amp;quot;/tmp/test.fifo&amp;quot;&lt;br /&gt;
// Longeur du buffer de lecture&lt;br /&gt;
#define BUFFER_LENGTH 30;&lt;br /&gt;
// Mode de lecture du tube&lt;br /&gt;
const mode_t FIFO_MODE = 0760;&lt;br /&gt;
&lt;br /&gt;
// Fonction exécutée par le thread&lt;br /&gt;
void * job(void * args) {&lt;br /&gt;
	// Récupération du descripteur en lecture&lt;br /&gt;
	int fdread;&lt;br /&gt;
	if ((fdread = open(FIFO_NAME, O_RDONLY)) == -1) {&lt;br /&gt;
		fprintf(stderr, &amp;quot;Impossible d&#039;ouvrir le tube en lecture: %s\n&amp;quot;,&lt;br /&gt;
				strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	// Création d&#039;un tampon pour stocker le contenu du tube&lt;br /&gt;
	char buffer[30];&lt;br /&gt;
	// Lecture du contenu du tube&lt;br /&gt;
	read(fdread, buffer, 30);&lt;br /&gt;
	// Affichage du contenu&lt;br /&gt;
	printf(&amp;quot;Le tube contient : %s&amp;quot;, buffer);&lt;br /&gt;
	// Fin de l&#039;exécution du thread&lt;br /&gt;
	pthread_exit(EXIT_SUCCESS);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
	// On supprime le tube s&#039;il existe déjà&lt;br /&gt;
	if (mkfifo(FIFO_NAME, FIFO_MODE) == -1) {&lt;br /&gt;
		printf(&amp;quot;Suppression du tube existant: %s\n&amp;quot;, FIFO_NAME);&lt;br /&gt;
		unlink(FIFO_NAME);&lt;br /&gt;
	}&lt;br /&gt;
	// Création de la variable qui va contenir le thread&lt;br /&gt;
	pthread_t thread;&lt;br /&gt;
	// Création du tube FIFO&lt;br /&gt;
	if (mkfifo(FIFO_NAME, FIFO_MODE) == -1) {&lt;br /&gt;
		printf(&amp;quot;Erreur de création du tube: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}else{&lt;br /&gt;
		printf(&amp;quot;Création du tube: %s\n&amp;quot;, FIFO_NAME);&lt;br /&gt;
	}&lt;br /&gt;
	// Création du thread&lt;br /&gt;
	pthread_create(&amp;amp;thread, NULL, job, NULL);&lt;br /&gt;
	// Récupération du descripteur en écriture&lt;br /&gt;
	int fdwrite;&lt;br /&gt;
	if ((fdwrite = open(FIFO_NAME, O_WRONLY)) == -1) {&lt;br /&gt;
		printf(&amp;quot;Impossible d&#039;ouvrir le tube en écriture: %s\n&amp;quot;,&lt;br /&gt;
				strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	// Ecriture dans le tube&lt;br /&gt;
	char message[] = &amp;quot;Bonjour thread&amp;quot;;&lt;br /&gt;
	write(fdwrite,  message, sizeof(message));&lt;br /&gt;
	// En attente de l&#039;éxécution du thread&lt;br /&gt;
	pthread_join(thread, NULL);&lt;br /&gt;
	// Fin de l&#039;exécution&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
{|style=&amp;quot;width:800px&amp;quot; align=&amp;quot;center&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
[[Fichier:Warning manual.jpg|centré|300px]]&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
Dans l&#039;exemple précédent, vous remarquerez que le thread est créé avant l&#039;ouverture du tube. C&#039;est absolument important de démarrer le thread avant, car la fonction &#039;&#039;open&#039;&#039; va bloquer le fil d&#039;exécution principal tant que le thread n&#039;ouvre pas le tube en lecture ! Pour eviter cela il faut utiliser l&#039;option &#039;&#039;&#039;O_NONBLOCK&#039;&#039;&#039;&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Magsss</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=C_pipe&amp;diff=3268</id>
		<title>C pipe</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=C_pipe&amp;diff=3268"/>
		<updated>2018-11-01T18:01:21Z</updated>

		<summary type="html">&lt;p&gt;Magsss : /* Utilisation uni-directionnelle */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Un tube est un canal par lequel les informations circulent de manière uni-directionnelle. Un processus écrit dans l&#039;entrée du tube et un autre processus lit les informations en sortie.&lt;br /&gt;
&lt;br /&gt;
= Manipulation des tubes =&lt;br /&gt;
== Création ==&lt;br /&gt;
La première étape est la création d&#039;un tube:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int tube[2];&lt;br /&gt;
&lt;br /&gt;
int pipe(int tube[2]);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*tube[0] &amp;amp;rarr; contiendra le fichier descripteur de l&#039;extrémité de lecture&lt;br /&gt;
*tube[1] &amp;amp;rarr; contiendra le fichier descripteur de l&#039;extrémité d&#039;écriture&lt;br /&gt;
*Le retour sera :&lt;br /&gt;
** &#039;0&#039; si tout s&#039;est bien passé&lt;br /&gt;
** &#039;1&#039; si une erreur survient et errno est positionné&lt;br /&gt;
&lt;br /&gt;
== Écriture ==&lt;br /&gt;
Pour écrire dans un tube :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t write(int tube[1], const void *message, size_t longueur);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*tube[0] &amp;amp;rarr; le fichier descripteur de l&#039;extrémité d&#039;écriture&lt;br /&gt;
*message &amp;amp;rarr; le message à écrire&lt;br /&gt;
*longueur &amp;amp;rarr; la longueur du message&lt;br /&gt;
*ssize_t &amp;amp;rarr; le nombre d&#039;octets écrits&lt;br /&gt;
&lt;br /&gt;
== Lecture ==&lt;br /&gt;
Pour lire dans un tube :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t read(int tube[0], void *message, size_t longueur);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*tube[1] &amp;amp;rarr; le fichier descripteur de l&#039;extrémité de lecture&lt;br /&gt;
*message &amp;amp;rarr; un tableau de caractères qui contiendra le message à lire&lt;br /&gt;
*longueur &amp;amp;rarr; la longueur du message à lire&lt;br /&gt;
*ssize_t &amp;amp;rarr; le nombre d&#039;octets lus&lt;br /&gt;
&lt;br /&gt;
==fermeture==&lt;br /&gt;
Un fois la lecture terminée on ferme le tube:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int close(int descripteur);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*descripteur : une extrémité du tube&lt;br /&gt;
&lt;br /&gt;
= Utilisation dans un contexte d&#039;exécution parallélisé =&lt;br /&gt;
== Utilisation uni-directionnelle ==&lt;br /&gt;
Ci-dessous un exemple qui permet au père de communiquer avec ses fils:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/wait.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Nombre total de thread&lt;br /&gt;
#define NB_FORK 2&lt;br /&gt;
// Taille du message&lt;br /&gt;
#define LENGTH_MSG 30&lt;br /&gt;
// Tableau contenant le message&lt;br /&gt;
char message[LENGTH_MSG] = &amp;quot;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// Fonction exécutée dans le fork&lt;br /&gt;
void job(int * tube) {&lt;br /&gt;
	int tid = getpid();&lt;br /&gt;
	// timer pour attendre maximum 5 secondes&lt;br /&gt;
	int i = 5;&lt;br /&gt;
	while (i &amp;gt; 0) {&lt;br /&gt;
		// lecture dans le tube&lt;br /&gt;
		if (read(*tube, message, LENGTH_MSG) &amp;gt; 0) {&lt;br /&gt;
			printf(&amp;quot;Message du processus [%i] : %s&amp;quot;, tid, message);&lt;br /&gt;
			break;&lt;br /&gt;
		}&lt;br /&gt;
		sleep(1);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// Fonction qui attend chacun des processus fils&lt;br /&gt;
void waitForAll() {&lt;br /&gt;
	int status;&lt;br /&gt;
	pid_t pid;&lt;br /&gt;
	int n = 0;&lt;br /&gt;
	while (n &amp;lt; NB_FORK) {&lt;br /&gt;
		pid = wait(&amp;amp;status);&lt;br /&gt;
		n++;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_FORK; i++) {&lt;br /&gt;
		int tube[2];&lt;br /&gt;
		pipe(tube);&lt;br /&gt;
		pid_t pid = fork();&lt;br /&gt;
		if (pid == -1) {&lt;br /&gt;
			// Il y a une erreur&lt;br /&gt;
			perror(&amp;quot;fork&amp;quot;);&lt;br /&gt;
			return EXIT_FAILURE;&lt;br /&gt;
		} else if (pid == 0) {&lt;br /&gt;
			// On est dans le fils&lt;br /&gt;
			close(tube[1]);&lt;br /&gt;
			job(&amp;amp;tube[0]);&lt;br /&gt;
			close(tube[0]);&lt;br /&gt;
			exit(EXIT_SUCCESS);&lt;br /&gt;
		} else {&lt;br /&gt;
			// On est dans le père&lt;br /&gt;
			// Ecriture du message dans le tableau&lt;br /&gt;
			sprintf(message, &amp;quot;Fork [%i], je suis ton père !\n&amp;quot;, pid);&lt;br /&gt;
			// Ecriture du message dans le tube&lt;br /&gt;
			write(tube[1], message, LENGTH_MSG);&lt;br /&gt;
			close(tube[0]);&lt;br /&gt;
			close(tube[1]);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	waitForAll();&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Utilisation bidirectionnel ==&lt;br /&gt;
Dans cette exemple, on créer une structure qui va contenir les deux tubes pour plus de facilité:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/wait.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Structure avec des tubes full-duplex&lt;br /&gt;
typedef struct fdpipe {&lt;br /&gt;
	int father[2];&lt;br /&gt;
	int son[2];&lt;br /&gt;
} fdpipe;&lt;br /&gt;
&lt;br /&gt;
// Nombre total de thread&lt;br /&gt;
#define NB_FORK 10&lt;br /&gt;
// Taille du message&lt;br /&gt;
#define LENGTH_MSG 50&lt;br /&gt;
// Tableau contenant le message&lt;br /&gt;
char message[LENGTH_MSG] = &amp;quot;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// Fonction exécutée dans le fork&lt;br /&gt;
void job(fdpipe * pipes) {&lt;br /&gt;
	int tid = getpid();&lt;br /&gt;
	// timer pour attendre maximum 5 secondes&lt;br /&gt;
	int i = 5;&lt;br /&gt;
	while (i &amp;gt; 0) {&lt;br /&gt;
		// lecture dans le tube&lt;br /&gt;
		if (read((*pipes).father[0], message, LENGTH_MSG) &amp;gt; 0) {&lt;br /&gt;
			printf(&amp;quot;Message du processus [%i] : %s\n&amp;quot;, tid, message);&lt;br /&gt;
			// Ecriture du message dans le tableau&lt;br /&gt;
			sprintf(message, &amp;quot;je suis [%i] et j&#039;ai bien reçu ton message !\n&amp;quot;, tid);&lt;br /&gt;
			write((*pipes).son[1], message, LENGTH_MSG);&lt;br /&gt;
			break;&lt;br /&gt;
		}&lt;br /&gt;
		sleep(1);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// Fonction qui attend chacun des processus fils&lt;br /&gt;
void waitForAll() {&lt;br /&gt;
	int status;&lt;br /&gt;
	pid_t pid;&lt;br /&gt;
	int n = 0;&lt;br /&gt;
	while (n &amp;lt; NB_FORK) {&lt;br /&gt;
		pid = wait(&amp;amp;status);&lt;br /&gt;
		n++;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_FORK; i++) {&lt;br /&gt;
		fdpipe pipes;&lt;br /&gt;
		pipe(pipes.father);&lt;br /&gt;
		pipe(pipes.son);&lt;br /&gt;
		pid_t pid = fork();&lt;br /&gt;
		if (pid == -1) {&lt;br /&gt;
			// Il y a une erreur&lt;br /&gt;
			perror(&amp;quot;fork&amp;quot;);&lt;br /&gt;
			return EXIT_FAILURE;&lt;br /&gt;
		} else if (pid == 0) {&lt;br /&gt;
			// On est dans le fils&lt;br /&gt;
			close(pipes.father[1]);&lt;br /&gt;
			job(&amp;amp;pipes);&lt;br /&gt;
			close(pipes.father[0]);&lt;br /&gt;
			close(pipes.son[0]);&lt;br /&gt;
			close(pipes.son[1]);&lt;br /&gt;
			exit(EXIT_SUCCESS);&lt;br /&gt;
		} else {&lt;br /&gt;
			// On est dans le père&lt;br /&gt;
			// Ecriture du message dans le tableau&lt;br /&gt;
			sprintf(message, &amp;quot;Fork [%i], je suis ton père !\n&amp;quot;, pid);&lt;br /&gt;
			// Ecriture du message dans le tube&lt;br /&gt;
			write(pipes.father[1], message, LENGTH_MSG);&lt;br /&gt;
			while (i &amp;gt; 0) {&lt;br /&gt;
				// lecture dans le tube&lt;br /&gt;
				if (read(pipes.son[0], message, LENGTH_MSG) &amp;gt; 0) {&lt;br /&gt;
					printf(&amp;quot;Réponse du fils : %s\n&amp;quot;, message);&lt;br /&gt;
					break;&lt;br /&gt;
				}&lt;br /&gt;
				sleep(1);&lt;br /&gt;
			}&lt;br /&gt;
			close(pipes.father[1]);&lt;br /&gt;
			close(pipes.father[0]);&lt;br /&gt;
			close(pipes.son[0]);&lt;br /&gt;
			close(pipes.son[1]);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	waitForAll();&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Ce qui nous donne le résultat suivant :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Message du processus [18523] : Fork [18523], je suis ton père !&lt;br /&gt;
Réponse du fils : je suis [18523] et j&#039;ai bien reçu ton message !&lt;br /&gt;
Message du processus [18522] : Fork [18522], je suis ton père !&lt;br /&gt;
Message du processus [18524] : Fork [18524], je suis ton père !&lt;br /&gt;
Réponse du fils : je suis [18524] et j&#039;ai bien reçu ton message !&lt;br /&gt;
Message du processus [18525] : Fork [18525], je suis ton père !&lt;br /&gt;
Réponse du fils : je suis [18525] et j&#039;ai bien reçu ton message !&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Tube nommé =&lt;br /&gt;
== Fonctions utilisées ==&lt;br /&gt;
Il est possible de nommer un tube pour pouvoir y accéder depuis un autre processus. Pour ce faire nous allons utiliser les fonctions suivantes :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/stat.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int mkfifo (const char* name, mode_t mode);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* name est le nom du tube ; &lt;br /&gt;
* mode correspond aux permission du tube (idem &#039;&#039;chmod&#039;&#039;) ;&lt;br /&gt;
* le code retour de la fonction varie entre : &lt;br /&gt;
** EACCES : droits insuffisants pour créer le tube ;&lt;br /&gt;
** EEXIST : un tube de même nom existe déjà ;&lt;br /&gt;
** ENAMETOOLONG : nom trop long ;&lt;br /&gt;
** ENOENT : le chemin n&#039;existe pas ;&lt;br /&gt;
** ENOSPC : espace insuffisant sur le système de fichiers.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int open (const char* name, int options);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* name est le nom du tube ; &lt;br /&gt;
* options permet de désigner l&#039;extrémité du tube:&lt;br /&gt;
** O_WRONLY: pour l&#039;entrée ;&lt;br /&gt;
** O_RDONLY: pour la sortie.&lt;br /&gt;
* le retour est le descripteur pour la lecture ou l&#039;écriture ou -1 en cas d&#039;échec.&lt;br /&gt;
&lt;br /&gt;
== Exemple d&#039;utilisation ==&lt;br /&gt;
Ci-dessous un exemple d&#039;utilisation avec un thread qui lit dans un tube nommé:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/wait.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;pthread.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/stat.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Nom du tube&lt;br /&gt;
#define FIFO_NAME &amp;quot;/tmp/test.fifo&amp;quot;&lt;br /&gt;
// Longeur du buffer de lecture&lt;br /&gt;
#define BUFFER_LENGTH 30;&lt;br /&gt;
// Mode de lecture du tube&lt;br /&gt;
const mode_t FIFO_MODE = 0760;&lt;br /&gt;
&lt;br /&gt;
// Fonction exécutée par le thread&lt;br /&gt;
void * job(void * args) {&lt;br /&gt;
	// Récupération du descripteur en lecture&lt;br /&gt;
	int fdread;&lt;br /&gt;
	if ((fdread = open(FIFO_NAME, O_RDONLY)) == -1) {&lt;br /&gt;
		fprintf(stderr, &amp;quot;Impossible d&#039;ouvrir le tube en lecture: %s\n&amp;quot;,&lt;br /&gt;
				strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	// Création d&#039;un tampon pour stocker le contenu du tube&lt;br /&gt;
	char buffer[30];&lt;br /&gt;
	// Lecture du contenu du tube&lt;br /&gt;
	read(fdread, buffer, 30);&lt;br /&gt;
	// Affichage du contenu&lt;br /&gt;
	printf(&amp;quot;Le tube contient : %s&amp;quot;, buffer);&lt;br /&gt;
	// Fin de l&#039;exécution du thread&lt;br /&gt;
	pthread_exit(EXIT_SUCCESS);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
	// On supprime le tube s&#039;il existe déjà&lt;br /&gt;
	if (mkfifo(FIFO_NAME, FIFO_MODE) == -1) {&lt;br /&gt;
		printf(&amp;quot;Suppression du tube existant: %s\n&amp;quot;, FIFO_NAME);&lt;br /&gt;
		unlink(FIFO_NAME);&lt;br /&gt;
	}&lt;br /&gt;
	// Création de la variable qui va contenir le thread&lt;br /&gt;
	pthread_t thread;&lt;br /&gt;
	// Création du tube FIFO&lt;br /&gt;
	if (mkfifo(FIFO_NAME, FIFO_MODE) == -1) {&lt;br /&gt;
		printf(&amp;quot;Erreur de création du tube: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}else{&lt;br /&gt;
		printf(&amp;quot;Création du tube: %s\n&amp;quot;, FIFO_NAME);&lt;br /&gt;
	}&lt;br /&gt;
	// Création du thread&lt;br /&gt;
	pthread_create(&amp;amp;thread, NULL, job, NULL);&lt;br /&gt;
	// Récupération du descripteur en écriture&lt;br /&gt;
	int fdwrite;&lt;br /&gt;
	if ((fdwrite = open(FIFO_NAME, O_WRONLY)) == -1) {&lt;br /&gt;
		printf(&amp;quot;Impossible d&#039;ouvrir le tube en écriture: %s\n&amp;quot;,&lt;br /&gt;
				strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	// Ecriture dans le tube&lt;br /&gt;
	char message[] = &amp;quot;Bonjour thread&amp;quot;;&lt;br /&gt;
	write(fdwrite,  message, sizeof(message));&lt;br /&gt;
	// En attente de l&#039;éxécution du thread&lt;br /&gt;
	pthread_join(thread, NULL);&lt;br /&gt;
	// Fin de l&#039;exécution&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
{|style=&amp;quot;width:800px&amp;quot; align=&amp;quot;center&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
[[Fichier:Warning manual.jpg|centré|300px]]&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
Dans l&#039;exemple précédent, vous remarquerez que le thread est créé avant l&#039;ouverture du tube. C&#039;est absolument important de démarrer le thread avant, car la fonction &#039;&#039;open&#039;&#039; va bloquer le fil d&#039;exécution principal tant que le thread n&#039;ouvre pas le tube en lecture ! Pour eviter cela il faut utiliser l&#039;option &#039;&#039;&#039;O_NONBLOCK&#039;&#039;&#039;&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Magsss</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=C_pipe&amp;diff=3267</id>
		<title>C pipe</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=C_pipe&amp;diff=3267"/>
		<updated>2018-11-01T18:01:09Z</updated>

		<summary type="html">&lt;p&gt;Magsss : /* Utilisation uni-directionnel */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Un tube est un canal par lequel les informations circulent de manière uni-directionnelle. Un processus écrit dans l&#039;entrée du tube et un autre processus lit les informations en sortie.&lt;br /&gt;
&lt;br /&gt;
= Manipulation des tubes =&lt;br /&gt;
== Création ==&lt;br /&gt;
La première étape est la création d&#039;un tube:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int tube[2];&lt;br /&gt;
&lt;br /&gt;
int pipe(int tube[2]);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*tube[0] &amp;amp;rarr; contiendra le fichier descripteur de l&#039;extrémité de lecture&lt;br /&gt;
*tube[1] &amp;amp;rarr; contiendra le fichier descripteur de l&#039;extrémité d&#039;écriture&lt;br /&gt;
*Le retour sera :&lt;br /&gt;
** &#039;0&#039; si tout s&#039;est bien passé&lt;br /&gt;
** &#039;1&#039; si une erreur survient et errno est positionné&lt;br /&gt;
&lt;br /&gt;
== Écriture ==&lt;br /&gt;
Pour écrire dans un tube :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t write(int tube[1], const void *message, size_t longueur);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*tube[0] &amp;amp;rarr; le fichier descripteur de l&#039;extrémité d&#039;écriture&lt;br /&gt;
*message &amp;amp;rarr; le message à écrire&lt;br /&gt;
*longueur &amp;amp;rarr; la longueur du message&lt;br /&gt;
*ssize_t &amp;amp;rarr; le nombre d&#039;octets écrits&lt;br /&gt;
&lt;br /&gt;
== Lecture ==&lt;br /&gt;
Pour lire dans un tube :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t read(int tube[0], void *message, size_t longueur);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*tube[1] &amp;amp;rarr; le fichier descripteur de l&#039;extrémité de lecture&lt;br /&gt;
*message &amp;amp;rarr; un tableau de caractères qui contiendra le message à lire&lt;br /&gt;
*longueur &amp;amp;rarr; la longueur du message à lire&lt;br /&gt;
*ssize_t &amp;amp;rarr; le nombre d&#039;octets lus&lt;br /&gt;
&lt;br /&gt;
==fermeture==&lt;br /&gt;
Un fois la lecture terminée on ferme le tube:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int close(int descripteur);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*descripteur : une extrémité du tube&lt;br /&gt;
&lt;br /&gt;
= Utilisation dans un contexte d&#039;exécution parallélisé =&lt;br /&gt;
== Utilisation uni-directionnelle ==&lt;br /&gt;
Ci-dessous un exemple qui permet au père de communiquer avec ces fils:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/wait.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Nombre total de thread&lt;br /&gt;
#define NB_FORK 2&lt;br /&gt;
// Taille du message&lt;br /&gt;
#define LENGTH_MSG 30&lt;br /&gt;
// Tableau contenant le message&lt;br /&gt;
char message[LENGTH_MSG] = &amp;quot;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// Fonction exécutée dans le fork&lt;br /&gt;
void job(int * tube) {&lt;br /&gt;
	int tid = getpid();&lt;br /&gt;
	// timer pour attendre maximum 5 secondes&lt;br /&gt;
	int i = 5;&lt;br /&gt;
	while (i &amp;gt; 0) {&lt;br /&gt;
		// lecture dans le tube&lt;br /&gt;
		if (read(*tube, message, LENGTH_MSG) &amp;gt; 0) {&lt;br /&gt;
			printf(&amp;quot;Message du processus [%i] : %s&amp;quot;, tid, message);&lt;br /&gt;
			break;&lt;br /&gt;
		}&lt;br /&gt;
		sleep(1);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// Fonction qui attend chacun des processus fils&lt;br /&gt;
void waitForAll() {&lt;br /&gt;
	int status;&lt;br /&gt;
	pid_t pid;&lt;br /&gt;
	int n = 0;&lt;br /&gt;
	while (n &amp;lt; NB_FORK) {&lt;br /&gt;
		pid = wait(&amp;amp;status);&lt;br /&gt;
		n++;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_FORK; i++) {&lt;br /&gt;
		int tube[2];&lt;br /&gt;
		pipe(tube);&lt;br /&gt;
		pid_t pid = fork();&lt;br /&gt;
		if (pid == -1) {&lt;br /&gt;
			// Il y a une erreur&lt;br /&gt;
			perror(&amp;quot;fork&amp;quot;);&lt;br /&gt;
			return EXIT_FAILURE;&lt;br /&gt;
		} else if (pid == 0) {&lt;br /&gt;
			// On est dans le fils&lt;br /&gt;
			close(tube[1]);&lt;br /&gt;
			job(&amp;amp;tube[0]);&lt;br /&gt;
			close(tube[0]);&lt;br /&gt;
			exit(EXIT_SUCCESS);&lt;br /&gt;
		} else {&lt;br /&gt;
			// On est dans le père&lt;br /&gt;
			// Ecriture du message dans le tableau&lt;br /&gt;
			sprintf(message, &amp;quot;Fork [%i], je suis ton père !\n&amp;quot;, pid);&lt;br /&gt;
			// Ecriture du message dans le tube&lt;br /&gt;
			write(tube[1], message, LENGTH_MSG);&lt;br /&gt;
			close(tube[0]);&lt;br /&gt;
			close(tube[1]);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	waitForAll();&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Utilisation bidirectionnel ==&lt;br /&gt;
Dans cette exemple, on créer une structure qui va contenir les deux tubes pour plus de facilité:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/wait.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Structure avec des tubes full-duplex&lt;br /&gt;
typedef struct fdpipe {&lt;br /&gt;
	int father[2];&lt;br /&gt;
	int son[2];&lt;br /&gt;
} fdpipe;&lt;br /&gt;
&lt;br /&gt;
// Nombre total de thread&lt;br /&gt;
#define NB_FORK 10&lt;br /&gt;
// Taille du message&lt;br /&gt;
#define LENGTH_MSG 50&lt;br /&gt;
// Tableau contenant le message&lt;br /&gt;
char message[LENGTH_MSG] = &amp;quot;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// Fonction exécutée dans le fork&lt;br /&gt;
void job(fdpipe * pipes) {&lt;br /&gt;
	int tid = getpid();&lt;br /&gt;
	// timer pour attendre maximum 5 secondes&lt;br /&gt;
	int i = 5;&lt;br /&gt;
	while (i &amp;gt; 0) {&lt;br /&gt;
		// lecture dans le tube&lt;br /&gt;
		if (read((*pipes).father[0], message, LENGTH_MSG) &amp;gt; 0) {&lt;br /&gt;
			printf(&amp;quot;Message du processus [%i] : %s\n&amp;quot;, tid, message);&lt;br /&gt;
			// Ecriture du message dans le tableau&lt;br /&gt;
			sprintf(message, &amp;quot;je suis [%i] et j&#039;ai bien reçu ton message !\n&amp;quot;, tid);&lt;br /&gt;
			write((*pipes).son[1], message, LENGTH_MSG);&lt;br /&gt;
			break;&lt;br /&gt;
		}&lt;br /&gt;
		sleep(1);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// Fonction qui attend chacun des processus fils&lt;br /&gt;
void waitForAll() {&lt;br /&gt;
	int status;&lt;br /&gt;
	pid_t pid;&lt;br /&gt;
	int n = 0;&lt;br /&gt;
	while (n &amp;lt; NB_FORK) {&lt;br /&gt;
		pid = wait(&amp;amp;status);&lt;br /&gt;
		n++;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_FORK; i++) {&lt;br /&gt;
		fdpipe pipes;&lt;br /&gt;
		pipe(pipes.father);&lt;br /&gt;
		pipe(pipes.son);&lt;br /&gt;
		pid_t pid = fork();&lt;br /&gt;
		if (pid == -1) {&lt;br /&gt;
			// Il y a une erreur&lt;br /&gt;
			perror(&amp;quot;fork&amp;quot;);&lt;br /&gt;
			return EXIT_FAILURE;&lt;br /&gt;
		} else if (pid == 0) {&lt;br /&gt;
			// On est dans le fils&lt;br /&gt;
			close(pipes.father[1]);&lt;br /&gt;
			job(&amp;amp;pipes);&lt;br /&gt;
			close(pipes.father[0]);&lt;br /&gt;
			close(pipes.son[0]);&lt;br /&gt;
			close(pipes.son[1]);&lt;br /&gt;
			exit(EXIT_SUCCESS);&lt;br /&gt;
		} else {&lt;br /&gt;
			// On est dans le père&lt;br /&gt;
			// Ecriture du message dans le tableau&lt;br /&gt;
			sprintf(message, &amp;quot;Fork [%i], je suis ton père !\n&amp;quot;, pid);&lt;br /&gt;
			// Ecriture du message dans le tube&lt;br /&gt;
			write(pipes.father[1], message, LENGTH_MSG);&lt;br /&gt;
			while (i &amp;gt; 0) {&lt;br /&gt;
				// lecture dans le tube&lt;br /&gt;
				if (read(pipes.son[0], message, LENGTH_MSG) &amp;gt; 0) {&lt;br /&gt;
					printf(&amp;quot;Réponse du fils : %s\n&amp;quot;, message);&lt;br /&gt;
					break;&lt;br /&gt;
				}&lt;br /&gt;
				sleep(1);&lt;br /&gt;
			}&lt;br /&gt;
			close(pipes.father[1]);&lt;br /&gt;
			close(pipes.father[0]);&lt;br /&gt;
			close(pipes.son[0]);&lt;br /&gt;
			close(pipes.son[1]);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	waitForAll();&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Ce qui nous donne le résultat suivant :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Message du processus [18523] : Fork [18523], je suis ton père !&lt;br /&gt;
Réponse du fils : je suis [18523] et j&#039;ai bien reçu ton message !&lt;br /&gt;
Message du processus [18522] : Fork [18522], je suis ton père !&lt;br /&gt;
Message du processus [18524] : Fork [18524], je suis ton père !&lt;br /&gt;
Réponse du fils : je suis [18524] et j&#039;ai bien reçu ton message !&lt;br /&gt;
Message du processus [18525] : Fork [18525], je suis ton père !&lt;br /&gt;
Réponse du fils : je suis [18525] et j&#039;ai bien reçu ton message !&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Tube nommé =&lt;br /&gt;
== Fonctions utilisées ==&lt;br /&gt;
Il est possible de nommer un tube pour pouvoir y accéder depuis un autre processus. Pour ce faire nous allons utiliser les fonctions suivantes :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/stat.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int mkfifo (const char* name, mode_t mode);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* name est le nom du tube ; &lt;br /&gt;
* mode correspond aux permission du tube (idem &#039;&#039;chmod&#039;&#039;) ;&lt;br /&gt;
* le code retour de la fonction varie entre : &lt;br /&gt;
** EACCES : droits insuffisants pour créer le tube ;&lt;br /&gt;
** EEXIST : un tube de même nom existe déjà ;&lt;br /&gt;
** ENAMETOOLONG : nom trop long ;&lt;br /&gt;
** ENOENT : le chemin n&#039;existe pas ;&lt;br /&gt;
** ENOSPC : espace insuffisant sur le système de fichiers.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int open (const char* name, int options);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* name est le nom du tube ; &lt;br /&gt;
* options permet de désigner l&#039;extrémité du tube:&lt;br /&gt;
** O_WRONLY: pour l&#039;entrée ;&lt;br /&gt;
** O_RDONLY: pour la sortie.&lt;br /&gt;
* le retour est le descripteur pour la lecture ou l&#039;écriture ou -1 en cas d&#039;échec.&lt;br /&gt;
&lt;br /&gt;
== Exemple d&#039;utilisation ==&lt;br /&gt;
Ci-dessous un exemple d&#039;utilisation avec un thread qui lit dans un tube nommé:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/wait.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;pthread.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/stat.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Nom du tube&lt;br /&gt;
#define FIFO_NAME &amp;quot;/tmp/test.fifo&amp;quot;&lt;br /&gt;
// Longeur du buffer de lecture&lt;br /&gt;
#define BUFFER_LENGTH 30;&lt;br /&gt;
// Mode de lecture du tube&lt;br /&gt;
const mode_t FIFO_MODE = 0760;&lt;br /&gt;
&lt;br /&gt;
// Fonction exécutée par le thread&lt;br /&gt;
void * job(void * args) {&lt;br /&gt;
	// Récupération du descripteur en lecture&lt;br /&gt;
	int fdread;&lt;br /&gt;
	if ((fdread = open(FIFO_NAME, O_RDONLY)) == -1) {&lt;br /&gt;
		fprintf(stderr, &amp;quot;Impossible d&#039;ouvrir le tube en lecture: %s\n&amp;quot;,&lt;br /&gt;
				strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	// Création d&#039;un tampon pour stocker le contenu du tube&lt;br /&gt;
	char buffer[30];&lt;br /&gt;
	// Lecture du contenu du tube&lt;br /&gt;
	read(fdread, buffer, 30);&lt;br /&gt;
	// Affichage du contenu&lt;br /&gt;
	printf(&amp;quot;Le tube contient : %s&amp;quot;, buffer);&lt;br /&gt;
	// Fin de l&#039;exécution du thread&lt;br /&gt;
	pthread_exit(EXIT_SUCCESS);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
	// On supprime le tube s&#039;il existe déjà&lt;br /&gt;
	if (mkfifo(FIFO_NAME, FIFO_MODE) == -1) {&lt;br /&gt;
		printf(&amp;quot;Suppression du tube existant: %s\n&amp;quot;, FIFO_NAME);&lt;br /&gt;
		unlink(FIFO_NAME);&lt;br /&gt;
	}&lt;br /&gt;
	// Création de la variable qui va contenir le thread&lt;br /&gt;
	pthread_t thread;&lt;br /&gt;
	// Création du tube FIFO&lt;br /&gt;
	if (mkfifo(FIFO_NAME, FIFO_MODE) == -1) {&lt;br /&gt;
		printf(&amp;quot;Erreur de création du tube: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}else{&lt;br /&gt;
		printf(&amp;quot;Création du tube: %s\n&amp;quot;, FIFO_NAME);&lt;br /&gt;
	}&lt;br /&gt;
	// Création du thread&lt;br /&gt;
	pthread_create(&amp;amp;thread, NULL, job, NULL);&lt;br /&gt;
	// Récupération du descripteur en écriture&lt;br /&gt;
	int fdwrite;&lt;br /&gt;
	if ((fdwrite = open(FIFO_NAME, O_WRONLY)) == -1) {&lt;br /&gt;
		printf(&amp;quot;Impossible d&#039;ouvrir le tube en écriture: %s\n&amp;quot;,&lt;br /&gt;
				strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	// Ecriture dans le tube&lt;br /&gt;
	char message[] = &amp;quot;Bonjour thread&amp;quot;;&lt;br /&gt;
	write(fdwrite,  message, sizeof(message));&lt;br /&gt;
	// En attente de l&#039;éxécution du thread&lt;br /&gt;
	pthread_join(thread, NULL);&lt;br /&gt;
	// Fin de l&#039;exécution&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
{|style=&amp;quot;width:800px&amp;quot; align=&amp;quot;center&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
[[Fichier:Warning manual.jpg|centré|300px]]&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
Dans l&#039;exemple précédent, vous remarquerez que le thread est créé avant l&#039;ouverture du tube. C&#039;est absolument important de démarrer le thread avant, car la fonction &#039;&#039;open&#039;&#039; va bloquer le fil d&#039;exécution principal tant que le thread n&#039;ouvre pas le tube en lecture ! Pour eviter cela il faut utiliser l&#039;option &#039;&#039;&#039;O_NONBLOCK&#039;&#039;&#039;&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Magsss</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=C_pipe&amp;diff=3266</id>
		<title>C pipe</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=C_pipe&amp;diff=3266"/>
		<updated>2018-11-01T18:01:00Z</updated>

		<summary type="html">&lt;p&gt;Magsss : /* Utilisation dans un contexte d&amp;#039;exécution parallélisé */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Un tube est un canal par lequel les informations circulent de manière uni-directionnelle. Un processus écrit dans l&#039;entrée du tube et un autre processus lit les informations en sortie.&lt;br /&gt;
&lt;br /&gt;
= Manipulation des tubes =&lt;br /&gt;
== Création ==&lt;br /&gt;
La première étape est la création d&#039;un tube:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int tube[2];&lt;br /&gt;
&lt;br /&gt;
int pipe(int tube[2]);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*tube[0] &amp;amp;rarr; contiendra le fichier descripteur de l&#039;extrémité de lecture&lt;br /&gt;
*tube[1] &amp;amp;rarr; contiendra le fichier descripteur de l&#039;extrémité d&#039;écriture&lt;br /&gt;
*Le retour sera :&lt;br /&gt;
** &#039;0&#039; si tout s&#039;est bien passé&lt;br /&gt;
** &#039;1&#039; si une erreur survient et errno est positionné&lt;br /&gt;
&lt;br /&gt;
== Écriture ==&lt;br /&gt;
Pour écrire dans un tube :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t write(int tube[1], const void *message, size_t longueur);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*tube[0] &amp;amp;rarr; le fichier descripteur de l&#039;extrémité d&#039;écriture&lt;br /&gt;
*message &amp;amp;rarr; le message à écrire&lt;br /&gt;
*longueur &amp;amp;rarr; la longueur du message&lt;br /&gt;
*ssize_t &amp;amp;rarr; le nombre d&#039;octets écrits&lt;br /&gt;
&lt;br /&gt;
== Lecture ==&lt;br /&gt;
Pour lire dans un tube :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t read(int tube[0], void *message, size_t longueur);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*tube[1] &amp;amp;rarr; le fichier descripteur de l&#039;extrémité de lecture&lt;br /&gt;
*message &amp;amp;rarr; un tableau de caractères qui contiendra le message à lire&lt;br /&gt;
*longueur &amp;amp;rarr; la longueur du message à lire&lt;br /&gt;
*ssize_t &amp;amp;rarr; le nombre d&#039;octets lus&lt;br /&gt;
&lt;br /&gt;
==fermeture==&lt;br /&gt;
Un fois la lecture terminée on ferme le tube:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int close(int descripteur);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*descripteur : une extrémité du tube&lt;br /&gt;
&lt;br /&gt;
= Utilisation dans un contexte d&#039;exécution parallélisé =&lt;br /&gt;
== Utilisation uni-directionnel ==&lt;br /&gt;
Ci-dessous un exemple qui permet au père de communiquer avec ces fils:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/wait.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Nombre total de thread&lt;br /&gt;
#define NB_FORK 2&lt;br /&gt;
// Taille du message&lt;br /&gt;
#define LENGTH_MSG 30&lt;br /&gt;
// Tableau contenant le message&lt;br /&gt;
char message[LENGTH_MSG] = &amp;quot;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// Fonction exécutée dans le fork&lt;br /&gt;
void job(int * tube) {&lt;br /&gt;
	int tid = getpid();&lt;br /&gt;
	// timer pour attendre maximum 5 secondes&lt;br /&gt;
	int i = 5;&lt;br /&gt;
	while (i &amp;gt; 0) {&lt;br /&gt;
		// lecture dans le tube&lt;br /&gt;
		if (read(*tube, message, LENGTH_MSG) &amp;gt; 0) {&lt;br /&gt;
			printf(&amp;quot;Message du processus [%i] : %s&amp;quot;, tid, message);&lt;br /&gt;
			break;&lt;br /&gt;
		}&lt;br /&gt;
		sleep(1);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// Fonction qui attend chacun des processus fils&lt;br /&gt;
void waitForAll() {&lt;br /&gt;
	int status;&lt;br /&gt;
	pid_t pid;&lt;br /&gt;
	int n = 0;&lt;br /&gt;
	while (n &amp;lt; NB_FORK) {&lt;br /&gt;
		pid = wait(&amp;amp;status);&lt;br /&gt;
		n++;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_FORK; i++) {&lt;br /&gt;
		int tube[2];&lt;br /&gt;
		pipe(tube);&lt;br /&gt;
		pid_t pid = fork();&lt;br /&gt;
		if (pid == -1) {&lt;br /&gt;
			// Il y a une erreur&lt;br /&gt;
			perror(&amp;quot;fork&amp;quot;);&lt;br /&gt;
			return EXIT_FAILURE;&lt;br /&gt;
		} else if (pid == 0) {&lt;br /&gt;
			// On est dans le fils&lt;br /&gt;
			close(tube[1]);&lt;br /&gt;
			job(&amp;amp;tube[0]);&lt;br /&gt;
			close(tube[0]);&lt;br /&gt;
			exit(EXIT_SUCCESS);&lt;br /&gt;
		} else {&lt;br /&gt;
			// On est dans le père&lt;br /&gt;
			// Ecriture du message dans le tableau&lt;br /&gt;
			sprintf(message, &amp;quot;Fork [%i], je suis ton père !\n&amp;quot;, pid);&lt;br /&gt;
			// Ecriture du message dans le tube&lt;br /&gt;
			write(tube[1], message, LENGTH_MSG);&lt;br /&gt;
			close(tube[0]);&lt;br /&gt;
			close(tube[1]);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	waitForAll();&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Utilisation bidirectionnel ==&lt;br /&gt;
Dans cette exemple, on créer une structure qui va contenir les deux tubes pour plus de facilité:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/wait.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Structure avec des tubes full-duplex&lt;br /&gt;
typedef struct fdpipe {&lt;br /&gt;
	int father[2];&lt;br /&gt;
	int son[2];&lt;br /&gt;
} fdpipe;&lt;br /&gt;
&lt;br /&gt;
// Nombre total de thread&lt;br /&gt;
#define NB_FORK 10&lt;br /&gt;
// Taille du message&lt;br /&gt;
#define LENGTH_MSG 50&lt;br /&gt;
// Tableau contenant le message&lt;br /&gt;
char message[LENGTH_MSG] = &amp;quot;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// Fonction exécutée dans le fork&lt;br /&gt;
void job(fdpipe * pipes) {&lt;br /&gt;
	int tid = getpid();&lt;br /&gt;
	// timer pour attendre maximum 5 secondes&lt;br /&gt;
	int i = 5;&lt;br /&gt;
	while (i &amp;gt; 0) {&lt;br /&gt;
		// lecture dans le tube&lt;br /&gt;
		if (read((*pipes).father[0], message, LENGTH_MSG) &amp;gt; 0) {&lt;br /&gt;
			printf(&amp;quot;Message du processus [%i] : %s\n&amp;quot;, tid, message);&lt;br /&gt;
			// Ecriture du message dans le tableau&lt;br /&gt;
			sprintf(message, &amp;quot;je suis [%i] et j&#039;ai bien reçu ton message !\n&amp;quot;, tid);&lt;br /&gt;
			write((*pipes).son[1], message, LENGTH_MSG);&lt;br /&gt;
			break;&lt;br /&gt;
		}&lt;br /&gt;
		sleep(1);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// Fonction qui attend chacun des processus fils&lt;br /&gt;
void waitForAll() {&lt;br /&gt;
	int status;&lt;br /&gt;
	pid_t pid;&lt;br /&gt;
	int n = 0;&lt;br /&gt;
	while (n &amp;lt; NB_FORK) {&lt;br /&gt;
		pid = wait(&amp;amp;status);&lt;br /&gt;
		n++;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_FORK; i++) {&lt;br /&gt;
		fdpipe pipes;&lt;br /&gt;
		pipe(pipes.father);&lt;br /&gt;
		pipe(pipes.son);&lt;br /&gt;
		pid_t pid = fork();&lt;br /&gt;
		if (pid == -1) {&lt;br /&gt;
			// Il y a une erreur&lt;br /&gt;
			perror(&amp;quot;fork&amp;quot;);&lt;br /&gt;
			return EXIT_FAILURE;&lt;br /&gt;
		} else if (pid == 0) {&lt;br /&gt;
			// On est dans le fils&lt;br /&gt;
			close(pipes.father[1]);&lt;br /&gt;
			job(&amp;amp;pipes);&lt;br /&gt;
			close(pipes.father[0]);&lt;br /&gt;
			close(pipes.son[0]);&lt;br /&gt;
			close(pipes.son[1]);&lt;br /&gt;
			exit(EXIT_SUCCESS);&lt;br /&gt;
		} else {&lt;br /&gt;
			// On est dans le père&lt;br /&gt;
			// Ecriture du message dans le tableau&lt;br /&gt;
			sprintf(message, &amp;quot;Fork [%i], je suis ton père !\n&amp;quot;, pid);&lt;br /&gt;
			// Ecriture du message dans le tube&lt;br /&gt;
			write(pipes.father[1], message, LENGTH_MSG);&lt;br /&gt;
			while (i &amp;gt; 0) {&lt;br /&gt;
				// lecture dans le tube&lt;br /&gt;
				if (read(pipes.son[0], message, LENGTH_MSG) &amp;gt; 0) {&lt;br /&gt;
					printf(&amp;quot;Réponse du fils : %s\n&amp;quot;, message);&lt;br /&gt;
					break;&lt;br /&gt;
				}&lt;br /&gt;
				sleep(1);&lt;br /&gt;
			}&lt;br /&gt;
			close(pipes.father[1]);&lt;br /&gt;
			close(pipes.father[0]);&lt;br /&gt;
			close(pipes.son[0]);&lt;br /&gt;
			close(pipes.son[1]);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	waitForAll();&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Ce qui nous donne le résultat suivant :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Message du processus [18523] : Fork [18523], je suis ton père !&lt;br /&gt;
Réponse du fils : je suis [18523] et j&#039;ai bien reçu ton message !&lt;br /&gt;
Message du processus [18522] : Fork [18522], je suis ton père !&lt;br /&gt;
Message du processus [18524] : Fork [18524], je suis ton père !&lt;br /&gt;
Réponse du fils : je suis [18524] et j&#039;ai bien reçu ton message !&lt;br /&gt;
Message du processus [18525] : Fork [18525], je suis ton père !&lt;br /&gt;
Réponse du fils : je suis [18525] et j&#039;ai bien reçu ton message !&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Tube nommé =&lt;br /&gt;
== Fonctions utilisées ==&lt;br /&gt;
Il est possible de nommer un tube pour pouvoir y accéder depuis un autre processus. Pour ce faire nous allons utiliser les fonctions suivantes :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/stat.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int mkfifo (const char* name, mode_t mode);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* name est le nom du tube ; &lt;br /&gt;
* mode correspond aux permission du tube (idem &#039;&#039;chmod&#039;&#039;) ;&lt;br /&gt;
* le code retour de la fonction varie entre : &lt;br /&gt;
** EACCES : droits insuffisants pour créer le tube ;&lt;br /&gt;
** EEXIST : un tube de même nom existe déjà ;&lt;br /&gt;
** ENAMETOOLONG : nom trop long ;&lt;br /&gt;
** ENOENT : le chemin n&#039;existe pas ;&lt;br /&gt;
** ENOSPC : espace insuffisant sur le système de fichiers.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int open (const char* name, int options);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* name est le nom du tube ; &lt;br /&gt;
* options permet de désigner l&#039;extrémité du tube:&lt;br /&gt;
** O_WRONLY: pour l&#039;entrée ;&lt;br /&gt;
** O_RDONLY: pour la sortie.&lt;br /&gt;
* le retour est le descripteur pour la lecture ou l&#039;écriture ou -1 en cas d&#039;échec.&lt;br /&gt;
&lt;br /&gt;
== Exemple d&#039;utilisation ==&lt;br /&gt;
Ci-dessous un exemple d&#039;utilisation avec un thread qui lit dans un tube nommé:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/wait.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;pthread.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/stat.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Nom du tube&lt;br /&gt;
#define FIFO_NAME &amp;quot;/tmp/test.fifo&amp;quot;&lt;br /&gt;
// Longeur du buffer de lecture&lt;br /&gt;
#define BUFFER_LENGTH 30;&lt;br /&gt;
// Mode de lecture du tube&lt;br /&gt;
const mode_t FIFO_MODE = 0760;&lt;br /&gt;
&lt;br /&gt;
// Fonction exécutée par le thread&lt;br /&gt;
void * job(void * args) {&lt;br /&gt;
	// Récupération du descripteur en lecture&lt;br /&gt;
	int fdread;&lt;br /&gt;
	if ((fdread = open(FIFO_NAME, O_RDONLY)) == -1) {&lt;br /&gt;
		fprintf(stderr, &amp;quot;Impossible d&#039;ouvrir le tube en lecture: %s\n&amp;quot;,&lt;br /&gt;
				strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	// Création d&#039;un tampon pour stocker le contenu du tube&lt;br /&gt;
	char buffer[30];&lt;br /&gt;
	// Lecture du contenu du tube&lt;br /&gt;
	read(fdread, buffer, 30);&lt;br /&gt;
	// Affichage du contenu&lt;br /&gt;
	printf(&amp;quot;Le tube contient : %s&amp;quot;, buffer);&lt;br /&gt;
	// Fin de l&#039;exécution du thread&lt;br /&gt;
	pthread_exit(EXIT_SUCCESS);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
	// On supprime le tube s&#039;il existe déjà&lt;br /&gt;
	if (mkfifo(FIFO_NAME, FIFO_MODE) == -1) {&lt;br /&gt;
		printf(&amp;quot;Suppression du tube existant: %s\n&amp;quot;, FIFO_NAME);&lt;br /&gt;
		unlink(FIFO_NAME);&lt;br /&gt;
	}&lt;br /&gt;
	// Création de la variable qui va contenir le thread&lt;br /&gt;
	pthread_t thread;&lt;br /&gt;
	// Création du tube FIFO&lt;br /&gt;
	if (mkfifo(FIFO_NAME, FIFO_MODE) == -1) {&lt;br /&gt;
		printf(&amp;quot;Erreur de création du tube: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}else{&lt;br /&gt;
		printf(&amp;quot;Création du tube: %s\n&amp;quot;, FIFO_NAME);&lt;br /&gt;
	}&lt;br /&gt;
	// Création du thread&lt;br /&gt;
	pthread_create(&amp;amp;thread, NULL, job, NULL);&lt;br /&gt;
	// Récupération du descripteur en écriture&lt;br /&gt;
	int fdwrite;&lt;br /&gt;
	if ((fdwrite = open(FIFO_NAME, O_WRONLY)) == -1) {&lt;br /&gt;
		printf(&amp;quot;Impossible d&#039;ouvrir le tube en écriture: %s\n&amp;quot;,&lt;br /&gt;
				strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	// Ecriture dans le tube&lt;br /&gt;
	char message[] = &amp;quot;Bonjour thread&amp;quot;;&lt;br /&gt;
	write(fdwrite,  message, sizeof(message));&lt;br /&gt;
	// En attente de l&#039;éxécution du thread&lt;br /&gt;
	pthread_join(thread, NULL);&lt;br /&gt;
	// Fin de l&#039;exécution&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
{|style=&amp;quot;width:800px&amp;quot; align=&amp;quot;center&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
[[Fichier:Warning manual.jpg|centré|300px]]&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
Dans l&#039;exemple précédent, vous remarquerez que le thread est créé avant l&#039;ouverture du tube. C&#039;est absolument important de démarrer le thread avant, car la fonction &#039;&#039;open&#039;&#039; va bloquer le fil d&#039;exécution principal tant que le thread n&#039;ouvre pas le tube en lecture ! Pour eviter cela il faut utiliser l&#039;option &#039;&#039;&#039;O_NONBLOCK&#039;&#039;&#039;&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Magsss</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=C_pipe&amp;diff=3265</id>
		<title>C pipe</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=C_pipe&amp;diff=3265"/>
		<updated>2018-11-01T18:00:43Z</updated>

		<summary type="html">&lt;p&gt;Magsss : /* fermeture */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Un tube est un canal par lequel les informations circulent de manière uni-directionnelle. Un processus écrit dans l&#039;entrée du tube et un autre processus lit les informations en sortie.&lt;br /&gt;
&lt;br /&gt;
= Manipulation des tubes =&lt;br /&gt;
== Création ==&lt;br /&gt;
La première étape est la création d&#039;un tube:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int tube[2];&lt;br /&gt;
&lt;br /&gt;
int pipe(int tube[2]);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*tube[0] &amp;amp;rarr; contiendra le fichier descripteur de l&#039;extrémité de lecture&lt;br /&gt;
*tube[1] &amp;amp;rarr; contiendra le fichier descripteur de l&#039;extrémité d&#039;écriture&lt;br /&gt;
*Le retour sera :&lt;br /&gt;
** &#039;0&#039; si tout s&#039;est bien passé&lt;br /&gt;
** &#039;1&#039; si une erreur survient et errno est positionné&lt;br /&gt;
&lt;br /&gt;
== Écriture ==&lt;br /&gt;
Pour écrire dans un tube :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t write(int tube[1], const void *message, size_t longueur);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*tube[0] &amp;amp;rarr; le fichier descripteur de l&#039;extrémité d&#039;écriture&lt;br /&gt;
*message &amp;amp;rarr; le message à écrire&lt;br /&gt;
*longueur &amp;amp;rarr; la longueur du message&lt;br /&gt;
*ssize_t &amp;amp;rarr; le nombre d&#039;octets écrits&lt;br /&gt;
&lt;br /&gt;
== Lecture ==&lt;br /&gt;
Pour lire dans un tube :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t read(int tube[0], void *message, size_t longueur);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*tube[1] &amp;amp;rarr; le fichier descripteur de l&#039;extrémité de lecture&lt;br /&gt;
*message &amp;amp;rarr; un tableau de caractères qui contiendra le message à lire&lt;br /&gt;
*longueur &amp;amp;rarr; la longueur du message à lire&lt;br /&gt;
*ssize_t &amp;amp;rarr; le nombre d&#039;octets lus&lt;br /&gt;
&lt;br /&gt;
==fermeture==&lt;br /&gt;
Un fois la lecture terminée on ferme le tube:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int close(int descripteur);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*descripteur : une extrémité du tube&lt;br /&gt;
&lt;br /&gt;
= Utilisation dans un contexte d&#039;exécution parallélisé =&lt;br /&gt;
== Utilisation uni-directionnel ==&lt;br /&gt;
Ci-dessous un exemple qui permet au père de communiquer avec ces fils:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/wait.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Nombre total de thread&lt;br /&gt;
#define NB_FORK 2&lt;br /&gt;
// Taille du message&lt;br /&gt;
#define LENGTH_MSG 30&lt;br /&gt;
// Tableau contenant le message&lt;br /&gt;
char message[LENGTH_MSG] = &amp;quot;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// Fonction exécutée dans le fork&lt;br /&gt;
void job(int * tube) {&lt;br /&gt;
	int tid = getpid();&lt;br /&gt;
	// timer pour attendre maximum 5 secondes&lt;br /&gt;
	int i = 5;&lt;br /&gt;
	while (i &amp;gt; 0) {&lt;br /&gt;
		// lecture dans le tube&lt;br /&gt;
		if (read(*tube, message, LENGTH_MSG) &amp;gt; 0) {&lt;br /&gt;
			printf(&amp;quot;Message du processus [%i] : %s&amp;quot;, tid, message);&lt;br /&gt;
			break;&lt;br /&gt;
		}&lt;br /&gt;
		sleep(1);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// Fonction qui attend chacun des processus fils&lt;br /&gt;
void waitForAll() {&lt;br /&gt;
	int status;&lt;br /&gt;
	pid_t pid;&lt;br /&gt;
	int n = 0;&lt;br /&gt;
	while (n &amp;lt; NB_FORK) {&lt;br /&gt;
		pid = wait(&amp;amp;status);&lt;br /&gt;
		n++;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_FORK; i++) {&lt;br /&gt;
		int tube[2];&lt;br /&gt;
		pipe(tube);&lt;br /&gt;
		pid_t pid = fork();&lt;br /&gt;
		if (pid == -1) {&lt;br /&gt;
			// Il y a une erreur&lt;br /&gt;
			perror(&amp;quot;fork&amp;quot;);&lt;br /&gt;
			return EXIT_FAILURE;&lt;br /&gt;
		} else if (pid == 0) {&lt;br /&gt;
			// On est dans le fils&lt;br /&gt;
			close(tube[1]);&lt;br /&gt;
			job(&amp;amp;tube[0]);&lt;br /&gt;
			close(tube[0]);&lt;br /&gt;
			exit(EXIT_SUCCESS);&lt;br /&gt;
		} else {&lt;br /&gt;
			// On est dans le père&lt;br /&gt;
			// Ecriture du message dans le tableau&lt;br /&gt;
			sprintf(message, &amp;quot;Fork [%i], je suis ton père !\n&amp;quot;, pid);&lt;br /&gt;
			// Ecriture du message dans le tube&lt;br /&gt;
			write(tube[1], message, LENGTH_MSG);&lt;br /&gt;
			close(tube[0]);&lt;br /&gt;
			close(tube[1]);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	waitForAll();&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Utilisation bidirectionnel ==&lt;br /&gt;
Dans cette exemple, on créer une structure qui va contenir les deux tubes pour plus de facilité:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/wait.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Structure avec des tubes full-duplex&lt;br /&gt;
typedef struct fdpipe {&lt;br /&gt;
	int father[2];&lt;br /&gt;
	int son[2];&lt;br /&gt;
} fdpipe;&lt;br /&gt;
&lt;br /&gt;
// Nombre total de thread&lt;br /&gt;
#define NB_FORK 10&lt;br /&gt;
// Taille du message&lt;br /&gt;
#define LENGTH_MSG 50&lt;br /&gt;
// Tableau contenant le message&lt;br /&gt;
char message[LENGTH_MSG] = &amp;quot;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// Fonction exécutée dans le fork&lt;br /&gt;
void job(fdpipe * pipes) {&lt;br /&gt;
	int tid = getpid();&lt;br /&gt;
	// timer pour attendre maximum 5 secondes&lt;br /&gt;
	int i = 5;&lt;br /&gt;
	while (i &amp;gt; 0) {&lt;br /&gt;
		// lecture dans le tube&lt;br /&gt;
		if (read((*pipes).father[0], message, LENGTH_MSG) &amp;gt; 0) {&lt;br /&gt;
			printf(&amp;quot;Message du processus [%i] : %s\n&amp;quot;, tid, message);&lt;br /&gt;
			// Ecriture du message dans le tableau&lt;br /&gt;
			sprintf(message, &amp;quot;je suis [%i] et j&#039;ai bien reçu ton message !\n&amp;quot;, tid);&lt;br /&gt;
			write((*pipes).son[1], message, LENGTH_MSG);&lt;br /&gt;
			break;&lt;br /&gt;
		}&lt;br /&gt;
		sleep(1);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// Fonction qui attend chacun des processus fils&lt;br /&gt;
void waitForAll() {&lt;br /&gt;
	int status;&lt;br /&gt;
	pid_t pid;&lt;br /&gt;
	int n = 0;&lt;br /&gt;
	while (n &amp;lt; NB_FORK) {&lt;br /&gt;
		pid = wait(&amp;amp;status);&lt;br /&gt;
		n++;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_FORK; i++) {&lt;br /&gt;
		fdpipe pipes;&lt;br /&gt;
		pipe(pipes.father);&lt;br /&gt;
		pipe(pipes.son);&lt;br /&gt;
		pid_t pid = fork();&lt;br /&gt;
		if (pid == -1) {&lt;br /&gt;
			// Il y a une erreur&lt;br /&gt;
			perror(&amp;quot;fork&amp;quot;);&lt;br /&gt;
			return EXIT_FAILURE;&lt;br /&gt;
		} else if (pid == 0) {&lt;br /&gt;
			// On est dans le fils&lt;br /&gt;
			close(pipes.father[1]);&lt;br /&gt;
			job(&amp;amp;pipes);&lt;br /&gt;
			close(pipes.father[0]);&lt;br /&gt;
			close(pipes.son[0]);&lt;br /&gt;
			close(pipes.son[1]);&lt;br /&gt;
			exit(EXIT_SUCCESS);&lt;br /&gt;
		} else {&lt;br /&gt;
			// On est dans le père&lt;br /&gt;
			// Ecriture du message dans le tableau&lt;br /&gt;
			sprintf(message, &amp;quot;Fork [%i], je suis ton père !\n&amp;quot;, pid);&lt;br /&gt;
			// Ecriture du message dans le tube&lt;br /&gt;
			write(pipes.father[1], message, LENGTH_MSG);&lt;br /&gt;
			while (i &amp;gt; 0) {&lt;br /&gt;
				// lecture dans le tube&lt;br /&gt;
				if (read(pipes.son[0], message, LENGTH_MSG) &amp;gt; 0) {&lt;br /&gt;
					printf(&amp;quot;Réponse du fils : %s\n&amp;quot;, message);&lt;br /&gt;
					break;&lt;br /&gt;
				}&lt;br /&gt;
				sleep(1);&lt;br /&gt;
			}&lt;br /&gt;
			close(pipes.father[1]);&lt;br /&gt;
			close(pipes.father[0]);&lt;br /&gt;
			close(pipes.son[0]);&lt;br /&gt;
			close(pipes.son[1]);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	waitForAll();&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Ce qui nous donne le résultat suivant :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Message du processus [18523] : Fork [18523], je suis ton père !&lt;br /&gt;
Réponse du fils : je suis [18523] et j&#039;ai bien reçu ton message !&lt;br /&gt;
Message du processus [18522] : Fork [18522], je suis ton père !&lt;br /&gt;
Message du processus [18524] : Fork [18524], je suis ton père !&lt;br /&gt;
Réponse du fils : je suis [18524] et j&#039;ai bien reçu ton message !&lt;br /&gt;
Message du processus [18525] : Fork [18525], je suis ton père !&lt;br /&gt;
Réponse du fils : je suis [18525] et j&#039;ai bien reçu ton message !&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
= Tube nommé =&lt;br /&gt;
== Fonctions utilisées ==&lt;br /&gt;
Il est possible de nommer un tube pour pouvoir y accéder depuis un autre processus. Pour ce faire nous allons utiliser les fonctions suivantes :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/stat.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int mkfifo (const char* name, mode_t mode);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* name est le nom du tube ; &lt;br /&gt;
* mode correspond aux permission du tube (idem &#039;&#039;chmod&#039;&#039;) ;&lt;br /&gt;
* le code retour de la fonction varie entre : &lt;br /&gt;
** EACCES : droits insuffisants pour créer le tube ;&lt;br /&gt;
** EEXIST : un tube de même nom existe déjà ;&lt;br /&gt;
** ENAMETOOLONG : nom trop long ;&lt;br /&gt;
** ENOENT : le chemin n&#039;existe pas ;&lt;br /&gt;
** ENOSPC : espace insuffisant sur le système de fichiers.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int open (const char* name, int options);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* name est le nom du tube ; &lt;br /&gt;
* options permet de désigner l&#039;extrémité du tube:&lt;br /&gt;
** O_WRONLY: pour l&#039;entrée ;&lt;br /&gt;
** O_RDONLY: pour la sortie.&lt;br /&gt;
* le retour est le descripteur pour la lecture ou l&#039;écriture ou -1 en cas d&#039;échec.&lt;br /&gt;
&lt;br /&gt;
== Exemple d&#039;utilisation ==&lt;br /&gt;
Ci-dessous un exemple d&#039;utilisation avec un thread qui lit dans un tube nommé:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/wait.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;pthread.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/stat.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Nom du tube&lt;br /&gt;
#define FIFO_NAME &amp;quot;/tmp/test.fifo&amp;quot;&lt;br /&gt;
// Longeur du buffer de lecture&lt;br /&gt;
#define BUFFER_LENGTH 30;&lt;br /&gt;
// Mode de lecture du tube&lt;br /&gt;
const mode_t FIFO_MODE = 0760;&lt;br /&gt;
&lt;br /&gt;
// Fonction exécutée par le thread&lt;br /&gt;
void * job(void * args) {&lt;br /&gt;
	// Récupération du descripteur en lecture&lt;br /&gt;
	int fdread;&lt;br /&gt;
	if ((fdread = open(FIFO_NAME, O_RDONLY)) == -1) {&lt;br /&gt;
		fprintf(stderr, &amp;quot;Impossible d&#039;ouvrir le tube en lecture: %s\n&amp;quot;,&lt;br /&gt;
				strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	// Création d&#039;un tampon pour stocker le contenu du tube&lt;br /&gt;
	char buffer[30];&lt;br /&gt;
	// Lecture du contenu du tube&lt;br /&gt;
	read(fdread, buffer, 30);&lt;br /&gt;
	// Affichage du contenu&lt;br /&gt;
	printf(&amp;quot;Le tube contient : %s&amp;quot;, buffer);&lt;br /&gt;
	// Fin de l&#039;exécution du thread&lt;br /&gt;
	pthread_exit(EXIT_SUCCESS);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
	// On supprime le tube s&#039;il existe déjà&lt;br /&gt;
	if (mkfifo(FIFO_NAME, FIFO_MODE) == -1) {&lt;br /&gt;
		printf(&amp;quot;Suppression du tube existant: %s\n&amp;quot;, FIFO_NAME);&lt;br /&gt;
		unlink(FIFO_NAME);&lt;br /&gt;
	}&lt;br /&gt;
	// Création de la variable qui va contenir le thread&lt;br /&gt;
	pthread_t thread;&lt;br /&gt;
	// Création du tube FIFO&lt;br /&gt;
	if (mkfifo(FIFO_NAME, FIFO_MODE) == -1) {&lt;br /&gt;
		printf(&amp;quot;Erreur de création du tube: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}else{&lt;br /&gt;
		printf(&amp;quot;Création du tube: %s\n&amp;quot;, FIFO_NAME);&lt;br /&gt;
	}&lt;br /&gt;
	// Création du thread&lt;br /&gt;
	pthread_create(&amp;amp;thread, NULL, job, NULL);&lt;br /&gt;
	// Récupération du descripteur en écriture&lt;br /&gt;
	int fdwrite;&lt;br /&gt;
	if ((fdwrite = open(FIFO_NAME, O_WRONLY)) == -1) {&lt;br /&gt;
		printf(&amp;quot;Impossible d&#039;ouvrir le tube en écriture: %s\n&amp;quot;,&lt;br /&gt;
				strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	// Ecriture dans le tube&lt;br /&gt;
	char message[] = &amp;quot;Bonjour thread&amp;quot;;&lt;br /&gt;
	write(fdwrite,  message, sizeof(message));&lt;br /&gt;
	// En attente de l&#039;éxécution du thread&lt;br /&gt;
	pthread_join(thread, NULL);&lt;br /&gt;
	// Fin de l&#039;exécution&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
{|style=&amp;quot;width:800px&amp;quot; align=&amp;quot;center&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
[[Fichier:Warning manual.jpg|centré|300px]]&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
Dans l&#039;exemple précédent, vous remarquerez que le thread est créé avant l&#039;ouverture du tube. C&#039;est absolument important de démarrer le thread avant, car la fonction &#039;&#039;open&#039;&#039; va bloquer le fil d&#039;exécution principal tant que le thread n&#039;ouvre pas le tube en lecture ! Pour eviter cela il faut utiliser l&#039;option &#039;&#039;&#039;O_NONBLOCK&#039;&#039;&#039;&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Magsss</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=C_pipe&amp;diff=3264</id>
		<title>C pipe</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=C_pipe&amp;diff=3264"/>
		<updated>2018-11-01T18:00:32Z</updated>

		<summary type="html">&lt;p&gt;Magsss : /* Lecture */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Un tube est un canal par lequel les informations circulent de manière uni-directionnelle. Un processus écrit dans l&#039;entrée du tube et un autre processus lit les informations en sortie.&lt;br /&gt;
&lt;br /&gt;
= Manipulation des tubes =&lt;br /&gt;
== Création ==&lt;br /&gt;
La première étape est la création d&#039;un tube:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int tube[2];&lt;br /&gt;
&lt;br /&gt;
int pipe(int tube[2]);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*tube[0] &amp;amp;rarr; contiendra le fichier descripteur de l&#039;extrémité de lecture&lt;br /&gt;
*tube[1] &amp;amp;rarr; contiendra le fichier descripteur de l&#039;extrémité d&#039;écriture&lt;br /&gt;
*Le retour sera :&lt;br /&gt;
** &#039;0&#039; si tout s&#039;est bien passé&lt;br /&gt;
** &#039;1&#039; si une erreur survient et errno est positionné&lt;br /&gt;
&lt;br /&gt;
== Écriture ==&lt;br /&gt;
Pour écrire dans un tube :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t write(int tube[1], const void *message, size_t longueur);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*tube[0] &amp;amp;rarr; le fichier descripteur de l&#039;extrémité d&#039;écriture&lt;br /&gt;
*message &amp;amp;rarr; le message à écrire&lt;br /&gt;
*longueur &amp;amp;rarr; la longueur du message&lt;br /&gt;
*ssize_t &amp;amp;rarr; le nombre d&#039;octets écrits&lt;br /&gt;
&lt;br /&gt;
== Lecture ==&lt;br /&gt;
Pour lire dans un tube :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t read(int tube[0], void *message, size_t longueur);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*tube[1] &amp;amp;rarr; le fichier descripteur de l&#039;extrémité de lecture&lt;br /&gt;
*message &amp;amp;rarr; un tableau de caractères qui contiendra le message à lire&lt;br /&gt;
*longueur &amp;amp;rarr; la longueur du message à lire&lt;br /&gt;
*ssize_t &amp;amp;rarr; le nombre d&#039;octets lus&lt;br /&gt;
&lt;br /&gt;
==fermeture==&lt;br /&gt;
Un fois la lecture terminé on ferme le tube:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int close(int descripteur);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*descripteur : une extrémité du tube&lt;br /&gt;
&lt;br /&gt;
= Utilisation dans un contexte d&#039;exécution parallélisé =&lt;br /&gt;
== Utilisation uni-directionnel ==&lt;br /&gt;
Ci-dessous un exemple qui permet au père de communiquer avec ces fils:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/wait.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Nombre total de thread&lt;br /&gt;
#define NB_FORK 2&lt;br /&gt;
// Taille du message&lt;br /&gt;
#define LENGTH_MSG 30&lt;br /&gt;
// Tableau contenant le message&lt;br /&gt;
char message[LENGTH_MSG] = &amp;quot;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// Fonction exécutée dans le fork&lt;br /&gt;
void job(int * tube) {&lt;br /&gt;
	int tid = getpid();&lt;br /&gt;
	// timer pour attendre maximum 5 secondes&lt;br /&gt;
	int i = 5;&lt;br /&gt;
	while (i &amp;gt; 0) {&lt;br /&gt;
		// lecture dans le tube&lt;br /&gt;
		if (read(*tube, message, LENGTH_MSG) &amp;gt; 0) {&lt;br /&gt;
			printf(&amp;quot;Message du processus [%i] : %s&amp;quot;, tid, message);&lt;br /&gt;
			break;&lt;br /&gt;
		}&lt;br /&gt;
		sleep(1);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// Fonction qui attend chacun des processus fils&lt;br /&gt;
void waitForAll() {&lt;br /&gt;
	int status;&lt;br /&gt;
	pid_t pid;&lt;br /&gt;
	int n = 0;&lt;br /&gt;
	while (n &amp;lt; NB_FORK) {&lt;br /&gt;
		pid = wait(&amp;amp;status);&lt;br /&gt;
		n++;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_FORK; i++) {&lt;br /&gt;
		int tube[2];&lt;br /&gt;
		pipe(tube);&lt;br /&gt;
		pid_t pid = fork();&lt;br /&gt;
		if (pid == -1) {&lt;br /&gt;
			// Il y a une erreur&lt;br /&gt;
			perror(&amp;quot;fork&amp;quot;);&lt;br /&gt;
			return EXIT_FAILURE;&lt;br /&gt;
		} else if (pid == 0) {&lt;br /&gt;
			// On est dans le fils&lt;br /&gt;
			close(tube[1]);&lt;br /&gt;
			job(&amp;amp;tube[0]);&lt;br /&gt;
			close(tube[0]);&lt;br /&gt;
			exit(EXIT_SUCCESS);&lt;br /&gt;
		} else {&lt;br /&gt;
			// On est dans le père&lt;br /&gt;
			// Ecriture du message dans le tableau&lt;br /&gt;
			sprintf(message, &amp;quot;Fork [%i], je suis ton père !\n&amp;quot;, pid);&lt;br /&gt;
			// Ecriture du message dans le tube&lt;br /&gt;
			write(tube[1], message, LENGTH_MSG);&lt;br /&gt;
			close(tube[0]);&lt;br /&gt;
			close(tube[1]);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	waitForAll();&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Utilisation bidirectionnel ==&lt;br /&gt;
Dans cette exemple, on créer une structure qui va contenir les deux tubes pour plus de facilité:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/wait.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Structure avec des tubes full-duplex&lt;br /&gt;
typedef struct fdpipe {&lt;br /&gt;
	int father[2];&lt;br /&gt;
	int son[2];&lt;br /&gt;
} fdpipe;&lt;br /&gt;
&lt;br /&gt;
// Nombre total de thread&lt;br /&gt;
#define NB_FORK 10&lt;br /&gt;
// Taille du message&lt;br /&gt;
#define LENGTH_MSG 50&lt;br /&gt;
// Tableau contenant le message&lt;br /&gt;
char message[LENGTH_MSG] = &amp;quot;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// Fonction exécutée dans le fork&lt;br /&gt;
void job(fdpipe * pipes) {&lt;br /&gt;
	int tid = getpid();&lt;br /&gt;
	// timer pour attendre maximum 5 secondes&lt;br /&gt;
	int i = 5;&lt;br /&gt;
	while (i &amp;gt; 0) {&lt;br /&gt;
		// lecture dans le tube&lt;br /&gt;
		if (read((*pipes).father[0], message, LENGTH_MSG) &amp;gt; 0) {&lt;br /&gt;
			printf(&amp;quot;Message du processus [%i] : %s\n&amp;quot;, tid, message);&lt;br /&gt;
			// Ecriture du message dans le tableau&lt;br /&gt;
			sprintf(message, &amp;quot;je suis [%i] et j&#039;ai bien reçu ton message !\n&amp;quot;, tid);&lt;br /&gt;
			write((*pipes).son[1], message, LENGTH_MSG);&lt;br /&gt;
			break;&lt;br /&gt;
		}&lt;br /&gt;
		sleep(1);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// Fonction qui attend chacun des processus fils&lt;br /&gt;
void waitForAll() {&lt;br /&gt;
	int status;&lt;br /&gt;
	pid_t pid;&lt;br /&gt;
	int n = 0;&lt;br /&gt;
	while (n &amp;lt; NB_FORK) {&lt;br /&gt;
		pid = wait(&amp;amp;status);&lt;br /&gt;
		n++;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_FORK; i++) {&lt;br /&gt;
		fdpipe pipes;&lt;br /&gt;
		pipe(pipes.father);&lt;br /&gt;
		pipe(pipes.son);&lt;br /&gt;
		pid_t pid = fork();&lt;br /&gt;
		if (pid == -1) {&lt;br /&gt;
			// Il y a une erreur&lt;br /&gt;
			perror(&amp;quot;fork&amp;quot;);&lt;br /&gt;
			return EXIT_FAILURE;&lt;br /&gt;
		} else if (pid == 0) {&lt;br /&gt;
			// On est dans le fils&lt;br /&gt;
			close(pipes.father[1]);&lt;br /&gt;
			job(&amp;amp;pipes);&lt;br /&gt;
			close(pipes.father[0]);&lt;br /&gt;
			close(pipes.son[0]);&lt;br /&gt;
			close(pipes.son[1]);&lt;br /&gt;
			exit(EXIT_SUCCESS);&lt;br /&gt;
		} else {&lt;br /&gt;
			// On est dans le père&lt;br /&gt;
			// Ecriture du message dans le tableau&lt;br /&gt;
			sprintf(message, &amp;quot;Fork [%i], je suis ton père !\n&amp;quot;, pid);&lt;br /&gt;
			// Ecriture du message dans le tube&lt;br /&gt;
			write(pipes.father[1], message, LENGTH_MSG);&lt;br /&gt;
			while (i &amp;gt; 0) {&lt;br /&gt;
				// lecture dans le tube&lt;br /&gt;
				if (read(pipes.son[0], message, LENGTH_MSG) &amp;gt; 0) {&lt;br /&gt;
					printf(&amp;quot;Réponse du fils : %s\n&amp;quot;, message);&lt;br /&gt;
					break;&lt;br /&gt;
				}&lt;br /&gt;
				sleep(1);&lt;br /&gt;
			}&lt;br /&gt;
			close(pipes.father[1]);&lt;br /&gt;
			close(pipes.father[0]);&lt;br /&gt;
			close(pipes.son[0]);&lt;br /&gt;
			close(pipes.son[1]);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	waitForAll();&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Ce qui nous donne le résultat suivant :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Message du processus [18523] : Fork [18523], je suis ton père !&lt;br /&gt;
Réponse du fils : je suis [18523] et j&#039;ai bien reçu ton message !&lt;br /&gt;
Message du processus [18522] : Fork [18522], je suis ton père !&lt;br /&gt;
Message du processus [18524] : Fork [18524], je suis ton père !&lt;br /&gt;
Réponse du fils : je suis [18524] et j&#039;ai bien reçu ton message !&lt;br /&gt;
Message du processus [18525] : Fork [18525], je suis ton père !&lt;br /&gt;
Réponse du fils : je suis [18525] et j&#039;ai bien reçu ton message !&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
= Tube nommé =&lt;br /&gt;
== Fonctions utilisées ==&lt;br /&gt;
Il est possible de nommer un tube pour pouvoir y accéder depuis un autre processus. Pour ce faire nous allons utiliser les fonctions suivantes :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/stat.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int mkfifo (const char* name, mode_t mode);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* name est le nom du tube ; &lt;br /&gt;
* mode correspond aux permission du tube (idem &#039;&#039;chmod&#039;&#039;) ;&lt;br /&gt;
* le code retour de la fonction varie entre : &lt;br /&gt;
** EACCES : droits insuffisants pour créer le tube ;&lt;br /&gt;
** EEXIST : un tube de même nom existe déjà ;&lt;br /&gt;
** ENAMETOOLONG : nom trop long ;&lt;br /&gt;
** ENOENT : le chemin n&#039;existe pas ;&lt;br /&gt;
** ENOSPC : espace insuffisant sur le système de fichiers.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int open (const char* name, int options);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* name est le nom du tube ; &lt;br /&gt;
* options permet de désigner l&#039;extrémité du tube:&lt;br /&gt;
** O_WRONLY: pour l&#039;entrée ;&lt;br /&gt;
** O_RDONLY: pour la sortie.&lt;br /&gt;
* le retour est le descripteur pour la lecture ou l&#039;écriture ou -1 en cas d&#039;échec.&lt;br /&gt;
&lt;br /&gt;
== Exemple d&#039;utilisation ==&lt;br /&gt;
Ci-dessous un exemple d&#039;utilisation avec un thread qui lit dans un tube nommé:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/wait.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;pthread.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/stat.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Nom du tube&lt;br /&gt;
#define FIFO_NAME &amp;quot;/tmp/test.fifo&amp;quot;&lt;br /&gt;
// Longeur du buffer de lecture&lt;br /&gt;
#define BUFFER_LENGTH 30;&lt;br /&gt;
// Mode de lecture du tube&lt;br /&gt;
const mode_t FIFO_MODE = 0760;&lt;br /&gt;
&lt;br /&gt;
// Fonction exécutée par le thread&lt;br /&gt;
void * job(void * args) {&lt;br /&gt;
	// Récupération du descripteur en lecture&lt;br /&gt;
	int fdread;&lt;br /&gt;
	if ((fdread = open(FIFO_NAME, O_RDONLY)) == -1) {&lt;br /&gt;
		fprintf(stderr, &amp;quot;Impossible d&#039;ouvrir le tube en lecture: %s\n&amp;quot;,&lt;br /&gt;
				strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	// Création d&#039;un tampon pour stocker le contenu du tube&lt;br /&gt;
	char buffer[30];&lt;br /&gt;
	// Lecture du contenu du tube&lt;br /&gt;
	read(fdread, buffer, 30);&lt;br /&gt;
	// Affichage du contenu&lt;br /&gt;
	printf(&amp;quot;Le tube contient : %s&amp;quot;, buffer);&lt;br /&gt;
	// Fin de l&#039;exécution du thread&lt;br /&gt;
	pthread_exit(EXIT_SUCCESS);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
	// On supprime le tube s&#039;il existe déjà&lt;br /&gt;
	if (mkfifo(FIFO_NAME, FIFO_MODE) == -1) {&lt;br /&gt;
		printf(&amp;quot;Suppression du tube existant: %s\n&amp;quot;, FIFO_NAME);&lt;br /&gt;
		unlink(FIFO_NAME);&lt;br /&gt;
	}&lt;br /&gt;
	// Création de la variable qui va contenir le thread&lt;br /&gt;
	pthread_t thread;&lt;br /&gt;
	// Création du tube FIFO&lt;br /&gt;
	if (mkfifo(FIFO_NAME, FIFO_MODE) == -1) {&lt;br /&gt;
		printf(&amp;quot;Erreur de création du tube: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}else{&lt;br /&gt;
		printf(&amp;quot;Création du tube: %s\n&amp;quot;, FIFO_NAME);&lt;br /&gt;
	}&lt;br /&gt;
	// Création du thread&lt;br /&gt;
	pthread_create(&amp;amp;thread, NULL, job, NULL);&lt;br /&gt;
	// Récupération du descripteur en écriture&lt;br /&gt;
	int fdwrite;&lt;br /&gt;
	if ((fdwrite = open(FIFO_NAME, O_WRONLY)) == -1) {&lt;br /&gt;
		printf(&amp;quot;Impossible d&#039;ouvrir le tube en écriture: %s\n&amp;quot;,&lt;br /&gt;
				strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	// Ecriture dans le tube&lt;br /&gt;
	char message[] = &amp;quot;Bonjour thread&amp;quot;;&lt;br /&gt;
	write(fdwrite,  message, sizeof(message));&lt;br /&gt;
	// En attente de l&#039;éxécution du thread&lt;br /&gt;
	pthread_join(thread, NULL);&lt;br /&gt;
	// Fin de l&#039;exécution&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
{|style=&amp;quot;width:800px&amp;quot; align=&amp;quot;center&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
[[Fichier:Warning manual.jpg|centré|300px]]&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
Dans l&#039;exemple précédent, vous remarquerez que le thread est créé avant l&#039;ouverture du tube. C&#039;est absolument important de démarrer le thread avant, car la fonction &#039;&#039;open&#039;&#039; va bloquer le fil d&#039;exécution principal tant que le thread n&#039;ouvre pas le tube en lecture ! Pour eviter cela il faut utiliser l&#039;option &#039;&#039;&#039;O_NONBLOCK&#039;&#039;&#039;&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Magsss</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=C_pipe&amp;diff=3263</id>
		<title>C pipe</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=C_pipe&amp;diff=3263"/>
		<updated>2018-11-01T17:59:28Z</updated>

		<summary type="html">&lt;p&gt;Magsss : /* Introduction */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Un tube est un canal par lequel les informations circulent de manière uni-directionnelle. Un processus écrit dans l&#039;entrée du tube et un autre processus lit les informations en sortie.&lt;br /&gt;
&lt;br /&gt;
= Manipulation des tubes =&lt;br /&gt;
== Création ==&lt;br /&gt;
La première étape est la création d&#039;un tube:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int tube[2];&lt;br /&gt;
&lt;br /&gt;
int pipe(int tube[2]);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*tube[0] &amp;amp;rarr; contiendra le fichier descripteur de l&#039;extrémité de lecture&lt;br /&gt;
*tube[1] &amp;amp;rarr; contiendra le fichier descripteur de l&#039;extrémité d&#039;écriture&lt;br /&gt;
*Le retour sera :&lt;br /&gt;
** &#039;0&#039; si tout s&#039;est bien passé&lt;br /&gt;
** &#039;1&#039; si une erreur survient et errno est positionné&lt;br /&gt;
&lt;br /&gt;
== Écriture ==&lt;br /&gt;
Pour écrire dans un tube :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t write(int tube[1], const void *message, size_t longueur);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*tube[0] &amp;amp;rarr; le fichier descripteur de l&#039;extrémité d&#039;écriture&lt;br /&gt;
*message &amp;amp;rarr; le message à écrire&lt;br /&gt;
*longueur &amp;amp;rarr; la longueur du message&lt;br /&gt;
*ssize_t &amp;amp;rarr; le nombre d&#039;octets écrits&lt;br /&gt;
&lt;br /&gt;
== Lecture ==&lt;br /&gt;
Pour lire dans un tube :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
ssize_t read(int tube[0], void *message, size_t longueur);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*tube[1] &amp;amp;rarr; le fichier descripteur de l&#039;extrémité de lecture&lt;br /&gt;
*message &amp;amp;rarr; un tableau de caractère qui contiendra le message à lire&lt;br /&gt;
*longueur &amp;amp;rarr; la longueur du message à lire&lt;br /&gt;
*ssize_t &amp;amp;rarr; le nombre d&#039;octets lus&lt;br /&gt;
&lt;br /&gt;
==fermeture==&lt;br /&gt;
Un fois la lecture terminé on ferme le tube:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int close(int descripteur);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*descripteur : une extrémité du tube&lt;br /&gt;
&lt;br /&gt;
= Utilisation dans un contexte d&#039;exécution parallélisé =&lt;br /&gt;
== Utilisation uni-directionnel ==&lt;br /&gt;
Ci-dessous un exemple qui permet au père de communiquer avec ces fils:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/wait.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Nombre total de thread&lt;br /&gt;
#define NB_FORK 2&lt;br /&gt;
// Taille du message&lt;br /&gt;
#define LENGTH_MSG 30&lt;br /&gt;
// Tableau contenant le message&lt;br /&gt;
char message[LENGTH_MSG] = &amp;quot;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// Fonction exécutée dans le fork&lt;br /&gt;
void job(int * tube) {&lt;br /&gt;
	int tid = getpid();&lt;br /&gt;
	// timer pour attendre maximum 5 secondes&lt;br /&gt;
	int i = 5;&lt;br /&gt;
	while (i &amp;gt; 0) {&lt;br /&gt;
		// lecture dans le tube&lt;br /&gt;
		if (read(*tube, message, LENGTH_MSG) &amp;gt; 0) {&lt;br /&gt;
			printf(&amp;quot;Message du processus [%i] : %s&amp;quot;, tid, message);&lt;br /&gt;
			break;&lt;br /&gt;
		}&lt;br /&gt;
		sleep(1);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// Fonction qui attend chacun des processus fils&lt;br /&gt;
void waitForAll() {&lt;br /&gt;
	int status;&lt;br /&gt;
	pid_t pid;&lt;br /&gt;
	int n = 0;&lt;br /&gt;
	while (n &amp;lt; NB_FORK) {&lt;br /&gt;
		pid = wait(&amp;amp;status);&lt;br /&gt;
		n++;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_FORK; i++) {&lt;br /&gt;
		int tube[2];&lt;br /&gt;
		pipe(tube);&lt;br /&gt;
		pid_t pid = fork();&lt;br /&gt;
		if (pid == -1) {&lt;br /&gt;
			// Il y a une erreur&lt;br /&gt;
			perror(&amp;quot;fork&amp;quot;);&lt;br /&gt;
			return EXIT_FAILURE;&lt;br /&gt;
		} else if (pid == 0) {&lt;br /&gt;
			// On est dans le fils&lt;br /&gt;
			close(tube[1]);&lt;br /&gt;
			job(&amp;amp;tube[0]);&lt;br /&gt;
			close(tube[0]);&lt;br /&gt;
			exit(EXIT_SUCCESS);&lt;br /&gt;
		} else {&lt;br /&gt;
			// On est dans le père&lt;br /&gt;
			// Ecriture du message dans le tableau&lt;br /&gt;
			sprintf(message, &amp;quot;Fork [%i], je suis ton père !\n&amp;quot;, pid);&lt;br /&gt;
			// Ecriture du message dans le tube&lt;br /&gt;
			write(tube[1], message, LENGTH_MSG);&lt;br /&gt;
			close(tube[0]);&lt;br /&gt;
			close(tube[1]);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	waitForAll();&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Utilisation bidirectionnel ==&lt;br /&gt;
Dans cette exemple, on créer une structure qui va contenir les deux tubes pour plus de facilité:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/wait.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Structure avec des tubes full-duplex&lt;br /&gt;
typedef struct fdpipe {&lt;br /&gt;
	int father[2];&lt;br /&gt;
	int son[2];&lt;br /&gt;
} fdpipe;&lt;br /&gt;
&lt;br /&gt;
// Nombre total de thread&lt;br /&gt;
#define NB_FORK 10&lt;br /&gt;
// Taille du message&lt;br /&gt;
#define LENGTH_MSG 50&lt;br /&gt;
// Tableau contenant le message&lt;br /&gt;
char message[LENGTH_MSG] = &amp;quot;&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
// Fonction exécutée dans le fork&lt;br /&gt;
void job(fdpipe * pipes) {&lt;br /&gt;
	int tid = getpid();&lt;br /&gt;
	// timer pour attendre maximum 5 secondes&lt;br /&gt;
	int i = 5;&lt;br /&gt;
	while (i &amp;gt; 0) {&lt;br /&gt;
		// lecture dans le tube&lt;br /&gt;
		if (read((*pipes).father[0], message, LENGTH_MSG) &amp;gt; 0) {&lt;br /&gt;
			printf(&amp;quot;Message du processus [%i] : %s\n&amp;quot;, tid, message);&lt;br /&gt;
			// Ecriture du message dans le tableau&lt;br /&gt;
			sprintf(message, &amp;quot;je suis [%i] et j&#039;ai bien reçu ton message !\n&amp;quot;, tid);&lt;br /&gt;
			write((*pipes).son[1], message, LENGTH_MSG);&lt;br /&gt;
			break;&lt;br /&gt;
		}&lt;br /&gt;
		sleep(1);&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
// Fonction qui attend chacun des processus fils&lt;br /&gt;
void waitForAll() {&lt;br /&gt;
	int status;&lt;br /&gt;
	pid_t pid;&lt;br /&gt;
	int n = 0;&lt;br /&gt;
	while (n &amp;lt; NB_FORK) {&lt;br /&gt;
		pid = wait(&amp;amp;status);&lt;br /&gt;
		n++;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_FORK; i++) {&lt;br /&gt;
		fdpipe pipes;&lt;br /&gt;
		pipe(pipes.father);&lt;br /&gt;
		pipe(pipes.son);&lt;br /&gt;
		pid_t pid = fork();&lt;br /&gt;
		if (pid == -1) {&lt;br /&gt;
			// Il y a une erreur&lt;br /&gt;
			perror(&amp;quot;fork&amp;quot;);&lt;br /&gt;
			return EXIT_FAILURE;&lt;br /&gt;
		} else if (pid == 0) {&lt;br /&gt;
			// On est dans le fils&lt;br /&gt;
			close(pipes.father[1]);&lt;br /&gt;
			job(&amp;amp;pipes);&lt;br /&gt;
			close(pipes.father[0]);&lt;br /&gt;
			close(pipes.son[0]);&lt;br /&gt;
			close(pipes.son[1]);&lt;br /&gt;
			exit(EXIT_SUCCESS);&lt;br /&gt;
		} else {&lt;br /&gt;
			// On est dans le père&lt;br /&gt;
			// Ecriture du message dans le tableau&lt;br /&gt;
			sprintf(message, &amp;quot;Fork [%i], je suis ton père !\n&amp;quot;, pid);&lt;br /&gt;
			// Ecriture du message dans le tube&lt;br /&gt;
			write(pipes.father[1], message, LENGTH_MSG);&lt;br /&gt;
			while (i &amp;gt; 0) {&lt;br /&gt;
				// lecture dans le tube&lt;br /&gt;
				if (read(pipes.son[0], message, LENGTH_MSG) &amp;gt; 0) {&lt;br /&gt;
					printf(&amp;quot;Réponse du fils : %s\n&amp;quot;, message);&lt;br /&gt;
					break;&lt;br /&gt;
				}&lt;br /&gt;
				sleep(1);&lt;br /&gt;
			}&lt;br /&gt;
			close(pipes.father[1]);&lt;br /&gt;
			close(pipes.father[0]);&lt;br /&gt;
			close(pipes.son[0]);&lt;br /&gt;
			close(pipes.son[1]);&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	waitForAll();&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Ce qui nous donne le résultat suivant :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Message du processus [18523] : Fork [18523], je suis ton père !&lt;br /&gt;
Réponse du fils : je suis [18523] et j&#039;ai bien reçu ton message !&lt;br /&gt;
Message du processus [18522] : Fork [18522], je suis ton père !&lt;br /&gt;
Message du processus [18524] : Fork [18524], je suis ton père !&lt;br /&gt;
Réponse du fils : je suis [18524] et j&#039;ai bien reçu ton message !&lt;br /&gt;
Message du processus [18525] : Fork [18525], je suis ton père !&lt;br /&gt;
Réponse du fils : je suis [18525] et j&#039;ai bien reçu ton message !&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
= Tube nommé =&lt;br /&gt;
== Fonctions utilisées ==&lt;br /&gt;
Il est possible de nommer un tube pour pouvoir y accéder depuis un autre processus. Pour ce faire nous allons utiliser les fonctions suivantes :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/stat.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int mkfifo (const char* name, mode_t mode);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* name est le nom du tube ; &lt;br /&gt;
* mode correspond aux permission du tube (idem &#039;&#039;chmod&#039;&#039;) ;&lt;br /&gt;
* le code retour de la fonction varie entre : &lt;br /&gt;
** EACCES : droits insuffisants pour créer le tube ;&lt;br /&gt;
** EEXIST : un tube de même nom existe déjà ;&lt;br /&gt;
** ENAMETOOLONG : nom trop long ;&lt;br /&gt;
** ENOENT : le chemin n&#039;existe pas ;&lt;br /&gt;
** ENOSPC : espace insuffisant sur le système de fichiers.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int open (const char* name, int options);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* name est le nom du tube ; &lt;br /&gt;
* options permet de désigner l&#039;extrémité du tube:&lt;br /&gt;
** O_WRONLY: pour l&#039;entrée ;&lt;br /&gt;
** O_RDONLY: pour la sortie.&lt;br /&gt;
* le retour est le descripteur pour la lecture ou l&#039;écriture ou -1 en cas d&#039;échec.&lt;br /&gt;
&lt;br /&gt;
== Exemple d&#039;utilisation ==&lt;br /&gt;
Ci-dessous un exemple d&#039;utilisation avec un thread qui lit dans un tube nommé:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/wait.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;pthread.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/stat.h&amp;gt;&lt;br /&gt;
#include &amp;lt;fcntl.h&amp;gt;&lt;br /&gt;
#include &amp;lt;errno.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Nom du tube&lt;br /&gt;
#define FIFO_NAME &amp;quot;/tmp/test.fifo&amp;quot;&lt;br /&gt;
// Longeur du buffer de lecture&lt;br /&gt;
#define BUFFER_LENGTH 30;&lt;br /&gt;
// Mode de lecture du tube&lt;br /&gt;
const mode_t FIFO_MODE = 0760;&lt;br /&gt;
&lt;br /&gt;
// Fonction exécutée par le thread&lt;br /&gt;
void * job(void * args) {&lt;br /&gt;
	// Récupération du descripteur en lecture&lt;br /&gt;
	int fdread;&lt;br /&gt;
	if ((fdread = open(FIFO_NAME, O_RDONLY)) == -1) {&lt;br /&gt;
		fprintf(stderr, &amp;quot;Impossible d&#039;ouvrir le tube en lecture: %s\n&amp;quot;,&lt;br /&gt;
				strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	// Création d&#039;un tampon pour stocker le contenu du tube&lt;br /&gt;
	char buffer[30];&lt;br /&gt;
	// Lecture du contenu du tube&lt;br /&gt;
	read(fdread, buffer, 30);&lt;br /&gt;
	// Affichage du contenu&lt;br /&gt;
	printf(&amp;quot;Le tube contient : %s&amp;quot;, buffer);&lt;br /&gt;
	// Fin de l&#039;exécution du thread&lt;br /&gt;
	pthread_exit(EXIT_SUCCESS);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
	// On supprime le tube s&#039;il existe déjà&lt;br /&gt;
	if (mkfifo(FIFO_NAME, FIFO_MODE) == -1) {&lt;br /&gt;
		printf(&amp;quot;Suppression du tube existant: %s\n&amp;quot;, FIFO_NAME);&lt;br /&gt;
		unlink(FIFO_NAME);&lt;br /&gt;
	}&lt;br /&gt;
	// Création de la variable qui va contenir le thread&lt;br /&gt;
	pthread_t thread;&lt;br /&gt;
	// Création du tube FIFO&lt;br /&gt;
	if (mkfifo(FIFO_NAME, FIFO_MODE) == -1) {&lt;br /&gt;
		printf(&amp;quot;Erreur de création du tube: %s\n&amp;quot;, strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}else{&lt;br /&gt;
		printf(&amp;quot;Création du tube: %s\n&amp;quot;, FIFO_NAME);&lt;br /&gt;
	}&lt;br /&gt;
	// Création du thread&lt;br /&gt;
	pthread_create(&amp;amp;thread, NULL, job, NULL);&lt;br /&gt;
	// Récupération du descripteur en écriture&lt;br /&gt;
	int fdwrite;&lt;br /&gt;
	if ((fdwrite = open(FIFO_NAME, O_WRONLY)) == -1) {&lt;br /&gt;
		printf(&amp;quot;Impossible d&#039;ouvrir le tube en écriture: %s\n&amp;quot;,&lt;br /&gt;
				strerror(errno));&lt;br /&gt;
		exit(EXIT_FAILURE);&lt;br /&gt;
	}&lt;br /&gt;
	// Ecriture dans le tube&lt;br /&gt;
	char message[] = &amp;quot;Bonjour thread&amp;quot;;&lt;br /&gt;
	write(fdwrite,  message, sizeof(message));&lt;br /&gt;
	// En attente de l&#039;éxécution du thread&lt;br /&gt;
	pthread_join(thread, NULL);&lt;br /&gt;
	// Fin de l&#039;exécution&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
{|style=&amp;quot;width:800px&amp;quot; align=&amp;quot;center&amp;quot;&lt;br /&gt;
|&lt;br /&gt;
[[Fichier:Warning manual.jpg|centré|300px]]&lt;br /&gt;
|valign=&amp;quot;top&amp;quot;|&lt;br /&gt;
Dans l&#039;exemple précédent, vous remarquerez que le thread est créé avant l&#039;ouverture du tube. C&#039;est absolument important de démarrer le thread avant, car la fonction &#039;&#039;open&#039;&#039; va bloquer le fil d&#039;exécution principal tant que le thread n&#039;ouvre pas le tube en lecture ! Pour eviter cela il faut utiliser l&#039;option &#039;&#039;&#039;O_NONBLOCK&#039;&#039;&#039;&lt;br /&gt;
|}&lt;/div&gt;</summary>
		<author><name>Magsss</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=C_fork&amp;diff=3262</id>
		<title>C fork</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=C_fork&amp;diff=3262"/>
		<updated>2018-11-01T17:58:10Z</updated>

		<summary type="html">&lt;p&gt;Magsss : /* Démonisation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Un fork est un processus lourd, comprenez par là que l&#039;intégralité du contexte d&#039;exécution du processus père est recopié dans le &#039;&#039;nouveau&#039;&#039; processus fils. Ne cherchez pas, comme avec les [[C_pthread | threads]], a échanger des variables entre les processus car cela ne marchera pas. Il faut utiliser des techniques de communication inter-processus comme les tubes, sockets ou encore sémaphores...&lt;br /&gt;
&lt;br /&gt;
= Fonctionnement =&lt;br /&gt;
Pour créer un fork, il suffit d&#039;appeler la fonction du même nom. Cette fonction aura différentes valeurs de retour en fonction du processus dans lequel on se trouve:&lt;br /&gt;
*-1 &amp;amp;rarr; il y a une erreur ;&lt;br /&gt;
*0  &amp;amp;rarr; on est dans le processus fils ;&lt;br /&gt;
*Le PID du fils &amp;amp;rarr; on est dans le processus père.&lt;br /&gt;
&lt;br /&gt;
La valeur retournée par la fonction fork est de type &#039;&#039;pid_t&#039;&#039;, c&#039;est pourquoi il faut obligatoirement inclure le fichier &#039;&#039;&amp;lt;sys/types.h&amp;gt;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Dans le cas d&#039;une erreur, celle-ci sera accessible grâce à la variable globale &#039;&#039;errno&#039;&#039;, il faudra donc inclure le fichier en-tête &#039;&#039;&amp;lt;errno.h&amp;gt;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
= Fonctions annexes =&lt;br /&gt;
Voici quelques fonctions annexes bien pratiques:&lt;br /&gt;
* getpid &amp;amp;rarr; retourne le PID du processus appelant, de type &#039;&#039;pid_t&#039;&#039; :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
printf(&amp;quot;Mon PID est %i\n&amp;quot;, getpid()); // affichera par exemple &amp;quot;Mon PID est 14804&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* getppid &amp;amp;rarr; retourne le PPID du processus appelant, de type &#039;&#039;pid_t&#039;&#039; :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
printf(&amp;quot;Mon PPID est %i\n&amp;quot;, getppid()); // affichera par exemple &amp;quot;Mon PPID est 14403&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* getuid &amp;amp;rarr; retourne l&#039;UID du processus appelant, de type &#039;&#039;uid_t&#039;&#039; :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
printf(&amp;quot;Mon UID est %i\n&amp;quot;, getuid()); // affichera par exemple &amp;quot;Mon UID est 0&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* getgid &amp;amp;rarr; retourne le GID du processus appelant, de type &#039;&#039;gid_t&#039;&#039; :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
printf(&amp;quot;Mon GID est %i\n&amp;quot;, getgid()); // affichera par exemple &amp;quot;Mon GID est 0&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* exit &amp;amp;rarr; permet de quitter le programme peu importe où dans le code :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
exit(EXIT_SUCCESS); // quitte le programme en retournant la valeur de succès&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* wait &amp;amp;rarr; permet au processus fils d&#039;être libéré pour éviter les zombies:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/wait.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int status;&lt;br /&gt;
&lt;br /&gt;
wait(&amp;amp;status); // la valeur de retour du processus fils sera dans la variable status&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Création d&#039;un fork =&lt;br /&gt;
Ci-dessous un exemple de création d&#039;un processus lourd :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
	pid_t pid = fork();&lt;br /&gt;
	if (pid == -1) {&lt;br /&gt;
		// Il y a une erreur&lt;br /&gt;
		perror(&amp;quot;fork&amp;quot;);&lt;br /&gt;
		return EXIT_FAILURE;&lt;br /&gt;
	} else if (pid == 0) {&lt;br /&gt;
		// On est dans le fils&lt;br /&gt;
		printf(&amp;quot;Mon PID est %i et celui de mon père est %i\n&amp;quot;, getpid(),	getppid());&lt;br /&gt;
	} else {&lt;br /&gt;
		// On est dans le père&lt;br /&gt;
		printf(&amp;quot;Mon PID est %i et celui de mon fils est %i\n&amp;quot;, getpid(), pid);&lt;br /&gt;
	}&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Cet exemple peut retourner la sortie suivante :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Mon PID est 15150 et celui de mon fils est 15151&lt;br /&gt;
Mon PID est 15151 et celui de mon père est 15150&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Cependant, on peut très bien avoir cela:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Mon PID est 15197 et celui de mon fils est 15198&lt;br /&gt;
Mon PID est 15198 et celui de mon père est 1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On voit que le numéro de processus du père est devenu &#039;&#039;1&#039;&#039;. Cela vient du fait que, lorsque le père se termine, le fils est automatiquement rattaché au processus de PID &#039;&#039;1&#039;&#039; pour ne pas devenir un processus [https://en.wikipedia.org/wiki/Orphan_process &#039;&#039;orphelin&#039;&#039;].&lt;br /&gt;
&lt;br /&gt;
= Création de plusieurs forks =&lt;br /&gt;
Lorsque l&#039;on a plusieurs forks à gérer, il faut garder la trace de chacun des numéros de processus et également attendre la fin de chacun des fils.&lt;br /&gt;
&lt;br /&gt;
Ci-dessous un code qui démarre plusieurs forks qui vont incrémenter un compteur.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/wait.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Nombre total de thread&lt;br /&gt;
#define NB_FORK 2&lt;br /&gt;
// Limite de l&#039;incrément&lt;br /&gt;
#define INCREMENT_LIMIT 2&lt;br /&gt;
// Initialisation de la donnée&lt;br /&gt;
int data = 0;&lt;br /&gt;
&lt;br /&gt;
// Fonction exécutée dans le fork&lt;br /&gt;
void job() {&lt;br /&gt;
	int tid = getpid();&lt;br /&gt;
	while (data &amp;lt; INCREMENT_LIMIT) {&lt;br /&gt;
		data++;&lt;br /&gt;
		printf(&amp;quot;fork [ %i ] data [ %i ]\n&amp;quot;, tid, data);&lt;br /&gt;
		// Pause l&#039;exécution du thread pendant 1 seconde&lt;br /&gt;
		sleep(1);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Fin du fork %i\n&amp;quot;, tid);&lt;br /&gt;
	exit(EXIT_SUCCESS);&lt;br /&gt;
}&lt;br /&gt;
// Fonction qui attend chacun des processus fils&lt;br /&gt;
void waitForAll() {&lt;br /&gt;
	int status;&lt;br /&gt;
	pid_t pid;&lt;br /&gt;
	int n = 0;&lt;br /&gt;
	while (n &amp;lt; NB_FORK) {&lt;br /&gt;
		pid = wait(&amp;amp;status);&lt;br /&gt;
		printf(&amp;quot;Fork [%i] terminé avec le code %i\n&amp;quot;, pid, status);&lt;br /&gt;
		n++;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_FORK; i++) {&lt;br /&gt;
		pid_t pid = fork();&lt;br /&gt;
		if (pid == -1) {&lt;br /&gt;
			// Il y a une erreur&lt;br /&gt;
			perror(&amp;quot;fork&amp;quot;);&lt;br /&gt;
			return EXIT_FAILURE;&lt;br /&gt;
		} else if (pid == 0) {&lt;br /&gt;
			// On est dans le fils&lt;br /&gt;
			job();&lt;br /&gt;
		} else {&lt;br /&gt;
			// On est dans le père&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	waitForAll();&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Ce code donne, par exemple, le résultat suivant:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
fork [ 15964 ] data [ 1 ]&lt;br /&gt;
fork [ 15963 ] data [ 1 ]&lt;br /&gt;
fork [ 15964 ] data [ 2 ]&lt;br /&gt;
fork [ 15963 ] data [ 2 ]&lt;br /&gt;
Fin du fork 15964&lt;br /&gt;
Fork [15964] terminé avec le code 0&lt;br /&gt;
Fin du fork 15963&lt;br /&gt;
Fork [15963] terminé avec le code 0&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On voit bien que le contexte est recopié car les compteurs sont modifiés indépendamment.&lt;br /&gt;
&lt;br /&gt;
=Démonisation=&lt;br /&gt;
Il peut être intéressant pour un programme, lorsque l&#039;on cherche à écrire un serveur, que celui-ci se détache du processus qui l&#039;a démarré : ce procédé s&#039;appelle la démonisation.&lt;br /&gt;
&lt;br /&gt;
Pour accomplir cela, rien de plus simple, il suffit d&#039;appeler la fonction suivante:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int daemon(int nochdir, int noclose);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* &#039;&#039;nochdir&#039;&#039; &amp;amp;rarr; si à &#039;&#039;0&#039;&#039;, le démon utilise comme répertoire de travail la racine (&#039;&#039;/&#039;&#039;), sinon il conserve le répertoire de travail courant.&lt;br /&gt;
* &#039;&#039;noclose&#039;&#039; &amp;amp;rarr; si à &#039;&#039;0&#039;&#039;, le démon redirige la sortie standard dans &#039;&#039;/dev/null&#039;&#039;, sinon aucun changement n&#039;est fait.&lt;br /&gt;
Pour comprendre le phénomène, utilisons le programme suivant :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
	printf(&amp;quot;PID du processus principal [ %i ]\n&amp;quot;, getpid());&lt;br /&gt;
	sleep(10);&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Il ne reste plus qu&#039;à compiler ce programme:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
gcc -o daemon.bin daemon.c&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Lorsque l&#039;on exécute le programme, il ne rend pas la main et on doit attendre 10 secondes  .&lt;br /&gt;
&lt;br /&gt;
Si on ajoute, avant la ligne :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
sleep(10);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
la ligne suivante:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
daemon(0,0);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
le programme se détache du processus courant et on peut le voir grâce à la commande &#039;&#039;ps&#039;&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# ./daemon.bin&lt;br /&gt;
PID du processus principal [ 16559 ]&lt;br /&gt;
# ps -ef | grep ./daemon.bin&lt;br /&gt;
root      16560      1  0 07:52 ?        00:00:00 ./daemon.bin&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On peut le voir plus simplement sur cet exemple :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
	printf(&amp;quot;PID du processus principal [ %i ]\n&amp;quot;, getpid());&lt;br /&gt;
	// Passage du programme en arrière plan sans modifier la sortie standard&lt;br /&gt;
	daemon(0, 1);&lt;br /&gt;
	printf(&amp;quot;PID du démon [ %i ]\n&amp;quot;, getpid());&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
L&#039;exécution donne, par exemple :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
PID du processus principal [ 16613 ]&lt;br /&gt;
PID du démon [ 16614 ]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Magsss</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=C_fork&amp;diff=3261</id>
		<title>C fork</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=C_fork&amp;diff=3261"/>
		<updated>2018-11-01T17:57:01Z</updated>

		<summary type="html">&lt;p&gt;Magsss : /* Création de plusieurs forks */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Un fork est un processus lourd, comprenez par là que l&#039;intégralité du contexte d&#039;exécution du processus père est recopié dans le &#039;&#039;nouveau&#039;&#039; processus fils. Ne cherchez pas, comme avec les [[C_pthread | threads]], a échanger des variables entre les processus car cela ne marchera pas. Il faut utiliser des techniques de communication inter-processus comme les tubes, sockets ou encore sémaphores...&lt;br /&gt;
&lt;br /&gt;
= Fonctionnement =&lt;br /&gt;
Pour créer un fork, il suffit d&#039;appeler la fonction du même nom. Cette fonction aura différentes valeurs de retour en fonction du processus dans lequel on se trouve:&lt;br /&gt;
*-1 &amp;amp;rarr; il y a une erreur ;&lt;br /&gt;
*0  &amp;amp;rarr; on est dans le processus fils ;&lt;br /&gt;
*Le PID du fils &amp;amp;rarr; on est dans le processus père.&lt;br /&gt;
&lt;br /&gt;
La valeur retournée par la fonction fork est de type &#039;&#039;pid_t&#039;&#039;, c&#039;est pourquoi il faut obligatoirement inclure le fichier &#039;&#039;&amp;lt;sys/types.h&amp;gt;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Dans le cas d&#039;une erreur, celle-ci sera accessible grâce à la variable globale &#039;&#039;errno&#039;&#039;, il faudra donc inclure le fichier en-tête &#039;&#039;&amp;lt;errno.h&amp;gt;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
= Fonctions annexes =&lt;br /&gt;
Voici quelques fonctions annexes bien pratiques:&lt;br /&gt;
* getpid &amp;amp;rarr; retourne le PID du processus appelant, de type &#039;&#039;pid_t&#039;&#039; :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
printf(&amp;quot;Mon PID est %i\n&amp;quot;, getpid()); // affichera par exemple &amp;quot;Mon PID est 14804&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* getppid &amp;amp;rarr; retourne le PPID du processus appelant, de type &#039;&#039;pid_t&#039;&#039; :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
printf(&amp;quot;Mon PPID est %i\n&amp;quot;, getppid()); // affichera par exemple &amp;quot;Mon PPID est 14403&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* getuid &amp;amp;rarr; retourne l&#039;UID du processus appelant, de type &#039;&#039;uid_t&#039;&#039; :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
printf(&amp;quot;Mon UID est %i\n&amp;quot;, getuid()); // affichera par exemple &amp;quot;Mon UID est 0&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* getgid &amp;amp;rarr; retourne le GID du processus appelant, de type &#039;&#039;gid_t&#039;&#039; :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
printf(&amp;quot;Mon GID est %i\n&amp;quot;, getgid()); // affichera par exemple &amp;quot;Mon GID est 0&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* exit &amp;amp;rarr; permet de quitter le programme peu importe où dans le code :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
exit(EXIT_SUCCESS); // quitte le programme en retournant la valeur de succès&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* wait &amp;amp;rarr; permet au processus fils d&#039;être libéré pour éviter les zombies:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/wait.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int status;&lt;br /&gt;
&lt;br /&gt;
wait(&amp;amp;status); // la valeur de retour du processus fils sera dans la variable status&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Création d&#039;un fork =&lt;br /&gt;
Ci-dessous un exemple de création d&#039;un processus lourd :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
	pid_t pid = fork();&lt;br /&gt;
	if (pid == -1) {&lt;br /&gt;
		// Il y a une erreur&lt;br /&gt;
		perror(&amp;quot;fork&amp;quot;);&lt;br /&gt;
		return EXIT_FAILURE;&lt;br /&gt;
	} else if (pid == 0) {&lt;br /&gt;
		// On est dans le fils&lt;br /&gt;
		printf(&amp;quot;Mon PID est %i et celui de mon père est %i\n&amp;quot;, getpid(),	getppid());&lt;br /&gt;
	} else {&lt;br /&gt;
		// On est dans le père&lt;br /&gt;
		printf(&amp;quot;Mon PID est %i et celui de mon fils est %i\n&amp;quot;, getpid(), pid);&lt;br /&gt;
	}&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Cet exemple peut retourner la sortie suivante :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Mon PID est 15150 et celui de mon fils est 15151&lt;br /&gt;
Mon PID est 15151 et celui de mon père est 15150&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Cependant, on peut très bien avoir cela:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Mon PID est 15197 et celui de mon fils est 15198&lt;br /&gt;
Mon PID est 15198 et celui de mon père est 1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On voit que le numéro de processus du père est devenu &#039;&#039;1&#039;&#039;. Cela vient du fait que, lorsque le père se termine, le fils est automatiquement rattaché au processus de PID &#039;&#039;1&#039;&#039; pour ne pas devenir un processus [https://en.wikipedia.org/wiki/Orphan_process &#039;&#039;orphelin&#039;&#039;].&lt;br /&gt;
&lt;br /&gt;
= Création de plusieurs forks =&lt;br /&gt;
Lorsque l&#039;on a plusieurs forks à gérer, il faut garder la trace de chacun des numéros de processus et également attendre la fin de chacun des fils.&lt;br /&gt;
&lt;br /&gt;
Ci-dessous un code qui démarre plusieurs forks qui vont incrémenter un compteur.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/wait.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Nombre total de thread&lt;br /&gt;
#define NB_FORK 2&lt;br /&gt;
// Limite de l&#039;incrément&lt;br /&gt;
#define INCREMENT_LIMIT 2&lt;br /&gt;
// Initialisation de la donnée&lt;br /&gt;
int data = 0;&lt;br /&gt;
&lt;br /&gt;
// Fonction exécutée dans le fork&lt;br /&gt;
void job() {&lt;br /&gt;
	int tid = getpid();&lt;br /&gt;
	while (data &amp;lt; INCREMENT_LIMIT) {&lt;br /&gt;
		data++;&lt;br /&gt;
		printf(&amp;quot;fork [ %i ] data [ %i ]\n&amp;quot;, tid, data);&lt;br /&gt;
		// Pause l&#039;exécution du thread pendant 1 seconde&lt;br /&gt;
		sleep(1);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Fin du fork %i\n&amp;quot;, tid);&lt;br /&gt;
	exit(EXIT_SUCCESS);&lt;br /&gt;
}&lt;br /&gt;
// Fonction qui attend chacun des processus fils&lt;br /&gt;
void waitForAll() {&lt;br /&gt;
	int status;&lt;br /&gt;
	pid_t pid;&lt;br /&gt;
	int n = 0;&lt;br /&gt;
	while (n &amp;lt; NB_FORK) {&lt;br /&gt;
		pid = wait(&amp;amp;status);&lt;br /&gt;
		printf(&amp;quot;Fork [%i] terminé avec le code %i\n&amp;quot;, pid, status);&lt;br /&gt;
		n++;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_FORK; i++) {&lt;br /&gt;
		pid_t pid = fork();&lt;br /&gt;
		if (pid == -1) {&lt;br /&gt;
			// Il y a une erreur&lt;br /&gt;
			perror(&amp;quot;fork&amp;quot;);&lt;br /&gt;
			return EXIT_FAILURE;&lt;br /&gt;
		} else if (pid == 0) {&lt;br /&gt;
			// On est dans le fils&lt;br /&gt;
			job();&lt;br /&gt;
		} else {&lt;br /&gt;
			// On est dans le père&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	waitForAll();&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Ce code donne, par exemple, le résultat suivant:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
fork [ 15964 ] data [ 1 ]&lt;br /&gt;
fork [ 15963 ] data [ 1 ]&lt;br /&gt;
fork [ 15964 ] data [ 2 ]&lt;br /&gt;
fork [ 15963 ] data [ 2 ]&lt;br /&gt;
Fin du fork 15964&lt;br /&gt;
Fork [15964] terminé avec le code 0&lt;br /&gt;
Fin du fork 15963&lt;br /&gt;
Fork [15963] terminé avec le code 0&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On voit bien que le contexte est recopié car les compteurs sont modifiés indépendamment.&lt;br /&gt;
&lt;br /&gt;
=Démonisation=&lt;br /&gt;
Il peut être intéressant pour un programme, lorsque l&#039;on cherche à écrire un serveur, que celui-ci se détache du processus qui l&#039;a démarré : ce procédé s&#039;appelle la démonisation.&lt;br /&gt;
&lt;br /&gt;
Pour accomplir cela, rien de plus simple, il suffit d&#039;appeler la fonction suivante:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int daemon(int nochdir, int noclose);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* &#039;&#039;nochdir&#039;&#039; &amp;amp;rarr; si à &#039;&#039;0&#039;&#039;, le démon utilise comme répertoire de travail la racine (&#039;&#039;/&#039;&#039;), sinon il conserve le répertoire de travail courant.&lt;br /&gt;
* &#039;&#039;noclose&#039;&#039; &amp;amp;rarr; si à &#039;&#039;0&#039;&#039;, le démon redirige la sortie standard dans &#039;&#039;/dev/null&#039;&#039;, sinon aucun changement n&#039;est fait.&lt;br /&gt;
Pour comprendre le phénomène, utilisons le programme suivant :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
	printf(&amp;quot;PID du processus principal [ %i ]\n&amp;quot;, getpid());&lt;br /&gt;
	sleep(10);&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Il ne reste plus qu&#039;à compiler ce programme:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
gcc -o daemon.bin daemon.c&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Lorsque l&#039;on exécute le programme, il ne rend pas la main et on doit attendre 10 secondes  .&lt;br /&gt;
&lt;br /&gt;
Si on ajoute, avant la ligne :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
sleep(10);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
La ligne suivante:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
daemon(0,0);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
le programme se détache du processus courant et on peut le voir grâce à la commande &#039;&#039;ps&#039;&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# ./daemon.bin&lt;br /&gt;
PID du processus principal [ 16559 ]&lt;br /&gt;
# ps -ef | grep ./daemon.bin&lt;br /&gt;
root      16560      1  0 07:52 ?        00:00:00 ./daemon.bin&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On peut le voir plus simplement sur cet exemple :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
	printf(&amp;quot;PID du processus principal [ %i ]\n&amp;quot;, getpid());&lt;br /&gt;
	// Passage du programme en arrière plan sans modifier la sortie standard&lt;br /&gt;
	daemon(0, 1);&lt;br /&gt;
	printf(&amp;quot;PID du démon [ %i ]\n&amp;quot;, getpid());&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
L&#039;exécution donne, par exemple :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
PID du processus principal [ 16613 ]&lt;br /&gt;
PID du démon [ 16614 ]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Magsss</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=C_fork&amp;diff=3260</id>
		<title>C fork</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=C_fork&amp;diff=3260"/>
		<updated>2018-11-01T17:56:21Z</updated>

		<summary type="html">&lt;p&gt;Magsss : /* Création d&amp;#039;un fork */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Un fork est un processus lourd, comprenez par là que l&#039;intégralité du contexte d&#039;exécution du processus père est recopié dans le &#039;&#039;nouveau&#039;&#039; processus fils. Ne cherchez pas, comme avec les [[C_pthread | threads]], a échanger des variables entre les processus car cela ne marchera pas. Il faut utiliser des techniques de communication inter-processus comme les tubes, sockets ou encore sémaphores...&lt;br /&gt;
&lt;br /&gt;
= Fonctionnement =&lt;br /&gt;
Pour créer un fork, il suffit d&#039;appeler la fonction du même nom. Cette fonction aura différentes valeurs de retour en fonction du processus dans lequel on se trouve:&lt;br /&gt;
*-1 &amp;amp;rarr; il y a une erreur ;&lt;br /&gt;
*0  &amp;amp;rarr; on est dans le processus fils ;&lt;br /&gt;
*Le PID du fils &amp;amp;rarr; on est dans le processus père.&lt;br /&gt;
&lt;br /&gt;
La valeur retournée par la fonction fork est de type &#039;&#039;pid_t&#039;&#039;, c&#039;est pourquoi il faut obligatoirement inclure le fichier &#039;&#039;&amp;lt;sys/types.h&amp;gt;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Dans le cas d&#039;une erreur, celle-ci sera accessible grâce à la variable globale &#039;&#039;errno&#039;&#039;, il faudra donc inclure le fichier en-tête &#039;&#039;&amp;lt;errno.h&amp;gt;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
= Fonctions annexes =&lt;br /&gt;
Voici quelques fonctions annexes bien pratiques:&lt;br /&gt;
* getpid &amp;amp;rarr; retourne le PID du processus appelant, de type &#039;&#039;pid_t&#039;&#039; :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
printf(&amp;quot;Mon PID est %i\n&amp;quot;, getpid()); // affichera par exemple &amp;quot;Mon PID est 14804&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* getppid &amp;amp;rarr; retourne le PPID du processus appelant, de type &#039;&#039;pid_t&#039;&#039; :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
printf(&amp;quot;Mon PPID est %i\n&amp;quot;, getppid()); // affichera par exemple &amp;quot;Mon PPID est 14403&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* getuid &amp;amp;rarr; retourne l&#039;UID du processus appelant, de type &#039;&#039;uid_t&#039;&#039; :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
printf(&amp;quot;Mon UID est %i\n&amp;quot;, getuid()); // affichera par exemple &amp;quot;Mon UID est 0&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* getgid &amp;amp;rarr; retourne le GID du processus appelant, de type &#039;&#039;gid_t&#039;&#039; :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
printf(&amp;quot;Mon GID est %i\n&amp;quot;, getgid()); // affichera par exemple &amp;quot;Mon GID est 0&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* exit &amp;amp;rarr; permet de quitter le programme peu importe où dans le code :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
exit(EXIT_SUCCESS); // quitte le programme en retournant la valeur de succès&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* wait &amp;amp;rarr; permet au processus fils d&#039;être libéré pour éviter les zombies:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/wait.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int status;&lt;br /&gt;
&lt;br /&gt;
wait(&amp;amp;status); // la valeur de retour du processus fils sera dans la variable status&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Création d&#039;un fork =&lt;br /&gt;
Ci-dessous un exemple de création d&#039;un processus lourd :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
	pid_t pid = fork();&lt;br /&gt;
	if (pid == -1) {&lt;br /&gt;
		// Il y a une erreur&lt;br /&gt;
		perror(&amp;quot;fork&amp;quot;);&lt;br /&gt;
		return EXIT_FAILURE;&lt;br /&gt;
	} else if (pid == 0) {&lt;br /&gt;
		// On est dans le fils&lt;br /&gt;
		printf(&amp;quot;Mon PID est %i et celui de mon père est %i\n&amp;quot;, getpid(),	getppid());&lt;br /&gt;
	} else {&lt;br /&gt;
		// On est dans le père&lt;br /&gt;
		printf(&amp;quot;Mon PID est %i et celui de mon fils est %i\n&amp;quot;, getpid(), pid);&lt;br /&gt;
	}&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Cet exemple peut retourner la sortie suivante :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Mon PID est 15150 et celui de mon fils est 15151&lt;br /&gt;
Mon PID est 15151 et celui de mon père est 15150&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Cependant, on peut très bien avoir cela:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Mon PID est 15197 et celui de mon fils est 15198&lt;br /&gt;
Mon PID est 15198 et celui de mon père est 1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On voit que le numéro de processus du père est devenu &#039;&#039;1&#039;&#039;. Cela vient du fait que, lorsque le père se termine, le fils est automatiquement rattaché au processus de PID &#039;&#039;1&#039;&#039; pour ne pas devenir un processus [https://en.wikipedia.org/wiki/Orphan_process &#039;&#039;orphelin&#039;&#039;].&lt;br /&gt;
&lt;br /&gt;
= Création de plusieurs forks =&lt;br /&gt;
Lorsque l&#039;on a plusieurs forks à gérer, il faut garder la trace de chacun des numéros de processus et également attendre la fin de chacun des fils.&lt;br /&gt;
&lt;br /&gt;
Ci-dessous un code qui démarre plusieurs forks qui vont incrémenter un compteur.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/wait.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Nombre total de thread&lt;br /&gt;
#define NB_FORK 2&lt;br /&gt;
// Limite de l&#039;incrément&lt;br /&gt;
#define INCREMENT_LIMIT 2&lt;br /&gt;
// Initialisation de la donnée&lt;br /&gt;
int data = 0;&lt;br /&gt;
&lt;br /&gt;
// Fonction exécutée dans le fork&lt;br /&gt;
void job() {&lt;br /&gt;
	int tid = getpid();&lt;br /&gt;
	while (data &amp;lt; INCREMENT_LIMIT) {&lt;br /&gt;
		data++;&lt;br /&gt;
		printf(&amp;quot;fork [ %i ] data [ %i ]\n&amp;quot;, tid, data);&lt;br /&gt;
		// Pause l&#039;exécution du thread pendant 1 seconde&lt;br /&gt;
		sleep(1);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Fin du fork %i\n&amp;quot;, tid);&lt;br /&gt;
	exit(EXIT_SUCCESS);&lt;br /&gt;
}&lt;br /&gt;
// Fonction qui attend chacun des processus fils&lt;br /&gt;
void waitForAll() {&lt;br /&gt;
	int status;&lt;br /&gt;
	pid_t pid;&lt;br /&gt;
	int n = 0;&lt;br /&gt;
	while (n &amp;lt; NB_FORK) {&lt;br /&gt;
		pid = wait(&amp;amp;status);&lt;br /&gt;
		printf(&amp;quot;Fork [%i] terminé avec le code %i\n&amp;quot;, pid, status);&lt;br /&gt;
		n++;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_FORK; i++) {&lt;br /&gt;
		pid_t pid = fork();&lt;br /&gt;
		if (pid == -1) {&lt;br /&gt;
			// Il y a une erreur&lt;br /&gt;
			perror(&amp;quot;fork&amp;quot;);&lt;br /&gt;
			return EXIT_FAILURE;&lt;br /&gt;
		} else if (pid == 0) {&lt;br /&gt;
			// On est dans le fils&lt;br /&gt;
			job();&lt;br /&gt;
		} else {&lt;br /&gt;
			// On est dans le père&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	waitForAll();&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Ce code donne, par exemple, le résultat suivant:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
fork [ 15964 ] data [ 1 ]&lt;br /&gt;
fork [ 15963 ] data [ 1 ]&lt;br /&gt;
fork [ 15964 ] data [ 2 ]&lt;br /&gt;
fork [ 15963 ] data [ 2 ]&lt;br /&gt;
Fin du fork 15964&lt;br /&gt;
Fork [15964] terminé avec le code 0&lt;br /&gt;
Fin du fork 15963&lt;br /&gt;
Fork [15963] terminé avec le code 0&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On voit bien que le contexte est recopié car les compteurs sont modifié indépendamment.&lt;br /&gt;
&lt;br /&gt;
=Démonisation=&lt;br /&gt;
Il peut être intéressant pour un programme, lorsque l&#039;on cherche à écrire un serveur, que celui-ci se détache du processus qui l&#039;a démarré : ce procédé s&#039;appelle la démonisation.&lt;br /&gt;
&lt;br /&gt;
Pour accomplir cela, rien de plus simple, il suffit d&#039;appeler la fonction suivante:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int daemon(int nochdir, int noclose);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* &#039;&#039;nochdir&#039;&#039; &amp;amp;rarr; si à &#039;&#039;0&#039;&#039;, le démon utilise comme répertoire de travail la racine (&#039;&#039;/&#039;&#039;), sinon il conserve le répertoire de travail courant.&lt;br /&gt;
* &#039;&#039;noclose&#039;&#039; &amp;amp;rarr; si à &#039;&#039;0&#039;&#039;, le démon redirige la sortie standard dans &#039;&#039;/dev/null&#039;&#039;, sinon aucun changement n&#039;est fait.&lt;br /&gt;
Pour comprendre le phénomène, utilisons le programme suivant :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
	printf(&amp;quot;PID du processus principal [ %i ]\n&amp;quot;, getpid());&lt;br /&gt;
	sleep(10);&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Il ne reste plus qu&#039;à compiler ce programme:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
gcc -o daemon.bin daemon.c&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Lorsque l&#039;on exécute le programme, il ne rend pas la main et on doit attendre 10 secondes  .&lt;br /&gt;
&lt;br /&gt;
Si on ajoute, avant la ligne :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
sleep(10);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
La ligne suivante:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
daemon(0,0);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
le programme se détache du processus courant et on peut le voir grâce à la commande &#039;&#039;ps&#039;&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# ./daemon.bin&lt;br /&gt;
PID du processus principal [ 16559 ]&lt;br /&gt;
# ps -ef | grep ./daemon.bin&lt;br /&gt;
root      16560      1  0 07:52 ?        00:00:00 ./daemon.bin&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On peut le voir plus simplement sur cet exemple :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
	printf(&amp;quot;PID du processus principal [ %i ]\n&amp;quot;, getpid());&lt;br /&gt;
	// Passage du programme en arrière plan sans modifier la sortie standard&lt;br /&gt;
	daemon(0, 1);&lt;br /&gt;
	printf(&amp;quot;PID du démon [ %i ]\n&amp;quot;, getpid());&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
L&#039;exécution donne, par exemple :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
PID du processus principal [ 16613 ]&lt;br /&gt;
PID du démon [ 16614 ]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Magsss</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=C_fork&amp;diff=3259</id>
		<title>C fork</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=C_fork&amp;diff=3259"/>
		<updated>2018-11-01T17:55:56Z</updated>

		<summary type="html">&lt;p&gt;Magsss : /* Création d&amp;#039;un fork */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Un fork est un processus lourd, comprenez par là que l&#039;intégralité du contexte d&#039;exécution du processus père est recopié dans le &#039;&#039;nouveau&#039;&#039; processus fils. Ne cherchez pas, comme avec les [[C_pthread | threads]], a échanger des variables entre les processus car cela ne marchera pas. Il faut utiliser des techniques de communication inter-processus comme les tubes, sockets ou encore sémaphores...&lt;br /&gt;
&lt;br /&gt;
= Fonctionnement =&lt;br /&gt;
Pour créer un fork, il suffit d&#039;appeler la fonction du même nom. Cette fonction aura différentes valeurs de retour en fonction du processus dans lequel on se trouve:&lt;br /&gt;
*-1 &amp;amp;rarr; il y a une erreur ;&lt;br /&gt;
*0  &amp;amp;rarr; on est dans le processus fils ;&lt;br /&gt;
*Le PID du fils &amp;amp;rarr; on est dans le processus père.&lt;br /&gt;
&lt;br /&gt;
La valeur retournée par la fonction fork est de type &#039;&#039;pid_t&#039;&#039;, c&#039;est pourquoi il faut obligatoirement inclure le fichier &#039;&#039;&amp;lt;sys/types.h&amp;gt;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Dans le cas d&#039;une erreur, celle-ci sera accessible grâce à la variable globale &#039;&#039;errno&#039;&#039;, il faudra donc inclure le fichier en-tête &#039;&#039;&amp;lt;errno.h&amp;gt;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
= Fonctions annexes =&lt;br /&gt;
Voici quelques fonctions annexes bien pratiques:&lt;br /&gt;
* getpid &amp;amp;rarr; retourne le PID du processus appelant, de type &#039;&#039;pid_t&#039;&#039; :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
printf(&amp;quot;Mon PID est %i\n&amp;quot;, getpid()); // affichera par exemple &amp;quot;Mon PID est 14804&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* getppid &amp;amp;rarr; retourne le PPID du processus appelant, de type &#039;&#039;pid_t&#039;&#039; :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
printf(&amp;quot;Mon PPID est %i\n&amp;quot;, getppid()); // affichera par exemple &amp;quot;Mon PPID est 14403&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* getuid &amp;amp;rarr; retourne l&#039;UID du processus appelant, de type &#039;&#039;uid_t&#039;&#039; :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
printf(&amp;quot;Mon UID est %i\n&amp;quot;, getuid()); // affichera par exemple &amp;quot;Mon UID est 0&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* getgid &amp;amp;rarr; retourne le GID du processus appelant, de type &#039;&#039;gid_t&#039;&#039; :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
printf(&amp;quot;Mon GID est %i\n&amp;quot;, getgid()); // affichera par exemple &amp;quot;Mon GID est 0&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* exit &amp;amp;rarr; permet de quitter le programme peu importe où dans le code :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
exit(EXIT_SUCCESS); // quitte le programme en retournant la valeur de succès&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* wait &amp;amp;rarr; permet au processus fils d&#039;être libéré pour éviter les zombies:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/wait.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int status;&lt;br /&gt;
&lt;br /&gt;
wait(&amp;amp;status); // la valeur de retour du processus fils sera dans la variable status&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Création d&#039;un fork =&lt;br /&gt;
Ci-dessous un exemple de création d&#039;un processus lourd :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
	pid_t pid = fork();&lt;br /&gt;
	if (pid == -1) {&lt;br /&gt;
		// Il y a une erreur&lt;br /&gt;
		perror(&amp;quot;fork&amp;quot;);&lt;br /&gt;
		return EXIT_FAILURE;&lt;br /&gt;
	} else if (pid == 0) {&lt;br /&gt;
		// On est dans le fils&lt;br /&gt;
		printf(&amp;quot;Mon PID est %i et celui de mon père est %i\n&amp;quot;, getpid(),	getppid());&lt;br /&gt;
	} else {&lt;br /&gt;
		// On est dans le père&lt;br /&gt;
		printf(&amp;quot;Mon PID est %i et celui de mon fils est %i\n&amp;quot;, getpid(), pid);&lt;br /&gt;
	}&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Cet exemple peut retourner la sortie suivante :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Mon PID est 15150 et celui de mon fils est 15151&lt;br /&gt;
Mon PID est 15151 et celui de mon père est 15150&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Cependant, on peut très bien avoir cela:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Mon PID est 15197 et celui de mon fils est 15198&lt;br /&gt;
Mon PID est 15198 et celui de mon père est 1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On voit que le numéro de processus du père est devenu &#039;&#039;1&#039;&#039;. Cela vient du faite que, lorsque le père se termine, le fils est automatiquement rattaché au processus de PID &#039;&#039;1&#039;&#039; pour ne pas devenir un processus [https://en.wikipedia.org/wiki/Orphan_process &#039;&#039;orphelin&#039;&#039;].&lt;br /&gt;
&lt;br /&gt;
= Création de plusieurs forks =&lt;br /&gt;
Lorsque l&#039;on a plusieurs forks à gérer, il faut garder la trace de chacun des numéros de processus et également attendre la fin de chacun des fils.&lt;br /&gt;
&lt;br /&gt;
Ci-dessous un code qui démarre plusieurs forks qui vont incrémenter un compteur.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/wait.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Nombre total de thread&lt;br /&gt;
#define NB_FORK 2&lt;br /&gt;
// Limite de l&#039;incrément&lt;br /&gt;
#define INCREMENT_LIMIT 2&lt;br /&gt;
// Initialisation de la donnée&lt;br /&gt;
int data = 0;&lt;br /&gt;
&lt;br /&gt;
// Fonction exécutée dans le fork&lt;br /&gt;
void job() {&lt;br /&gt;
	int tid = getpid();&lt;br /&gt;
	while (data &amp;lt; INCREMENT_LIMIT) {&lt;br /&gt;
		data++;&lt;br /&gt;
		printf(&amp;quot;fork [ %i ] data [ %i ]\n&amp;quot;, tid, data);&lt;br /&gt;
		// Pause l&#039;exécution du thread pendant 1 seconde&lt;br /&gt;
		sleep(1);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Fin du fork %i\n&amp;quot;, tid);&lt;br /&gt;
	exit(EXIT_SUCCESS);&lt;br /&gt;
}&lt;br /&gt;
// Fonction qui attend chacun des processus fils&lt;br /&gt;
void waitForAll() {&lt;br /&gt;
	int status;&lt;br /&gt;
	pid_t pid;&lt;br /&gt;
	int n = 0;&lt;br /&gt;
	while (n &amp;lt; NB_FORK) {&lt;br /&gt;
		pid = wait(&amp;amp;status);&lt;br /&gt;
		printf(&amp;quot;Fork [%i] terminé avec le code %i\n&amp;quot;, pid, status);&lt;br /&gt;
		n++;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_FORK; i++) {&lt;br /&gt;
		pid_t pid = fork();&lt;br /&gt;
		if (pid == -1) {&lt;br /&gt;
			// Il y a une erreur&lt;br /&gt;
			perror(&amp;quot;fork&amp;quot;);&lt;br /&gt;
			return EXIT_FAILURE;&lt;br /&gt;
		} else if (pid == 0) {&lt;br /&gt;
			// On est dans le fils&lt;br /&gt;
			job();&lt;br /&gt;
		} else {&lt;br /&gt;
			// On est dans le père&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	waitForAll();&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Ce code donne, par exemple, le résultat suivant:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
fork [ 15964 ] data [ 1 ]&lt;br /&gt;
fork [ 15963 ] data [ 1 ]&lt;br /&gt;
fork [ 15964 ] data [ 2 ]&lt;br /&gt;
fork [ 15963 ] data [ 2 ]&lt;br /&gt;
Fin du fork 15964&lt;br /&gt;
Fork [15964] terminé avec le code 0&lt;br /&gt;
Fin du fork 15963&lt;br /&gt;
Fork [15963] terminé avec le code 0&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On voit bien que le contexte est recopié car les compteurs sont modifié indépendamment.&lt;br /&gt;
&lt;br /&gt;
=Démonisation=&lt;br /&gt;
Il peut être intéressant pour un programme, lorsque l&#039;on cherche à écrire un serveur, que celui-ci se détache du processus qui l&#039;a démarré : ce procédé s&#039;appelle la démonisation.&lt;br /&gt;
&lt;br /&gt;
Pour accomplir cela, rien de plus simple, il suffit d&#039;appeler la fonction suivante:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int daemon(int nochdir, int noclose);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* &#039;&#039;nochdir&#039;&#039; &amp;amp;rarr; si à &#039;&#039;0&#039;&#039;, le démon utilise comme répertoire de travail la racine (&#039;&#039;/&#039;&#039;), sinon il conserve le répertoire de travail courant.&lt;br /&gt;
* &#039;&#039;noclose&#039;&#039; &amp;amp;rarr; si à &#039;&#039;0&#039;&#039;, le démon redirige la sortie standard dans &#039;&#039;/dev/null&#039;&#039;, sinon aucun changement n&#039;est fait.&lt;br /&gt;
Pour comprendre le phénomène, utilisons le programme suivant :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
	printf(&amp;quot;PID du processus principal [ %i ]\n&amp;quot;, getpid());&lt;br /&gt;
	sleep(10);&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Il ne reste plus qu&#039;à compiler ce programme:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
gcc -o daemon.bin daemon.c&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Lorsque l&#039;on exécute le programme, il ne rend pas la main et on doit attendre 10 secondes  .&lt;br /&gt;
&lt;br /&gt;
Si on ajoute, avant la ligne :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
sleep(10);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
La ligne suivante:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
daemon(0,0);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
le programme se détache du processus courant et on peut le voir grâce à la commande &#039;&#039;ps&#039;&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# ./daemon.bin&lt;br /&gt;
PID du processus principal [ 16559 ]&lt;br /&gt;
# ps -ef | grep ./daemon.bin&lt;br /&gt;
root      16560      1  0 07:52 ?        00:00:00 ./daemon.bin&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On peut le voir plus simplement sur cet exemple :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
	printf(&amp;quot;PID du processus principal [ %i ]\n&amp;quot;, getpid());&lt;br /&gt;
	// Passage du programme en arrière plan sans modifier la sortie standard&lt;br /&gt;
	daemon(0, 1);&lt;br /&gt;
	printf(&amp;quot;PID du démon [ %i ]\n&amp;quot;, getpid());&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
L&#039;exécution donne, par exemple :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
PID du processus principal [ 16613 ]&lt;br /&gt;
PID du démon [ 16614 ]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Magsss</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=C_fork&amp;diff=3258</id>
		<title>C fork</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=C_fork&amp;diff=3258"/>
		<updated>2018-11-01T17:55:29Z</updated>

		<summary type="html">&lt;p&gt;Magsss : /* Fonctions annexes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Un fork est un processus lourd, comprenez par là que l&#039;intégralité du contexte d&#039;exécution du processus père est recopié dans le &#039;&#039;nouveau&#039;&#039; processus fils. Ne cherchez pas, comme avec les [[C_pthread | threads]], a échanger des variables entre les processus car cela ne marchera pas. Il faut utiliser des techniques de communication inter-processus comme les tubes, sockets ou encore sémaphores...&lt;br /&gt;
&lt;br /&gt;
= Fonctionnement =&lt;br /&gt;
Pour créer un fork, il suffit d&#039;appeler la fonction du même nom. Cette fonction aura différentes valeurs de retour en fonction du processus dans lequel on se trouve:&lt;br /&gt;
*-1 &amp;amp;rarr; il y a une erreur ;&lt;br /&gt;
*0  &amp;amp;rarr; on est dans le processus fils ;&lt;br /&gt;
*Le PID du fils &amp;amp;rarr; on est dans le processus père.&lt;br /&gt;
&lt;br /&gt;
La valeur retournée par la fonction fork est de type &#039;&#039;pid_t&#039;&#039;, c&#039;est pourquoi il faut obligatoirement inclure le fichier &#039;&#039;&amp;lt;sys/types.h&amp;gt;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Dans le cas d&#039;une erreur, celle-ci sera accessible grâce à la variable globale &#039;&#039;errno&#039;&#039;, il faudra donc inclure le fichier en-tête &#039;&#039;&amp;lt;errno.h&amp;gt;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
= Fonctions annexes =&lt;br /&gt;
Voici quelques fonctions annexes bien pratiques:&lt;br /&gt;
* getpid &amp;amp;rarr; retourne le PID du processus appelant, de type &#039;&#039;pid_t&#039;&#039; :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
printf(&amp;quot;Mon PID est %i\n&amp;quot;, getpid()); // affichera par exemple &amp;quot;Mon PID est 14804&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* getppid &amp;amp;rarr; retourne le PPID du processus appelant, de type &#039;&#039;pid_t&#039;&#039; :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
printf(&amp;quot;Mon PPID est %i\n&amp;quot;, getppid()); // affichera par exemple &amp;quot;Mon PPID est 14403&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* getuid &amp;amp;rarr; retourne l&#039;UID du processus appelant, de type &#039;&#039;uid_t&#039;&#039; :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
printf(&amp;quot;Mon UID est %i\n&amp;quot;, getuid()); // affichera par exemple &amp;quot;Mon UID est 0&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* getgid &amp;amp;rarr; retourne le GID du processus appelant, de type &#039;&#039;gid_t&#039;&#039; :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
printf(&amp;quot;Mon GID est %i\n&amp;quot;, getgid()); // affichera par exemple &amp;quot;Mon GID est 0&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* exit &amp;amp;rarr; permet de quitter le programme peu importe où dans le code :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
exit(EXIT_SUCCESS); // quitte le programme en retournant la valeur de succès&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* wait &amp;amp;rarr; permet au processus fils d&#039;être libéré pour éviter les zombies:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/wait.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int status;&lt;br /&gt;
&lt;br /&gt;
wait(&amp;amp;status); // la valeur de retour du processus fils sera dans la variable status&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Création d&#039;un fork =&lt;br /&gt;
Ci-dessous un exemple de création d&#039;un processus lourd :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
	pid_t pid = fork();&lt;br /&gt;
	if (pid == -1) {&lt;br /&gt;
		// Il y a une erreur&lt;br /&gt;
		perror(&amp;quot;fork&amp;quot;);&lt;br /&gt;
		return EXIT_FAILURE;&lt;br /&gt;
	} else if (pid == 0) {&lt;br /&gt;
		// On est dans le fils&lt;br /&gt;
		printf(&amp;quot;Mon PID est %i et celui de mon père est %i\n&amp;quot;, getpid(),	getppid());&lt;br /&gt;
	} else {&lt;br /&gt;
		// On est dans le père&lt;br /&gt;
		printf(&amp;quot;Mon PID est %i et celui de mon fils est %i\n&amp;quot;, getpid(), pid);&lt;br /&gt;
	}&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Cette exemple peut retourner la sortie suivante :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Mon PID est 15150 et celui de mon fils est 15151&lt;br /&gt;
Mon PID est 15151 et celui de mon père est 15150&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Cependant, on peut très bien avoir cela:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Mon PID est 15197 et celui de mon fils est 15198&lt;br /&gt;
Mon PID est 15198 et celui de mon père est 1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On voit que le numéro de processus du père est devenu &#039;&#039;1&#039;&#039;. Cela vient du faite que, lorsque le père se termine, le fils est automatiquement rattaché au processus de PID &#039;&#039;1&#039;&#039; pour ne pas devenir un processus [https://en.wikipedia.org/wiki/Orphan_process &#039;&#039;orphelin&#039;&#039;].&lt;br /&gt;
&lt;br /&gt;
= Création de plusieurs forks =&lt;br /&gt;
Lorsque l&#039;on a plusieurs forks à gérer, il faut garder la trace de chacun des numéros de processus et également attendre la fin de chacun des fils.&lt;br /&gt;
&lt;br /&gt;
Ci-dessous un code qui démarre plusieurs forks qui vont incrémenter un compteur.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/wait.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Nombre total de thread&lt;br /&gt;
#define NB_FORK 2&lt;br /&gt;
// Limite de l&#039;incrément&lt;br /&gt;
#define INCREMENT_LIMIT 2&lt;br /&gt;
// Initialisation de la donnée&lt;br /&gt;
int data = 0;&lt;br /&gt;
&lt;br /&gt;
// Fonction exécutée dans le fork&lt;br /&gt;
void job() {&lt;br /&gt;
	int tid = getpid();&lt;br /&gt;
	while (data &amp;lt; INCREMENT_LIMIT) {&lt;br /&gt;
		data++;&lt;br /&gt;
		printf(&amp;quot;fork [ %i ] data [ %i ]\n&amp;quot;, tid, data);&lt;br /&gt;
		// Pause l&#039;exécution du thread pendant 1 seconde&lt;br /&gt;
		sleep(1);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Fin du fork %i\n&amp;quot;, tid);&lt;br /&gt;
	exit(EXIT_SUCCESS);&lt;br /&gt;
}&lt;br /&gt;
// Fonction qui attend chacun des processus fils&lt;br /&gt;
void waitForAll() {&lt;br /&gt;
	int status;&lt;br /&gt;
	pid_t pid;&lt;br /&gt;
	int n = 0;&lt;br /&gt;
	while (n &amp;lt; NB_FORK) {&lt;br /&gt;
		pid = wait(&amp;amp;status);&lt;br /&gt;
		printf(&amp;quot;Fork [%i] terminé avec le code %i\n&amp;quot;, pid, status);&lt;br /&gt;
		n++;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_FORK; i++) {&lt;br /&gt;
		pid_t pid = fork();&lt;br /&gt;
		if (pid == -1) {&lt;br /&gt;
			// Il y a une erreur&lt;br /&gt;
			perror(&amp;quot;fork&amp;quot;);&lt;br /&gt;
			return EXIT_FAILURE;&lt;br /&gt;
		} else if (pid == 0) {&lt;br /&gt;
			// On est dans le fils&lt;br /&gt;
			job();&lt;br /&gt;
		} else {&lt;br /&gt;
			// On est dans le père&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	waitForAll();&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Ce code donne, par exemple, le résultat suivant:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
fork [ 15964 ] data [ 1 ]&lt;br /&gt;
fork [ 15963 ] data [ 1 ]&lt;br /&gt;
fork [ 15964 ] data [ 2 ]&lt;br /&gt;
fork [ 15963 ] data [ 2 ]&lt;br /&gt;
Fin du fork 15964&lt;br /&gt;
Fork [15964] terminé avec le code 0&lt;br /&gt;
Fin du fork 15963&lt;br /&gt;
Fork [15963] terminé avec le code 0&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On voit bien que le contexte est recopié car les compteurs sont modifié indépendamment.&lt;br /&gt;
&lt;br /&gt;
=Démonisation=&lt;br /&gt;
Il peut être intéressant pour un programme, lorsque l&#039;on cherche à écrire un serveur, que celui-ci se détache du processus qui l&#039;a démarré : ce procédé s&#039;appelle la démonisation.&lt;br /&gt;
&lt;br /&gt;
Pour accomplir cela, rien de plus simple, il suffit d&#039;appeler la fonction suivante:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int daemon(int nochdir, int noclose);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* &#039;&#039;nochdir&#039;&#039; &amp;amp;rarr; si à &#039;&#039;0&#039;&#039;, le démon utilise comme répertoire de travail la racine (&#039;&#039;/&#039;&#039;), sinon il conserve le répertoire de travail courant.&lt;br /&gt;
* &#039;&#039;noclose&#039;&#039; &amp;amp;rarr; si à &#039;&#039;0&#039;&#039;, le démon redirige la sortie standard dans &#039;&#039;/dev/null&#039;&#039;, sinon aucun changement n&#039;est fait.&lt;br /&gt;
Pour comprendre le phénomène, utilisons le programme suivant :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
	printf(&amp;quot;PID du processus principal [ %i ]\n&amp;quot;, getpid());&lt;br /&gt;
	sleep(10);&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Il ne reste plus qu&#039;à compiler ce programme:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
gcc -o daemon.bin daemon.c&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Lorsque l&#039;on exécute le programme, il ne rend pas la main et on doit attendre 10 secondes  .&lt;br /&gt;
&lt;br /&gt;
Si on ajoute, avant la ligne :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
sleep(10);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
La ligne suivante:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
daemon(0,0);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
le programme se détache du processus courant et on peut le voir grâce à la commande &#039;&#039;ps&#039;&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# ./daemon.bin&lt;br /&gt;
PID du processus principal [ 16559 ]&lt;br /&gt;
# ps -ef | grep ./daemon.bin&lt;br /&gt;
root      16560      1  0 07:52 ?        00:00:00 ./daemon.bin&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On peut le voir plus simplement sur cet exemple :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
	printf(&amp;quot;PID du processus principal [ %i ]\n&amp;quot;, getpid());&lt;br /&gt;
	// Passage du programme en arrière plan sans modifier la sortie standard&lt;br /&gt;
	daemon(0, 1);&lt;br /&gt;
	printf(&amp;quot;PID du démon [ %i ]\n&amp;quot;, getpid());&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
L&#039;exécution donne, par exemple :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
PID du processus principal [ 16613 ]&lt;br /&gt;
PID du démon [ 16614 ]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Magsss</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=C_fork&amp;diff=3257</id>
		<title>C fork</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=C_fork&amp;diff=3257"/>
		<updated>2018-11-01T17:54:11Z</updated>

		<summary type="html">&lt;p&gt;Magsss : /* Fonctionnement */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Un fork est un processus lourd, comprenez par là que l&#039;intégralité du contexte d&#039;exécution du processus père est recopié dans le &#039;&#039;nouveau&#039;&#039; processus fils. Ne cherchez pas, comme avec les [[C_pthread | threads]], a échanger des variables entre les processus car cela ne marchera pas. Il faut utiliser des techniques de communication inter-processus comme les tubes, sockets ou encore sémaphores...&lt;br /&gt;
&lt;br /&gt;
= Fonctionnement =&lt;br /&gt;
Pour créer un fork, il suffit d&#039;appeler la fonction du même nom. Cette fonction aura différentes valeurs de retour en fonction du processus dans lequel on se trouve:&lt;br /&gt;
*-1 &amp;amp;rarr; il y a une erreur ;&lt;br /&gt;
*0  &amp;amp;rarr; on est dans le processus fils ;&lt;br /&gt;
*Le PID du fils &amp;amp;rarr; on est dans le processus père.&lt;br /&gt;
&lt;br /&gt;
La valeur retournée par la fonction fork est de type &#039;&#039;pid_t&#039;&#039;, c&#039;est pourquoi il faut obligatoirement inclure le fichier &#039;&#039;&amp;lt;sys/types.h&amp;gt;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Dans le cas d&#039;une erreur, celle-ci sera accessible grâce à la variable globale &#039;&#039;errno&#039;&#039;, il faudra donc inclure le fichier en-tête &#039;&#039;&amp;lt;errno.h&amp;gt;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
= Fonctions annexes =&lt;br /&gt;
Voici quelques fonctions annexes bien pratiques:&lt;br /&gt;
* getpid &amp;amp;rarr; retourne le PID du processus appelant, de type &#039;&#039;pid_t&#039;&#039; :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
printf(&amp;quot;Mon PID est %i\n&amp;quot;, getpid()); // affichera par exemple &amp;quot;Mon PID est 14804&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* getppid &amp;amp;rarr; retourne le PPID du processus appelant, de type &#039;&#039;pid_t&#039;&#039; :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
printf(&amp;quot;Mon PPID est %i\n&amp;quot;, getppid()); // affichera par exemple &amp;quot;Mon PPID est 14403&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* getuid &amp;amp;rarr; retourne l&#039;UID du processus appelant, de type &#039;&#039;uid_t&#039;&#039; :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
printf(&amp;quot;Mon UID est %i\n&amp;quot;, getuid()); // affichera par exemple &amp;quot;Mon UID est 0&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* getgid &amp;amp;rarr; retourne le GID du processus appelant, de type &#039;&#039;gid_t&#039;&#039; :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
printf(&amp;quot;Mon GID est %i\n&amp;quot;, getgid()); // affichera par exemple &amp;quot;Mon GID est 0&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* exit &amp;amp;rarr; permet de quitter le programme peut importe où dans le code :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
exit(EXIT_SUCCESS); // quitte le programme en retournant la valeur de succès&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* wait &amp;amp;rarr; permet au processus fils d&#039;être libéré pour éviter les zombies:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/wait.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int status;&lt;br /&gt;
&lt;br /&gt;
wait(&amp;amp;status); // la valeur de retour du processus fils sera dans la variable status&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Création d&#039;un fork =&lt;br /&gt;
Ci-dessous un exemple de création d&#039;un processus lourd :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
	pid_t pid = fork();&lt;br /&gt;
	if (pid == -1) {&lt;br /&gt;
		// Il y a une erreur&lt;br /&gt;
		perror(&amp;quot;fork&amp;quot;);&lt;br /&gt;
		return EXIT_FAILURE;&lt;br /&gt;
	} else if (pid == 0) {&lt;br /&gt;
		// On est dans le fils&lt;br /&gt;
		printf(&amp;quot;Mon PID est %i et celui de mon père est %i\n&amp;quot;, getpid(),	getppid());&lt;br /&gt;
	} else {&lt;br /&gt;
		// On est dans le père&lt;br /&gt;
		printf(&amp;quot;Mon PID est %i et celui de mon fils est %i\n&amp;quot;, getpid(), pid);&lt;br /&gt;
	}&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Cette exemple peut retourner la sortie suivante :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Mon PID est 15150 et celui de mon fils est 15151&lt;br /&gt;
Mon PID est 15151 et celui de mon père est 15150&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Cependant, on peut très bien avoir cela:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Mon PID est 15197 et celui de mon fils est 15198&lt;br /&gt;
Mon PID est 15198 et celui de mon père est 1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On voit que le numéro de processus du père est devenu &#039;&#039;1&#039;&#039;. Cela vient du faite que, lorsque le père se termine, le fils est automatiquement rattaché au processus de PID &#039;&#039;1&#039;&#039; pour ne pas devenir un processus [https://en.wikipedia.org/wiki/Orphan_process &#039;&#039;orphelin&#039;&#039;].&lt;br /&gt;
&lt;br /&gt;
= Création de plusieurs forks =&lt;br /&gt;
Lorsque l&#039;on a plusieurs forks à gérer, il faut garder la trace de chacun des numéros de processus et également attendre la fin de chacun des fils.&lt;br /&gt;
&lt;br /&gt;
Ci-dessous un code qui démarre plusieurs forks qui vont incrémenter un compteur.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/wait.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Nombre total de thread&lt;br /&gt;
#define NB_FORK 2&lt;br /&gt;
// Limite de l&#039;incrément&lt;br /&gt;
#define INCREMENT_LIMIT 2&lt;br /&gt;
// Initialisation de la donnée&lt;br /&gt;
int data = 0;&lt;br /&gt;
&lt;br /&gt;
// Fonction exécutée dans le fork&lt;br /&gt;
void job() {&lt;br /&gt;
	int tid = getpid();&lt;br /&gt;
	while (data &amp;lt; INCREMENT_LIMIT) {&lt;br /&gt;
		data++;&lt;br /&gt;
		printf(&amp;quot;fork [ %i ] data [ %i ]\n&amp;quot;, tid, data);&lt;br /&gt;
		// Pause l&#039;exécution du thread pendant 1 seconde&lt;br /&gt;
		sleep(1);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Fin du fork %i\n&amp;quot;, tid);&lt;br /&gt;
	exit(EXIT_SUCCESS);&lt;br /&gt;
}&lt;br /&gt;
// Fonction qui attend chacun des processus fils&lt;br /&gt;
void waitForAll() {&lt;br /&gt;
	int status;&lt;br /&gt;
	pid_t pid;&lt;br /&gt;
	int n = 0;&lt;br /&gt;
	while (n &amp;lt; NB_FORK) {&lt;br /&gt;
		pid = wait(&amp;amp;status);&lt;br /&gt;
		printf(&amp;quot;Fork [%i] terminé avec le code %i\n&amp;quot;, pid, status);&lt;br /&gt;
		n++;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_FORK; i++) {&lt;br /&gt;
		pid_t pid = fork();&lt;br /&gt;
		if (pid == -1) {&lt;br /&gt;
			// Il y a une erreur&lt;br /&gt;
			perror(&amp;quot;fork&amp;quot;);&lt;br /&gt;
			return EXIT_FAILURE;&lt;br /&gt;
		} else if (pid == 0) {&lt;br /&gt;
			// On est dans le fils&lt;br /&gt;
			job();&lt;br /&gt;
		} else {&lt;br /&gt;
			// On est dans le père&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	waitForAll();&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Ce code donne, par exemple, le résultat suivant:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
fork [ 15964 ] data [ 1 ]&lt;br /&gt;
fork [ 15963 ] data [ 1 ]&lt;br /&gt;
fork [ 15964 ] data [ 2 ]&lt;br /&gt;
fork [ 15963 ] data [ 2 ]&lt;br /&gt;
Fin du fork 15964&lt;br /&gt;
Fork [15964] terminé avec le code 0&lt;br /&gt;
Fin du fork 15963&lt;br /&gt;
Fork [15963] terminé avec le code 0&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On voit bien que le contexte est recopié car les compteurs sont modifié indépendamment.&lt;br /&gt;
&lt;br /&gt;
=Démonisation=&lt;br /&gt;
Il peut être intéressant pour un programme, lorsque l&#039;on cherche à écrire un serveur, que celui-ci se détache du processus qui l&#039;a démarré : ce procédé s&#039;appelle la démonisation.&lt;br /&gt;
&lt;br /&gt;
Pour accomplir cela, rien de plus simple, il suffit d&#039;appeler la fonction suivante:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int daemon(int nochdir, int noclose);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* &#039;&#039;nochdir&#039;&#039; &amp;amp;rarr; si à &#039;&#039;0&#039;&#039;, le démon utilise comme répertoire de travail la racine (&#039;&#039;/&#039;&#039;), sinon il conserve le répertoire de travail courant.&lt;br /&gt;
* &#039;&#039;noclose&#039;&#039; &amp;amp;rarr; si à &#039;&#039;0&#039;&#039;, le démon redirige la sortie standard dans &#039;&#039;/dev/null&#039;&#039;, sinon aucun changement n&#039;est fait.&lt;br /&gt;
Pour comprendre le phénomène, utilisons le programme suivant :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
	printf(&amp;quot;PID du processus principal [ %i ]\n&amp;quot;, getpid());&lt;br /&gt;
	sleep(10);&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Il ne reste plus qu&#039;à compiler ce programme:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
gcc -o daemon.bin daemon.c&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Lorsque l&#039;on exécute le programme, il ne rend pas la main et on doit attendre 10 secondes  .&lt;br /&gt;
&lt;br /&gt;
Si on ajoute, avant la ligne :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
sleep(10);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
La ligne suivante:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
daemon(0,0);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
le programme se détache du processus courant et on peut le voir grâce à la commande &#039;&#039;ps&#039;&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# ./daemon.bin&lt;br /&gt;
PID du processus principal [ 16559 ]&lt;br /&gt;
# ps -ef | grep ./daemon.bin&lt;br /&gt;
root      16560      1  0 07:52 ?        00:00:00 ./daemon.bin&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On peut le voir plus simplement sur cet exemple :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
	printf(&amp;quot;PID du processus principal [ %i ]\n&amp;quot;, getpid());&lt;br /&gt;
	// Passage du programme en arrière plan sans modifier la sortie standard&lt;br /&gt;
	daemon(0, 1);&lt;br /&gt;
	printf(&amp;quot;PID du démon [ %i ]\n&amp;quot;, getpid());&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
L&#039;exécution donne, par exemple :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
PID du processus principal [ 16613 ]&lt;br /&gt;
PID du démon [ 16614 ]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Magsss</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=C_fork&amp;diff=3256</id>
		<title>C fork</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=C_fork&amp;diff=3256"/>
		<updated>2018-11-01T17:52:58Z</updated>

		<summary type="html">&lt;p&gt;Magsss : /* Introduction */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Un fork est un processus lourd, comprenez par là que l&#039;intégralité du contexte d&#039;exécution du processus père est recopié dans le &#039;&#039;nouveau&#039;&#039; processus fils. Ne cherchez pas, comme avec les [[C_pthread | threads]], a échanger des variables entre les processus car cela ne marchera pas. Il faut utiliser des techniques de communication inter-processus comme les tubes, sockets ou encore sémaphores...&lt;br /&gt;
&lt;br /&gt;
= Fonctionnement =&lt;br /&gt;
Pour créer un fork, il suffit d&#039;appeler la fonction du même nom. Cette fonction aura différentes valeurs de retour en fonction du processus dans lequel on se trouve:&lt;br /&gt;
*-1 &amp;amp;rarr; il y a une erreur ;&lt;br /&gt;
*0  &amp;amp;rarr; on est dans le processus fils ;&lt;br /&gt;
*Le PID du fils &amp;amp;rarr; on est dans le processus père.&lt;br /&gt;
&lt;br /&gt;
La valeur retournée par la fonction fork est de type &#039;&#039;pid_t&#039;&#039;, c&#039;est pourquoi il faut obligatoirement inclure le fichier &#039;&#039;&amp;lt;sys/types.h&amp;gt;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
Dans le cas d&#039;une erreur, celle-ci sera accessible grâce à la variable globale &#039;&#039;errno&#039;&#039;, il faudra donc inclure le fichier entête &#039;&#039;&amp;lt;errno.h&amp;gt;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
= Fonctions annexes =&lt;br /&gt;
Voici quelques fonctions annexes bien pratiques:&lt;br /&gt;
* getpid &amp;amp;rarr; retourne le PID du processus appelant, de type &#039;&#039;pid_t&#039;&#039; :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
printf(&amp;quot;Mon PID est %i\n&amp;quot;, getpid()); // affichera par exemple &amp;quot;Mon PID est 14804&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* getppid &amp;amp;rarr; retourne le PPID du processus appelant, de type &#039;&#039;pid_t&#039;&#039; :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
printf(&amp;quot;Mon PPID est %i\n&amp;quot;, getppid()); // affichera par exemple &amp;quot;Mon PPID est 14403&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* getuid &amp;amp;rarr; retourne l&#039;UID du processus appelant, de type &#039;&#039;uid_t&#039;&#039; :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
printf(&amp;quot;Mon UID est %i\n&amp;quot;, getuid()); // affichera par exemple &amp;quot;Mon UID est 0&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* getgid &amp;amp;rarr; retourne le GID du processus appelant, de type &#039;&#039;gid_t&#039;&#039; :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
printf(&amp;quot;Mon GID est %i\n&amp;quot;, getgid()); // affichera par exemple &amp;quot;Mon GID est 0&amp;quot;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* exit &amp;amp;rarr; permet de quitter le programme peut importe où dans le code :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
exit(EXIT_SUCCESS); // quitte le programme en retournant la valeur de succès&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* wait &amp;amp;rarr; permet au processus fils d&#039;être libéré pour éviter les zombies:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/wait.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int status;&lt;br /&gt;
&lt;br /&gt;
wait(&amp;amp;status); // la valeur de retour du processus fils sera dans la variable status&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Création d&#039;un fork =&lt;br /&gt;
Ci-dessous un exemple de création d&#039;un processus lourd :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
	pid_t pid = fork();&lt;br /&gt;
	if (pid == -1) {&lt;br /&gt;
		// Il y a une erreur&lt;br /&gt;
		perror(&amp;quot;fork&amp;quot;);&lt;br /&gt;
		return EXIT_FAILURE;&lt;br /&gt;
	} else if (pid == 0) {&lt;br /&gt;
		// On est dans le fils&lt;br /&gt;
		printf(&amp;quot;Mon PID est %i et celui de mon père est %i\n&amp;quot;, getpid(),	getppid());&lt;br /&gt;
	} else {&lt;br /&gt;
		// On est dans le père&lt;br /&gt;
		printf(&amp;quot;Mon PID est %i et celui de mon fils est %i\n&amp;quot;, getpid(), pid);&lt;br /&gt;
	}&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Cette exemple peut retourner la sortie suivante :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Mon PID est 15150 et celui de mon fils est 15151&lt;br /&gt;
Mon PID est 15151 et celui de mon père est 15150&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Cependant, on peut très bien avoir cela:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Mon PID est 15197 et celui de mon fils est 15198&lt;br /&gt;
Mon PID est 15198 et celui de mon père est 1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On voit que le numéro de processus du père est devenu &#039;&#039;1&#039;&#039;. Cela vient du faite que, lorsque le père se termine, le fils est automatiquement rattaché au processus de PID &#039;&#039;1&#039;&#039; pour ne pas devenir un processus [https://en.wikipedia.org/wiki/Orphan_process &#039;&#039;orphelin&#039;&#039;].&lt;br /&gt;
&lt;br /&gt;
= Création de plusieurs forks =&lt;br /&gt;
Lorsque l&#039;on a plusieurs forks à gérer, il faut garder la trace de chacun des numéros de processus et également attendre la fin de chacun des fils.&lt;br /&gt;
&lt;br /&gt;
Ci-dessous un code qui démarre plusieurs forks qui vont incrémenter un compteur.&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/types.h&amp;gt;&lt;br /&gt;
#include &amp;lt;sys/wait.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Nombre total de thread&lt;br /&gt;
#define NB_FORK 2&lt;br /&gt;
// Limite de l&#039;incrément&lt;br /&gt;
#define INCREMENT_LIMIT 2&lt;br /&gt;
// Initialisation de la donnée&lt;br /&gt;
int data = 0;&lt;br /&gt;
&lt;br /&gt;
// Fonction exécutée dans le fork&lt;br /&gt;
void job() {&lt;br /&gt;
	int tid = getpid();&lt;br /&gt;
	while (data &amp;lt; INCREMENT_LIMIT) {&lt;br /&gt;
		data++;&lt;br /&gt;
		printf(&amp;quot;fork [ %i ] data [ %i ]\n&amp;quot;, tid, data);&lt;br /&gt;
		// Pause l&#039;exécution du thread pendant 1 seconde&lt;br /&gt;
		sleep(1);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Fin du fork %i\n&amp;quot;, tid);&lt;br /&gt;
	exit(EXIT_SUCCESS);&lt;br /&gt;
}&lt;br /&gt;
// Fonction qui attend chacun des processus fils&lt;br /&gt;
void waitForAll() {&lt;br /&gt;
	int status;&lt;br /&gt;
	pid_t pid;&lt;br /&gt;
	int n = 0;&lt;br /&gt;
	while (n &amp;lt; NB_FORK) {&lt;br /&gt;
		pid = wait(&amp;amp;status);&lt;br /&gt;
		printf(&amp;quot;Fork [%i] terminé avec le code %i\n&amp;quot;, pid, status);&lt;br /&gt;
		n++;&lt;br /&gt;
	}&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_FORK; i++) {&lt;br /&gt;
		pid_t pid = fork();&lt;br /&gt;
		if (pid == -1) {&lt;br /&gt;
			// Il y a une erreur&lt;br /&gt;
			perror(&amp;quot;fork&amp;quot;);&lt;br /&gt;
			return EXIT_FAILURE;&lt;br /&gt;
		} else if (pid == 0) {&lt;br /&gt;
			// On est dans le fils&lt;br /&gt;
			job();&lt;br /&gt;
		} else {&lt;br /&gt;
			// On est dans le père&lt;br /&gt;
		}&lt;br /&gt;
	}&lt;br /&gt;
	waitForAll();&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Ce code donne, par exemple, le résultat suivant:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
fork [ 15964 ] data [ 1 ]&lt;br /&gt;
fork [ 15963 ] data [ 1 ]&lt;br /&gt;
fork [ 15964 ] data [ 2 ]&lt;br /&gt;
fork [ 15963 ] data [ 2 ]&lt;br /&gt;
Fin du fork 15964&lt;br /&gt;
Fork [15964] terminé avec le code 0&lt;br /&gt;
Fin du fork 15963&lt;br /&gt;
Fork [15963] terminé avec le code 0&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On voit bien que le contexte est recopié car les compteurs sont modifié indépendamment.&lt;br /&gt;
&lt;br /&gt;
=Démonisation=&lt;br /&gt;
Il peut être intéressant pour un programme, lorsque l&#039;on cherche à écrire un serveur, que celui-ci se détache du processus qui l&#039;a démarré : ce procédé s&#039;appelle la démonisation.&lt;br /&gt;
&lt;br /&gt;
Pour accomplir cela, rien de plus simple, il suffit d&#039;appeler la fonction suivante:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int daemon(int nochdir, int noclose);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* &#039;&#039;nochdir&#039;&#039; &amp;amp;rarr; si à &#039;&#039;0&#039;&#039;, le démon utilise comme répertoire de travail la racine (&#039;&#039;/&#039;&#039;), sinon il conserve le répertoire de travail courant.&lt;br /&gt;
* &#039;&#039;noclose&#039;&#039; &amp;amp;rarr; si à &#039;&#039;0&#039;&#039;, le démon redirige la sortie standard dans &#039;&#039;/dev/null&#039;&#039;, sinon aucun changement n&#039;est fait.&lt;br /&gt;
Pour comprendre le phénomène, utilisons le programme suivant :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
	printf(&amp;quot;PID du processus principal [ %i ]\n&amp;quot;, getpid());&lt;br /&gt;
	sleep(10);&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Il ne reste plus qu&#039;à compiler ce programme:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
gcc -o daemon.bin daemon.c&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Lorsque l&#039;on exécute le programme, il ne rend pas la main et on doit attendre 10 secondes  .&lt;br /&gt;
&lt;br /&gt;
Si on ajoute, avant la ligne :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
sleep(10);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
La ligne suivante:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
daemon(0,0);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
le programme se détache du processus courant et on peut le voir grâce à la commande &#039;&#039;ps&#039;&#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# ./daemon.bin&lt;br /&gt;
PID du processus principal [ 16559 ]&lt;br /&gt;
# ps -ef | grep ./daemon.bin&lt;br /&gt;
root      16560      1  0 07:52 ?        00:00:00 ./daemon.bin&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On peut le voir plus simplement sur cet exemple :&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
int main() {&lt;br /&gt;
	printf(&amp;quot;PID du processus principal [ %i ]\n&amp;quot;, getpid());&lt;br /&gt;
	// Passage du programme en arrière plan sans modifier la sortie standard&lt;br /&gt;
	daemon(0, 1);&lt;br /&gt;
	printf(&amp;quot;PID du démon [ %i ]\n&amp;quot;, getpid());&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
L&#039;exécution donne, par exemple :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
PID du processus principal [ 16613 ]&lt;br /&gt;
PID du démon [ 16614 ]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Magsss</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=C_pthread&amp;diff=3255</id>
		<title>C pthread</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=C_pthread&amp;diff=3255"/>
		<updated>2018-11-01T17:51:56Z</updated>

		<summary type="html">&lt;p&gt;Magsss : /* Exclusion mutuelle */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Les threads ou &#039;&#039;processus légers&#039;&#039; sont utilisés pour paralléliser l&#039;exécution dans un programme. Ils sont dits &#039;&#039;légers&#039;&#039; car ils s&#039;exécutent dans le même contexte que le processus d&#039;exécution principal qui les crée et consomment donc moins de mémoire / CPU.&lt;br /&gt;
&lt;br /&gt;
= Pré-requis =&lt;br /&gt;
Pour pouvoir créer des threads il faut utiliser la librairie &#039;&#039;pthread&#039;&#039; (POSIX thread):&lt;br /&gt;
&amp;lt;source lang=&#039;c&#039;&amp;gt;&lt;br /&gt;
#include &amp;lt;pthread.h&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Ce qui donne en ligne de commande:&lt;br /&gt;
&amp;lt;source lang=&#039;bash&#039;&amp;gt;&lt;br /&gt;
gcc -lpthread thread_exemple.c&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Pour inclure la librairie dans [[Eclipse_install|Eclispe]] regardez [[C_devel#Ajouter_une_librairie_.C3.A0_un_projet|ici]].&lt;br /&gt;
= Premier thread =&lt;br /&gt;
Dans ce premier exemple, nous allons créer un thread qui affiche une message:&lt;br /&gt;
&amp;lt;source lang=&#039;c&#039;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;pthread.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void *thread_1(void *arg) {&lt;br /&gt;
	printf(&amp;quot;Nous sommes dans le thread.\n&amp;quot;);&lt;br /&gt;
	// Arrêt propre du thread&lt;br /&gt;
	pthread_exit(NULL);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
	// Création de la variable qui va contenir le thread&lt;br /&gt;
	pthread_t thread1;&lt;br /&gt;
	printf(&amp;quot;Avant la création du thread.\n&amp;quot;);&lt;br /&gt;
	// Création du thread&lt;br /&gt;
	pthread_create(&amp;amp;thread1, NULL, thread_1, NULL);&lt;br /&gt;
	printf(&amp;quot;Après la création du thread.\n&amp;quot;);&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Dans cet exemple, nous utilisons la fonction suivante:&lt;br /&gt;
&amp;lt;source lang=&#039;c&#039;&amp;gt;&lt;br /&gt;
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* &#039;&#039;*thread&#039;&#039; est une référence vers la variable qui va contenir le thread;&lt;br /&gt;
* &#039;&#039;*attr&#039;&#039; correspond aux arguments de création du thread, passer à la fonction &#039;&#039;pthread_create&#039;&#039;;&lt;br /&gt;
* &#039;&#039;*(*start_routine)&#039;&#039; est un pointeur vers la fonction exécutée par le thread;&lt;br /&gt;
* &#039;&#039;*arg&#039;&#039; est un pointeur vers les arguments passés en paramètres à la fonction &#039;&#039;start_routine&#039;&#039;;&lt;br /&gt;
* le code retour de la fonction varie entre :&lt;br /&gt;
** &#039;&#039;0&#039;&#039; &amp;amp;rarr; lorsque tout s&#039;est bien passé;&lt;br /&gt;
** EAGAIN &amp;amp;rarr; lorsque les ressources sont insuffisantes ou le nombre de threads maximum est atteint;&lt;br /&gt;
** EINVAL &amp;amp;rarr; lorsqu&#039;un argument invalide est passé en paramètre;&lt;br /&gt;
** EPERM &amp;amp;rarr; lorsqu&#039;il y a un problème de droit sur l&#039;ordonnanceur (passé en paramètre); &lt;br /&gt;
Lorsque l&#039;on exécute cet exemple, on a le résultat suivant :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Avant la création du thread.&lt;br /&gt;
Après la création du thread.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On ne voit pas le message du thread car le programme se termine sans attendre la fin de son exécution. Pour attendre la fin de l&#039;exécution du thread, il faut utiliser la fonction suivante :&lt;br /&gt;
&amp;lt;source lang=&#039;c&#039;&amp;gt;&lt;br /&gt;
int pthread_join(pthread_t thread, void **retval);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* &#039;&#039;*thread&#039;&#039; est une référence vers la variable qui va contenir le thread;&lt;br /&gt;
* &#039;&#039;**retval&#039;&#039; est un pointeur vers un entier qui contiendra la valeur de retour du thread;&lt;br /&gt;
* le code retour de la fonction varie entre :&lt;br /&gt;
** &#039;&#039;0&#039;&#039; &amp;amp;rarr; lorsque tout s&#039;est bien passé;&lt;br /&gt;
** EDEADLK &amp;amp;rarr; lorsqu&#039;il y a un &#039;&#039;deadlock&#039;&#039;;&lt;br /&gt;
** EINVAL &amp;amp;rarr; lorsque le thread n&#039;est pas joignable;&lt;br /&gt;
** ESRCH &amp;amp;rarr; si le thread n&#039;existe pas;&lt;br /&gt;
Ajoutez la ligne suivante :&lt;br /&gt;
&amp;lt;source lang=&#039;c&#039;&amp;gt;&lt;br /&gt;
pthread_join(thread1, NULL);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
après l&#039;appel de la fonction &#039;&#039;pthread_create&#039;&#039; pour avoir le résultat suivant:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Avant la création du thread.&lt;br /&gt;
Nous sommes dans le thread.&lt;br /&gt;
Après la création du thread.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Modification d&#039;une variable =&lt;br /&gt;
Dans cet exemple nous allons incrémenter dans le thread un entier qui est déclaré dans le processus principal:&lt;br /&gt;
&amp;lt;source lang=&#039;c&#039;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;pthread.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void *thread_1(void *arg) {&lt;br /&gt;
	int *i = (int *) arg;&lt;br /&gt;
	(*i)++;&lt;br /&gt;
	// Arrêt propre du thread&lt;br /&gt;
	pthread_exit(NULL);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
	// Création de la variable qui va contenir le thread&lt;br /&gt;
	int i = 1;&lt;br /&gt;
	pthread_t thread1;&lt;br /&gt;
	printf(&amp;quot;Avant la création du thread, i = %i.\n&amp;quot;, i);&lt;br /&gt;
	// Création du thread&lt;br /&gt;
	pthread_create(&amp;amp;thread1, NULL, thread_1, &amp;amp;i);&lt;br /&gt;
	pthread_join(thread1, NULL);&lt;br /&gt;
	printf(&amp;quot;Après la création du thread, i = %i.\n&amp;quot;, i);&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Cela donne le résultat suivant :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Avant la création du thread, i = 1.&lt;br /&gt;
Après la création du thread, i = 2.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On peut remarquer que:&lt;br /&gt;
* dans le processus principal, on passe une référence vers la variable &#039;&#039;i&#039;&#039; (&#039;&#039;&amp;amp;i&#039;&#039;) dans la fonction &#039;&#039;pthread_create&#039;&#039;;&lt;br /&gt;
* dans le thread on récupère le pointeur vers cette variable et on la déclare comme étant un entier : &lt;br /&gt;
&amp;lt;source lang=&#039;c&#039;&amp;gt;&lt;br /&gt;
int *i = (int *) arg;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* on incrémente la valeur et non l&#039;adresse pointée :&lt;br /&gt;
&amp;lt;source lang=&#039;c&#039;&amp;gt;&lt;br /&gt;
(*i)++;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Exclusion mutuelle =&lt;br /&gt;
Lorsque l&#039;on cherche à modifier dans plusieurs threads une variable globale, il peut y avoir un problème d&#039;accès concurrent (modification / lecture simultanée). Ce problème peut être réglé en utilisant ce que l&#039;on appelle un &#039;&#039;mutex&#039;&#039; (&#039;&#039;mutual exclusion&#039;&#039;) et pour des questions pratiques, on utilise généralement une structure pour associer la variable à son &#039;&#039;mutex&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
typedef struct mutex_data {&lt;br /&gt;
	int data;&lt;br /&gt;
	pthread_mutex_t mutex;&lt;br /&gt;
} mutex_data;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dans l&#039;exemple ci-dessus on crée une structure de données avec un entier et un &#039;&#039;mutex&#039;&#039;.&lt;br /&gt;
Il existe plusieurs fonctions pour manipuler les &#039;&#039;mutex&#039;&#039; et voici les principales:&lt;br /&gt;
*pour initialiser un &#039;&#039;mutex&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int pthread_mutex_init(pthread_mutex_t* mutex, const pthread_mutexattr_t* attr )&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*pour verrouiller un &#039;&#039;mutex&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int pthread_mutex_lock(pthread_mutex_t *mutex)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*pour déverrouiller un &#039;&#039;mutex&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int pthread_mutex_unlock(pthread_mutex_t *mutex)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*pour détruire un &#039;&#039;mutex&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int pthread_mutex_destroy(pthread_mutex_t *mutex)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le programme suivant permet d&#039;incrémenter la valeur d&#039;un entier dans plusieurs threads différents:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;pthread.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Nombre total de thread&lt;br /&gt;
#define NB_THREAD 2&lt;br /&gt;
// Limite de l&#039;incrément&lt;br /&gt;
#define INCREMENT_LIMIT 10&lt;br /&gt;
&lt;br /&gt;
// Tableau contenant les threads&lt;br /&gt;
pthread_t threads[NB_THREAD];&lt;br /&gt;
&lt;br /&gt;
// Structure de données contenant le mutex&lt;br /&gt;
typedef struct mutex_data {&lt;br /&gt;
	int data;&lt;br /&gt;
	pthread_mutex_t mutex;&lt;br /&gt;
} mutex_data;&lt;br /&gt;
&lt;br /&gt;
// Fonction exécutée dans le thread&lt;br /&gt;
void * job(void *arg) {&lt;br /&gt;
	mutex_data *md = (mutex_data*) arg;&lt;br /&gt;
	pthread_t tid = pthread_self();&lt;br /&gt;
	while ((*md).data &amp;lt; INCREMENT_LIMIT) {&lt;br /&gt;
		// Vérouillage du mutex&lt;br /&gt;
		pthread_mutex_lock(&amp;amp;(*md).mutex);&lt;br /&gt;
		(*md).data++;&lt;br /&gt;
		// Dévérouillage du mutex&lt;br /&gt;
		pthread_mutex_unlock(&amp;amp;(*md).mutex);&lt;br /&gt;
		printf(&amp;quot;thread [ %ld ] data [ %i ]\n&amp;quot;, tid, (*md).data);&lt;br /&gt;
		// Pause l&#039;exécution du thread pendant 1 seconde&lt;br /&gt;
		sleep(1);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Fin du thread %ld\n&amp;quot;, tid);&lt;br /&gt;
	pthread_exit(NULL);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Fonction principale&lt;br /&gt;
int main() {&lt;br /&gt;
	// Création du mutex&lt;br /&gt;
	mutex_data md;&lt;br /&gt;
	// Initialisation de la donnée&lt;br /&gt;
	md.data = 0;&lt;br /&gt;
	// Initialisation du mutex&lt;br /&gt;
	if (pthread_mutex_init(&amp;amp;md.mutex, NULL) != 0) {&lt;br /&gt;
		printf(&amp;quot;\n mutex init failed\n&amp;quot;);&lt;br /&gt;
		return EXIT_FAILURE;&lt;br /&gt;
	}&lt;br /&gt;
	// Boucle de création des threads&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_THREAD; i++) {&lt;br /&gt;
		// Création du thread et passage de la structure par référence&lt;br /&gt;
		int err = pthread_create(&amp;amp;threads[i], NULL, job, &amp;amp;md);&lt;br /&gt;
		if (err != 0) {&lt;br /&gt;
			printf(&amp;quot;Echec de la création du thread: [%s]&amp;quot;, strerror(err));&lt;br /&gt;
			break;&lt;br /&gt;
		}&lt;br /&gt;
		printf(&amp;quot;Création du thread numéro %ld\n&amp;quot;, threads[i]);&lt;br /&gt;
	}&lt;br /&gt;
	// En attente des threads&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_THREAD; i++) {&lt;br /&gt;
		pthread_join(threads[i], NULL);&lt;br /&gt;
	}&lt;br /&gt;
	// Destruction du mutex&lt;br /&gt;
	pthread_mutex_destroy(&amp;amp;md.mutex);&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Un exemple de l&#039;exécution de ce programme donne la sortie suivante :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Création du thread numéro 140530420492032&lt;br /&gt;
Création du thread numéro 140530410002176&lt;br /&gt;
thread [ 140530410002176 ] data [ 1 ]&lt;br /&gt;
thread [ 140530420492032 ] data [ 2 ]&lt;br /&gt;
thread [ 140530410002176 ] data [ 3 ]&lt;br /&gt;
thread [ 140530420492032 ] data [ 4 ]&lt;br /&gt;
thread [ 140530410002176 ] data [ 5 ]&lt;br /&gt;
thread [ 140530420492032 ] data [ 6 ]&lt;br /&gt;
thread [ 140530420492032 ] data [ 7 ]&lt;br /&gt;
thread [ 140530410002176 ] data [ 8 ]&lt;br /&gt;
thread [ 140530420492032 ] data [ 9 ]&lt;br /&gt;
thread [ 140530410002176 ] data [ 10 ]&lt;br /&gt;
Fin du thread 140530420492032&lt;br /&gt;
Fin du thread 140530410002176&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Magsss</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=C_pthread&amp;diff=3254</id>
		<title>C pthread</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=C_pthread&amp;diff=3254"/>
		<updated>2018-11-01T17:51:11Z</updated>

		<summary type="html">&lt;p&gt;Magsss : /* Exclusion mutuelle */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Les threads ou &#039;&#039;processus légers&#039;&#039; sont utilisés pour paralléliser l&#039;exécution dans un programme. Ils sont dits &#039;&#039;légers&#039;&#039; car ils s&#039;exécutent dans le même contexte que le processus d&#039;exécution principal qui les crée et consomment donc moins de mémoire / CPU.&lt;br /&gt;
&lt;br /&gt;
= Pré-requis =&lt;br /&gt;
Pour pouvoir créer des threads il faut utiliser la librairie &#039;&#039;pthread&#039;&#039; (POSIX thread):&lt;br /&gt;
&amp;lt;source lang=&#039;c&#039;&amp;gt;&lt;br /&gt;
#include &amp;lt;pthread.h&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Ce qui donne en ligne de commande:&lt;br /&gt;
&amp;lt;source lang=&#039;bash&#039;&amp;gt;&lt;br /&gt;
gcc -lpthread thread_exemple.c&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Pour inclure la librairie dans [[Eclipse_install|Eclispe]] regardez [[C_devel#Ajouter_une_librairie_.C3.A0_un_projet|ici]].&lt;br /&gt;
= Premier thread =&lt;br /&gt;
Dans ce premier exemple, nous allons créer un thread qui affiche une message:&lt;br /&gt;
&amp;lt;source lang=&#039;c&#039;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;pthread.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void *thread_1(void *arg) {&lt;br /&gt;
	printf(&amp;quot;Nous sommes dans le thread.\n&amp;quot;);&lt;br /&gt;
	// Arrêt propre du thread&lt;br /&gt;
	pthread_exit(NULL);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
	// Création de la variable qui va contenir le thread&lt;br /&gt;
	pthread_t thread1;&lt;br /&gt;
	printf(&amp;quot;Avant la création du thread.\n&amp;quot;);&lt;br /&gt;
	// Création du thread&lt;br /&gt;
	pthread_create(&amp;amp;thread1, NULL, thread_1, NULL);&lt;br /&gt;
	printf(&amp;quot;Après la création du thread.\n&amp;quot;);&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Dans cet exemple, nous utilisons la fonction suivante:&lt;br /&gt;
&amp;lt;source lang=&#039;c&#039;&amp;gt;&lt;br /&gt;
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* &#039;&#039;*thread&#039;&#039; est une référence vers la variable qui va contenir le thread;&lt;br /&gt;
* &#039;&#039;*attr&#039;&#039; correspond aux arguments de création du thread, passer à la fonction &#039;&#039;pthread_create&#039;&#039;;&lt;br /&gt;
* &#039;&#039;*(*start_routine)&#039;&#039; est un pointeur vers la fonction exécutée par le thread;&lt;br /&gt;
* &#039;&#039;*arg&#039;&#039; est un pointeur vers les arguments passés en paramètres à la fonction &#039;&#039;start_routine&#039;&#039;;&lt;br /&gt;
* le code retour de la fonction varie entre :&lt;br /&gt;
** &#039;&#039;0&#039;&#039; &amp;amp;rarr; lorsque tout s&#039;est bien passé;&lt;br /&gt;
** EAGAIN &amp;amp;rarr; lorsque les ressources sont insuffisantes ou le nombre de threads maximum est atteint;&lt;br /&gt;
** EINVAL &amp;amp;rarr; lorsqu&#039;un argument invalide est passé en paramètre;&lt;br /&gt;
** EPERM &amp;amp;rarr; lorsqu&#039;il y a un problème de droit sur l&#039;ordonnanceur (passé en paramètre); &lt;br /&gt;
Lorsque l&#039;on exécute cet exemple, on a le résultat suivant :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Avant la création du thread.&lt;br /&gt;
Après la création du thread.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On ne voit pas le message du thread car le programme se termine sans attendre la fin de son exécution. Pour attendre la fin de l&#039;exécution du thread, il faut utiliser la fonction suivante :&lt;br /&gt;
&amp;lt;source lang=&#039;c&#039;&amp;gt;&lt;br /&gt;
int pthread_join(pthread_t thread, void **retval);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* &#039;&#039;*thread&#039;&#039; est une référence vers la variable qui va contenir le thread;&lt;br /&gt;
* &#039;&#039;**retval&#039;&#039; est un pointeur vers un entier qui contiendra la valeur de retour du thread;&lt;br /&gt;
* le code retour de la fonction varie entre :&lt;br /&gt;
** &#039;&#039;0&#039;&#039; &amp;amp;rarr; lorsque tout s&#039;est bien passé;&lt;br /&gt;
** EDEADLK &amp;amp;rarr; lorsqu&#039;il y a un &#039;&#039;deadlock&#039;&#039;;&lt;br /&gt;
** EINVAL &amp;amp;rarr; lorsque le thread n&#039;est pas joignable;&lt;br /&gt;
** ESRCH &amp;amp;rarr; si le thread n&#039;existe pas;&lt;br /&gt;
Ajoutez la ligne suivante :&lt;br /&gt;
&amp;lt;source lang=&#039;c&#039;&amp;gt;&lt;br /&gt;
pthread_join(thread1, NULL);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
après l&#039;appel de la fonction &#039;&#039;pthread_create&#039;&#039; pour avoir le résultat suivant:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Avant la création du thread.&lt;br /&gt;
Nous sommes dans le thread.&lt;br /&gt;
Après la création du thread.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Modification d&#039;une variable =&lt;br /&gt;
Dans cet exemple nous allons incrémenter dans le thread un entier qui est déclaré dans le processus principal:&lt;br /&gt;
&amp;lt;source lang=&#039;c&#039;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;pthread.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void *thread_1(void *arg) {&lt;br /&gt;
	int *i = (int *) arg;&lt;br /&gt;
	(*i)++;&lt;br /&gt;
	// Arrêt propre du thread&lt;br /&gt;
	pthread_exit(NULL);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
	// Création de la variable qui va contenir le thread&lt;br /&gt;
	int i = 1;&lt;br /&gt;
	pthread_t thread1;&lt;br /&gt;
	printf(&amp;quot;Avant la création du thread, i = %i.\n&amp;quot;, i);&lt;br /&gt;
	// Création du thread&lt;br /&gt;
	pthread_create(&amp;amp;thread1, NULL, thread_1, &amp;amp;i);&lt;br /&gt;
	pthread_join(thread1, NULL);&lt;br /&gt;
	printf(&amp;quot;Après la création du thread, i = %i.\n&amp;quot;, i);&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Cela donne le résultat suivant :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Avant la création du thread, i = 1.&lt;br /&gt;
Après la création du thread, i = 2.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On peut remarquer que:&lt;br /&gt;
* dans le processus principal, on passe une référence vers la variable &#039;&#039;i&#039;&#039; (&#039;&#039;&amp;amp;i&#039;&#039;) dans la fonction &#039;&#039;pthread_create&#039;&#039;;&lt;br /&gt;
* dans le thread on récupère le pointeur vers cette variable et on la déclare comme étant un entier : &lt;br /&gt;
&amp;lt;source lang=&#039;c&#039;&amp;gt;&lt;br /&gt;
int *i = (int *) arg;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* on incrémente la valeur et non l&#039;adresse pointée :&lt;br /&gt;
&amp;lt;source lang=&#039;c&#039;&amp;gt;&lt;br /&gt;
(*i)++;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Exclusion mutuelle =&lt;br /&gt;
Lorsque l&#039;on cherche à modifier dans plusieurs threads une variable globale, il peut y avoir un problème d&#039;accès concurrent (modification / lecture simultanée). Ce problème peut être réglé en utilisant ce que l&#039;on appelle un &#039;&#039;mutex&#039;&#039; (&#039;&#039;mutual exclusion&#039;&#039;) et pour des questions pratiques, on utilise généralement une structure pour associer la variable à son &#039;&#039;mutex&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
typedef struct mutex_data {&lt;br /&gt;
	int data;&lt;br /&gt;
	pthread_mutex_t mutex;&lt;br /&gt;
} mutex_data;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dans l&#039;exemple ci-dessus on créer une structure de données avec un entier et un &#039;&#039;mutex&#039;&#039;.&lt;br /&gt;
Il existe plusieurs fonctions pour manipuler les &#039;&#039;mutex&#039;&#039; et voici les principales:&lt;br /&gt;
*pour initialiser un &#039;&#039;mutex&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int pthread_mutex_init(pthread_mutex_t* mutex, const pthread_mutexattr_t* attr )&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*pour verrouiller un &#039;&#039;mutex&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int pthread_mutex_lock(pthread_mutex_t *mutex)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*pour déverrouiller un &#039;&#039;mutex&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int pthread_mutex_unlock(pthread_mutex_t *mutex)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*pour détruire un &#039;&#039;mutex&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int pthread_mutex_destroy(pthread_mutex_t *mutex)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le programme suivant permet d&#039;incrémenter la valeur d&#039;un entier dans plusieurs thread différents:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;pthread.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Nombre total de thread&lt;br /&gt;
#define NB_THREAD 2&lt;br /&gt;
// Limite de l&#039;incrément&lt;br /&gt;
#define INCREMENT_LIMIT 10&lt;br /&gt;
&lt;br /&gt;
// Tableau contenant les threads&lt;br /&gt;
pthread_t threads[NB_THREAD];&lt;br /&gt;
&lt;br /&gt;
// Structure de données contenant le mutex&lt;br /&gt;
typedef struct mutex_data {&lt;br /&gt;
	int data;&lt;br /&gt;
	pthread_mutex_t mutex;&lt;br /&gt;
} mutex_data;&lt;br /&gt;
&lt;br /&gt;
// Fonction exécutée dans le thread&lt;br /&gt;
void * job(void *arg) {&lt;br /&gt;
	mutex_data *md = (mutex_data*) arg;&lt;br /&gt;
	pthread_t tid = pthread_self();&lt;br /&gt;
	while ((*md).data &amp;lt; INCREMENT_LIMIT) {&lt;br /&gt;
		// Vérouillage du mutex&lt;br /&gt;
		pthread_mutex_lock(&amp;amp;(*md).mutex);&lt;br /&gt;
		(*md).data++;&lt;br /&gt;
		// Dévérouillage du mutex&lt;br /&gt;
		pthread_mutex_unlock(&amp;amp;(*md).mutex);&lt;br /&gt;
		printf(&amp;quot;thread [ %ld ] data [ %i ]\n&amp;quot;, tid, (*md).data);&lt;br /&gt;
		// Pause l&#039;exécution du thread pendant 1 seconde&lt;br /&gt;
		sleep(1);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Fin du thread %ld\n&amp;quot;, tid);&lt;br /&gt;
	pthread_exit(NULL);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Fonction principale&lt;br /&gt;
int main() {&lt;br /&gt;
	// Création du mutex&lt;br /&gt;
	mutex_data md;&lt;br /&gt;
	// Initialisation de la donnée&lt;br /&gt;
	md.data = 0;&lt;br /&gt;
	// Initialisation du mutex&lt;br /&gt;
	if (pthread_mutex_init(&amp;amp;md.mutex, NULL) != 0) {&lt;br /&gt;
		printf(&amp;quot;\n mutex init failed\n&amp;quot;);&lt;br /&gt;
		return EXIT_FAILURE;&lt;br /&gt;
	}&lt;br /&gt;
	// Boucle de création des threads&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_THREAD; i++) {&lt;br /&gt;
		// Création du thread et passage de la structure par référence&lt;br /&gt;
		int err = pthread_create(&amp;amp;threads[i], NULL, job, &amp;amp;md);&lt;br /&gt;
		if (err != 0) {&lt;br /&gt;
			printf(&amp;quot;Echec de la création du thread: [%s]&amp;quot;, strerror(err));&lt;br /&gt;
			break;&lt;br /&gt;
		}&lt;br /&gt;
		printf(&amp;quot;Création du thread numéro %ld\n&amp;quot;, threads[i]);&lt;br /&gt;
	}&lt;br /&gt;
	// En attente des threads&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_THREAD; i++) {&lt;br /&gt;
		pthread_join(threads[i], NULL);&lt;br /&gt;
	}&lt;br /&gt;
	// Destruction du mutex&lt;br /&gt;
	pthread_mutex_destroy(&amp;amp;md.mutex);&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Un exemple de l&#039;exécution de ce programme donne la sortie suivante :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Création du thread numéro 140530420492032&lt;br /&gt;
Création du thread numéro 140530410002176&lt;br /&gt;
thread [ 140530410002176 ] data [ 1 ]&lt;br /&gt;
thread [ 140530420492032 ] data [ 2 ]&lt;br /&gt;
thread [ 140530410002176 ] data [ 3 ]&lt;br /&gt;
thread [ 140530420492032 ] data [ 4 ]&lt;br /&gt;
thread [ 140530410002176 ] data [ 5 ]&lt;br /&gt;
thread [ 140530420492032 ] data [ 6 ]&lt;br /&gt;
thread [ 140530420492032 ] data [ 7 ]&lt;br /&gt;
thread [ 140530410002176 ] data [ 8 ]&lt;br /&gt;
thread [ 140530420492032 ] data [ 9 ]&lt;br /&gt;
thread [ 140530410002176 ] data [ 10 ]&lt;br /&gt;
Fin du thread 140530420492032&lt;br /&gt;
Fin du thread 140530410002176&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Magsss</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=C_pthread&amp;diff=3253</id>
		<title>C pthread</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=C_pthread&amp;diff=3253"/>
		<updated>2018-11-01T17:50:22Z</updated>

		<summary type="html">&lt;p&gt;Magsss : /* Modification d&amp;#039;une variable */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Les threads ou &#039;&#039;processus légers&#039;&#039; sont utilisés pour paralléliser l&#039;exécution dans un programme. Ils sont dits &#039;&#039;légers&#039;&#039; car ils s&#039;exécutent dans le même contexte que le processus d&#039;exécution principal qui les crée et consomment donc moins de mémoire / CPU.&lt;br /&gt;
&lt;br /&gt;
= Pré-requis =&lt;br /&gt;
Pour pouvoir créer des threads il faut utiliser la librairie &#039;&#039;pthread&#039;&#039; (POSIX thread):&lt;br /&gt;
&amp;lt;source lang=&#039;c&#039;&amp;gt;&lt;br /&gt;
#include &amp;lt;pthread.h&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Ce qui donne en ligne de commande:&lt;br /&gt;
&amp;lt;source lang=&#039;bash&#039;&amp;gt;&lt;br /&gt;
gcc -lpthread thread_exemple.c&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Pour inclure la librairie dans [[Eclipse_install|Eclispe]] regardez [[C_devel#Ajouter_une_librairie_.C3.A0_un_projet|ici]].&lt;br /&gt;
= Premier thread =&lt;br /&gt;
Dans ce premier exemple, nous allons créer un thread qui affiche une message:&lt;br /&gt;
&amp;lt;source lang=&#039;c&#039;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;pthread.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void *thread_1(void *arg) {&lt;br /&gt;
	printf(&amp;quot;Nous sommes dans le thread.\n&amp;quot;);&lt;br /&gt;
	// Arrêt propre du thread&lt;br /&gt;
	pthread_exit(NULL);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
	// Création de la variable qui va contenir le thread&lt;br /&gt;
	pthread_t thread1;&lt;br /&gt;
	printf(&amp;quot;Avant la création du thread.\n&amp;quot;);&lt;br /&gt;
	// Création du thread&lt;br /&gt;
	pthread_create(&amp;amp;thread1, NULL, thread_1, NULL);&lt;br /&gt;
	printf(&amp;quot;Après la création du thread.\n&amp;quot;);&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Dans cet exemple, nous utilisons la fonction suivante:&lt;br /&gt;
&amp;lt;source lang=&#039;c&#039;&amp;gt;&lt;br /&gt;
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* &#039;&#039;*thread&#039;&#039; est une référence vers la variable qui va contenir le thread;&lt;br /&gt;
* &#039;&#039;*attr&#039;&#039; correspond aux arguments de création du thread, passer à la fonction &#039;&#039;pthread_create&#039;&#039;;&lt;br /&gt;
* &#039;&#039;*(*start_routine)&#039;&#039; est un pointeur vers la fonction exécutée par le thread;&lt;br /&gt;
* &#039;&#039;*arg&#039;&#039; est un pointeur vers les arguments passés en paramètres à la fonction &#039;&#039;start_routine&#039;&#039;;&lt;br /&gt;
* le code retour de la fonction varie entre :&lt;br /&gt;
** &#039;&#039;0&#039;&#039; &amp;amp;rarr; lorsque tout s&#039;est bien passé;&lt;br /&gt;
** EAGAIN &amp;amp;rarr; lorsque les ressources sont insuffisantes ou le nombre de threads maximum est atteint;&lt;br /&gt;
** EINVAL &amp;amp;rarr; lorsqu&#039;un argument invalide est passé en paramètre;&lt;br /&gt;
** EPERM &amp;amp;rarr; lorsqu&#039;il y a un problème de droit sur l&#039;ordonnanceur (passé en paramètre); &lt;br /&gt;
Lorsque l&#039;on exécute cet exemple, on a le résultat suivant :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Avant la création du thread.&lt;br /&gt;
Après la création du thread.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On ne voit pas le message du thread car le programme se termine sans attendre la fin de son exécution. Pour attendre la fin de l&#039;exécution du thread, il faut utiliser la fonction suivante :&lt;br /&gt;
&amp;lt;source lang=&#039;c&#039;&amp;gt;&lt;br /&gt;
int pthread_join(pthread_t thread, void **retval);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* &#039;&#039;*thread&#039;&#039; est une référence vers la variable qui va contenir le thread;&lt;br /&gt;
* &#039;&#039;**retval&#039;&#039; est un pointeur vers un entier qui contiendra la valeur de retour du thread;&lt;br /&gt;
* le code retour de la fonction varie entre :&lt;br /&gt;
** &#039;&#039;0&#039;&#039; &amp;amp;rarr; lorsque tout s&#039;est bien passé;&lt;br /&gt;
** EDEADLK &amp;amp;rarr; lorsqu&#039;il y a un &#039;&#039;deadlock&#039;&#039;;&lt;br /&gt;
** EINVAL &amp;amp;rarr; lorsque le thread n&#039;est pas joignable;&lt;br /&gt;
** ESRCH &amp;amp;rarr; si le thread n&#039;existe pas;&lt;br /&gt;
Ajoutez la ligne suivante :&lt;br /&gt;
&amp;lt;source lang=&#039;c&#039;&amp;gt;&lt;br /&gt;
pthread_join(thread1, NULL);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
après l&#039;appel de la fonction &#039;&#039;pthread_create&#039;&#039; pour avoir le résultat suivant:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Avant la création du thread.&lt;br /&gt;
Nous sommes dans le thread.&lt;br /&gt;
Après la création du thread.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Modification d&#039;une variable =&lt;br /&gt;
Dans cet exemple nous allons incrémenter dans le thread un entier qui est déclaré dans le processus principal:&lt;br /&gt;
&amp;lt;source lang=&#039;c&#039;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;pthread.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void *thread_1(void *arg) {&lt;br /&gt;
	int *i = (int *) arg;&lt;br /&gt;
	(*i)++;&lt;br /&gt;
	// Arrêt propre du thread&lt;br /&gt;
	pthread_exit(NULL);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
	// Création de la variable qui va contenir le thread&lt;br /&gt;
	int i = 1;&lt;br /&gt;
	pthread_t thread1;&lt;br /&gt;
	printf(&amp;quot;Avant la création du thread, i = %i.\n&amp;quot;, i);&lt;br /&gt;
	// Création du thread&lt;br /&gt;
	pthread_create(&amp;amp;thread1, NULL, thread_1, &amp;amp;i);&lt;br /&gt;
	pthread_join(thread1, NULL);&lt;br /&gt;
	printf(&amp;quot;Après la création du thread, i = %i.\n&amp;quot;, i);&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Cela donne le résultat suivant :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Avant la création du thread, i = 1.&lt;br /&gt;
Après la création du thread, i = 2.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On peut remarquer que:&lt;br /&gt;
* dans le processus principal, on passe une référence vers la variable &#039;&#039;i&#039;&#039; (&#039;&#039;&amp;amp;i&#039;&#039;) dans la fonction &#039;&#039;pthread_create&#039;&#039;;&lt;br /&gt;
* dans le thread on récupère le pointeur vers cette variable et on la déclare comme étant un entier : &lt;br /&gt;
&amp;lt;source lang=&#039;c&#039;&amp;gt;&lt;br /&gt;
int *i = (int *) arg;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* on incrémente la valeur et non l&#039;adresse pointée :&lt;br /&gt;
&amp;lt;source lang=&#039;c&#039;&amp;gt;&lt;br /&gt;
(*i)++;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Exclusion mutuelle =&lt;br /&gt;
Lorsque l&#039;on cherche à modifier dans plusieurs threads, une variable globale, il peut y avoir un problème d&#039;accès concurrent (modification / lecture simultanée). Ce problème peut être réglé en utilisant ce que l&#039;on appel un &#039;&#039;mutex&#039;&#039; (&#039;&#039;mutual exclusion&#039;&#039;) et pour des questions pratiques, on utilise généralement une structure pour associer la variable à son &#039;&#039;mutex&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
typedef struct mutex_data {&lt;br /&gt;
	int data;&lt;br /&gt;
	pthread_mutex_t mutex;&lt;br /&gt;
} mutex_data;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dans l&#039;exemple ci-dessus on créer une structure de données avec un entier et un &#039;&#039;mutex&#039;&#039;.&lt;br /&gt;
Il existe plusieurs fonctions pour manipuler les &#039;&#039;mutex&#039;&#039; et voici les principales:&lt;br /&gt;
*pour initialiser un &#039;&#039;mutex&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int pthread_mutex_init(pthread_mutex_t* mutex, const pthread_mutexattr_t* attr )&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*pour verrouiller un &#039;&#039;mutex&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int pthread_mutex_lock(pthread_mutex_t *mutex)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*pour déverrouiller un &#039;&#039;mutex&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int pthread_mutex_unlock(pthread_mutex_t *mutex)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*pour détruire un &#039;&#039;mutex&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int pthread_mutex_destroy(pthread_mutex_t *mutex)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le programme suivant permet d&#039;incrémenter la valeur d&#039;un entier dans plusieurs thread différents:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;pthread.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Nombre total de thread&lt;br /&gt;
#define NB_THREAD 2&lt;br /&gt;
// Limite de l&#039;incrément&lt;br /&gt;
#define INCREMENT_LIMIT 10&lt;br /&gt;
&lt;br /&gt;
// Tableau contenant les threads&lt;br /&gt;
pthread_t threads[NB_THREAD];&lt;br /&gt;
&lt;br /&gt;
// Structure de données contenant le mutex&lt;br /&gt;
typedef struct mutex_data {&lt;br /&gt;
	int data;&lt;br /&gt;
	pthread_mutex_t mutex;&lt;br /&gt;
} mutex_data;&lt;br /&gt;
&lt;br /&gt;
// Fonction exécutée dans le thread&lt;br /&gt;
void * job(void *arg) {&lt;br /&gt;
	mutex_data *md = (mutex_data*) arg;&lt;br /&gt;
	pthread_t tid = pthread_self();&lt;br /&gt;
	while ((*md).data &amp;lt; INCREMENT_LIMIT) {&lt;br /&gt;
		// Vérouillage du mutex&lt;br /&gt;
		pthread_mutex_lock(&amp;amp;(*md).mutex);&lt;br /&gt;
		(*md).data++;&lt;br /&gt;
		// Dévérouillage du mutex&lt;br /&gt;
		pthread_mutex_unlock(&amp;amp;(*md).mutex);&lt;br /&gt;
		printf(&amp;quot;thread [ %ld ] data [ %i ]\n&amp;quot;, tid, (*md).data);&lt;br /&gt;
		// Pause l&#039;exécution du thread pendant 1 seconde&lt;br /&gt;
		sleep(1);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Fin du thread %ld\n&amp;quot;, tid);&lt;br /&gt;
	pthread_exit(NULL);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Fonction principale&lt;br /&gt;
int main() {&lt;br /&gt;
	// Création du mutex&lt;br /&gt;
	mutex_data md;&lt;br /&gt;
	// Initialisation de la donnée&lt;br /&gt;
	md.data = 0;&lt;br /&gt;
	// Initialisation du mutex&lt;br /&gt;
	if (pthread_mutex_init(&amp;amp;md.mutex, NULL) != 0) {&lt;br /&gt;
		printf(&amp;quot;\n mutex init failed\n&amp;quot;);&lt;br /&gt;
		return EXIT_FAILURE;&lt;br /&gt;
	}&lt;br /&gt;
	// Boucle de création des threads&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_THREAD; i++) {&lt;br /&gt;
		// Création du thread et passage de la structure par référence&lt;br /&gt;
		int err = pthread_create(&amp;amp;threads[i], NULL, job, &amp;amp;md);&lt;br /&gt;
		if (err != 0) {&lt;br /&gt;
			printf(&amp;quot;Echec de la création du thread: [%s]&amp;quot;, strerror(err));&lt;br /&gt;
			break;&lt;br /&gt;
		}&lt;br /&gt;
		printf(&amp;quot;Création du thread numéro %ld\n&amp;quot;, threads[i]);&lt;br /&gt;
	}&lt;br /&gt;
	// En attente des threads&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_THREAD; i++) {&lt;br /&gt;
		pthread_join(threads[i], NULL);&lt;br /&gt;
	}&lt;br /&gt;
	// Destruction du mutex&lt;br /&gt;
	pthread_mutex_destroy(&amp;amp;md.mutex);&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Un exemple de l&#039;exécution de ce programme donne la sortie suivante :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Création du thread numéro 140530420492032&lt;br /&gt;
Création du thread numéro 140530410002176&lt;br /&gt;
thread [ 140530410002176 ] data [ 1 ]&lt;br /&gt;
thread [ 140530420492032 ] data [ 2 ]&lt;br /&gt;
thread [ 140530410002176 ] data [ 3 ]&lt;br /&gt;
thread [ 140530420492032 ] data [ 4 ]&lt;br /&gt;
thread [ 140530410002176 ] data [ 5 ]&lt;br /&gt;
thread [ 140530420492032 ] data [ 6 ]&lt;br /&gt;
thread [ 140530420492032 ] data [ 7 ]&lt;br /&gt;
thread [ 140530410002176 ] data [ 8 ]&lt;br /&gt;
thread [ 140530420492032 ] data [ 9 ]&lt;br /&gt;
thread [ 140530410002176 ] data [ 10 ]&lt;br /&gt;
Fin du thread 140530420492032&lt;br /&gt;
Fin du thread 140530410002176&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Magsss</name></author>
	</entry>
	<entry>
		<id>https://tala-informatique.fr/index.php?title=C_pthread&amp;diff=3252</id>
		<title>C pthread</title>
		<link rel="alternate" type="text/html" href="https://tala-informatique.fr/index.php?title=C_pthread&amp;diff=3252"/>
		<updated>2018-11-01T17:49:53Z</updated>

		<summary type="html">&lt;p&gt;Magsss : /* Premier thread */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Introduction =&lt;br /&gt;
Les threads ou &#039;&#039;processus légers&#039;&#039; sont utilisés pour paralléliser l&#039;exécution dans un programme. Ils sont dits &#039;&#039;légers&#039;&#039; car ils s&#039;exécutent dans le même contexte que le processus d&#039;exécution principal qui les crée et consomment donc moins de mémoire / CPU.&lt;br /&gt;
&lt;br /&gt;
= Pré-requis =&lt;br /&gt;
Pour pouvoir créer des threads il faut utiliser la librairie &#039;&#039;pthread&#039;&#039; (POSIX thread):&lt;br /&gt;
&amp;lt;source lang=&#039;c&#039;&amp;gt;&lt;br /&gt;
#include &amp;lt;pthread.h&amp;gt;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Ce qui donne en ligne de commande:&lt;br /&gt;
&amp;lt;source lang=&#039;bash&#039;&amp;gt;&lt;br /&gt;
gcc -lpthread thread_exemple.c&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Pour inclure la librairie dans [[Eclipse_install|Eclispe]] regardez [[C_devel#Ajouter_une_librairie_.C3.A0_un_projet|ici]].&lt;br /&gt;
= Premier thread =&lt;br /&gt;
Dans ce premier exemple, nous allons créer un thread qui affiche une message:&lt;br /&gt;
&amp;lt;source lang=&#039;c&#039;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;pthread.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void *thread_1(void *arg) {&lt;br /&gt;
	printf(&amp;quot;Nous sommes dans le thread.\n&amp;quot;);&lt;br /&gt;
	// Arrêt propre du thread&lt;br /&gt;
	pthread_exit(NULL);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
	// Création de la variable qui va contenir le thread&lt;br /&gt;
	pthread_t thread1;&lt;br /&gt;
	printf(&amp;quot;Avant la création du thread.\n&amp;quot;);&lt;br /&gt;
	// Création du thread&lt;br /&gt;
	pthread_create(&amp;amp;thread1, NULL, thread_1, NULL);&lt;br /&gt;
	printf(&amp;quot;Après la création du thread.\n&amp;quot;);&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Dans cet exemple, nous utilisons la fonction suivante:&lt;br /&gt;
&amp;lt;source lang=&#039;c&#039;&amp;gt;&lt;br /&gt;
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* &#039;&#039;*thread&#039;&#039; est une référence vers la variable qui va contenir le thread;&lt;br /&gt;
* &#039;&#039;*attr&#039;&#039; correspond aux arguments de création du thread, passer à la fonction &#039;&#039;pthread_create&#039;&#039;;&lt;br /&gt;
* &#039;&#039;*(*start_routine)&#039;&#039; est un pointeur vers la fonction exécutée par le thread;&lt;br /&gt;
* &#039;&#039;*arg&#039;&#039; est un pointeur vers les arguments passés en paramètres à la fonction &#039;&#039;start_routine&#039;&#039;;&lt;br /&gt;
* le code retour de la fonction varie entre :&lt;br /&gt;
** &#039;&#039;0&#039;&#039; &amp;amp;rarr; lorsque tout s&#039;est bien passé;&lt;br /&gt;
** EAGAIN &amp;amp;rarr; lorsque les ressources sont insuffisantes ou le nombre de threads maximum est atteint;&lt;br /&gt;
** EINVAL &amp;amp;rarr; lorsqu&#039;un argument invalide est passé en paramètre;&lt;br /&gt;
** EPERM &amp;amp;rarr; lorsqu&#039;il y a un problème de droit sur l&#039;ordonnanceur (passé en paramètre); &lt;br /&gt;
Lorsque l&#039;on exécute cet exemple, on a le résultat suivant :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Avant la création du thread.&lt;br /&gt;
Après la création du thread.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On ne voit pas le message du thread car le programme se termine sans attendre la fin de son exécution. Pour attendre la fin de l&#039;exécution du thread, il faut utiliser la fonction suivante :&lt;br /&gt;
&amp;lt;source lang=&#039;c&#039;&amp;gt;&lt;br /&gt;
int pthread_join(pthread_t thread, void **retval);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* &#039;&#039;*thread&#039;&#039; est une référence vers la variable qui va contenir le thread;&lt;br /&gt;
* &#039;&#039;**retval&#039;&#039; est un pointeur vers un entier qui contiendra la valeur de retour du thread;&lt;br /&gt;
* le code retour de la fonction varie entre :&lt;br /&gt;
** &#039;&#039;0&#039;&#039; &amp;amp;rarr; lorsque tout s&#039;est bien passé;&lt;br /&gt;
** EDEADLK &amp;amp;rarr; lorsqu&#039;il y a un &#039;&#039;deadlock&#039;&#039;;&lt;br /&gt;
** EINVAL &amp;amp;rarr; lorsque le thread n&#039;est pas joignable;&lt;br /&gt;
** ESRCH &amp;amp;rarr; si le thread n&#039;existe pas;&lt;br /&gt;
Ajoutez la ligne suivante :&lt;br /&gt;
&amp;lt;source lang=&#039;c&#039;&amp;gt;&lt;br /&gt;
pthread_join(thread1, NULL);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
après l&#039;appel de la fonction &#039;&#039;pthread_create&#039;&#039; pour avoir le résultat suivant:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Avant la création du thread.&lt;br /&gt;
Nous sommes dans le thread.&lt;br /&gt;
Après la création du thread.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Modification d&#039;une variable =&lt;br /&gt;
Dans cet exemple nous allons incrémenter dans le thread un entier qui est déclarer dans le processus principal:&lt;br /&gt;
&amp;lt;source lang=&#039;c&#039;&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;pthread.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
void *thread_1(void *arg) {&lt;br /&gt;
	int *i = (int *) arg;&lt;br /&gt;
	(*i)++;&lt;br /&gt;
	// Arrêt propre du thread&lt;br /&gt;
	pthread_exit(NULL);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
int main(void) {&lt;br /&gt;
	// Création de la variable qui va contenir le thread&lt;br /&gt;
	int i = 1;&lt;br /&gt;
	pthread_t thread1;&lt;br /&gt;
	printf(&amp;quot;Avant la création du thread, i = %i.\n&amp;quot;, i);&lt;br /&gt;
	// Création du thread&lt;br /&gt;
	pthread_create(&amp;amp;thread1, NULL, thread_1, &amp;amp;i);&lt;br /&gt;
	pthread_join(thread1, NULL);&lt;br /&gt;
	printf(&amp;quot;Après la création du thread, i = %i.\n&amp;quot;, i);&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Cela donne le résultat suivant :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Avant la création du thread, i = 1.&lt;br /&gt;
Après la création du thread, i = 2.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On peut remarquer que:&lt;br /&gt;
* dans le processus principal, on passe une référence vers la variable &#039;&#039;i&#039;&#039; (&#039;&#039;&amp;amp;i&#039;&#039;) dans la fonction &#039;&#039;pthread_create&#039;&#039;;&lt;br /&gt;
* dans le thread on récupère le pointeur vers cette variable et on la déclare comme étant un entier : &lt;br /&gt;
&amp;lt;source lang=&#039;c&#039;&amp;gt;&lt;br /&gt;
int *i = (int *) arg;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* on incrémente la valeur et non l&#039;adresse pointée :&lt;br /&gt;
&amp;lt;source lang=&#039;c&#039;&amp;gt;&lt;br /&gt;
(*i)++;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
= Exclusion mutuelle =&lt;br /&gt;
Lorsque l&#039;on cherche à modifier dans plusieurs threads, une variable globale, il peut y avoir un problème d&#039;accès concurrent (modification / lecture simultanée). Ce problème peut être réglé en utilisant ce que l&#039;on appel un &#039;&#039;mutex&#039;&#039; (&#039;&#039;mutual exclusion&#039;&#039;) et pour des questions pratiques, on utilise généralement une structure pour associer la variable à son &#039;&#039;mutex&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
typedef struct mutex_data {&lt;br /&gt;
	int data;&lt;br /&gt;
	pthread_mutex_t mutex;&lt;br /&gt;
} mutex_data;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Dans l&#039;exemple ci-dessus on créer une structure de données avec un entier et un &#039;&#039;mutex&#039;&#039;.&lt;br /&gt;
Il existe plusieurs fonctions pour manipuler les &#039;&#039;mutex&#039;&#039; et voici les principales:&lt;br /&gt;
*pour initialiser un &#039;&#039;mutex&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int pthread_mutex_init(pthread_mutex_t* mutex, const pthread_mutexattr_t* attr )&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*pour verrouiller un &#039;&#039;mutex&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int pthread_mutex_lock(pthread_mutex_t *mutex)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*pour déverrouiller un &#039;&#039;mutex&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int pthread_mutex_unlock(pthread_mutex_t *mutex)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
*pour détruire un &#039;&#039;mutex&#039;&#039;:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
int pthread_mutex_destroy(pthread_mutex_t *mutex)&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Le programme suivant permet d&#039;incrémenter la valeur d&#039;un entier dans plusieurs thread différents:&lt;br /&gt;
&amp;lt;source lang=&amp;quot;c&amp;quot;&amp;gt;&lt;br /&gt;
#include &amp;lt;pthread.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdio.h&amp;gt;&lt;br /&gt;
#include &amp;lt;stdlib.h&amp;gt;&lt;br /&gt;
#include &amp;lt;string.h&amp;gt;&lt;br /&gt;
#include &amp;lt;unistd.h&amp;gt;&lt;br /&gt;
&lt;br /&gt;
// Nombre total de thread&lt;br /&gt;
#define NB_THREAD 2&lt;br /&gt;
// Limite de l&#039;incrément&lt;br /&gt;
#define INCREMENT_LIMIT 10&lt;br /&gt;
&lt;br /&gt;
// Tableau contenant les threads&lt;br /&gt;
pthread_t threads[NB_THREAD];&lt;br /&gt;
&lt;br /&gt;
// Structure de données contenant le mutex&lt;br /&gt;
typedef struct mutex_data {&lt;br /&gt;
	int data;&lt;br /&gt;
	pthread_mutex_t mutex;&lt;br /&gt;
} mutex_data;&lt;br /&gt;
&lt;br /&gt;
// Fonction exécutée dans le thread&lt;br /&gt;
void * job(void *arg) {&lt;br /&gt;
	mutex_data *md = (mutex_data*) arg;&lt;br /&gt;
	pthread_t tid = pthread_self();&lt;br /&gt;
	while ((*md).data &amp;lt; INCREMENT_LIMIT) {&lt;br /&gt;
		// Vérouillage du mutex&lt;br /&gt;
		pthread_mutex_lock(&amp;amp;(*md).mutex);&lt;br /&gt;
		(*md).data++;&lt;br /&gt;
		// Dévérouillage du mutex&lt;br /&gt;
		pthread_mutex_unlock(&amp;amp;(*md).mutex);&lt;br /&gt;
		printf(&amp;quot;thread [ %ld ] data [ %i ]\n&amp;quot;, tid, (*md).data);&lt;br /&gt;
		// Pause l&#039;exécution du thread pendant 1 seconde&lt;br /&gt;
		sleep(1);&lt;br /&gt;
	}&lt;br /&gt;
	printf(&amp;quot;Fin du thread %ld\n&amp;quot;, tid);&lt;br /&gt;
	pthread_exit(NULL);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Fonction principale&lt;br /&gt;
int main() {&lt;br /&gt;
	// Création du mutex&lt;br /&gt;
	mutex_data md;&lt;br /&gt;
	// Initialisation de la donnée&lt;br /&gt;
	md.data = 0;&lt;br /&gt;
	// Initialisation du mutex&lt;br /&gt;
	if (pthread_mutex_init(&amp;amp;md.mutex, NULL) != 0) {&lt;br /&gt;
		printf(&amp;quot;\n mutex init failed\n&amp;quot;);&lt;br /&gt;
		return EXIT_FAILURE;&lt;br /&gt;
	}&lt;br /&gt;
	// Boucle de création des threads&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_THREAD; i++) {&lt;br /&gt;
		// Création du thread et passage de la structure par référence&lt;br /&gt;
		int err = pthread_create(&amp;amp;threads[i], NULL, job, &amp;amp;md);&lt;br /&gt;
		if (err != 0) {&lt;br /&gt;
			printf(&amp;quot;Echec de la création du thread: [%s]&amp;quot;, strerror(err));&lt;br /&gt;
			break;&lt;br /&gt;
		}&lt;br /&gt;
		printf(&amp;quot;Création du thread numéro %ld\n&amp;quot;, threads[i]);&lt;br /&gt;
	}&lt;br /&gt;
	// En attente des threads&lt;br /&gt;
	for (int i = 0; i &amp;lt; NB_THREAD; i++) {&lt;br /&gt;
		pthread_join(threads[i], NULL);&lt;br /&gt;
	}&lt;br /&gt;
	// Destruction du mutex&lt;br /&gt;
	pthread_mutex_destroy(&amp;amp;md.mutex);&lt;br /&gt;
	return EXIT_SUCCESS;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
Un exemple de l&#039;exécution de ce programme donne la sortie suivante :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Création du thread numéro 140530420492032&lt;br /&gt;
Création du thread numéro 140530410002176&lt;br /&gt;
thread [ 140530410002176 ] data [ 1 ]&lt;br /&gt;
thread [ 140530420492032 ] data [ 2 ]&lt;br /&gt;
thread [ 140530410002176 ] data [ 3 ]&lt;br /&gt;
thread [ 140530420492032 ] data [ 4 ]&lt;br /&gt;
thread [ 140530410002176 ] data [ 5 ]&lt;br /&gt;
thread [ 140530420492032 ] data [ 6 ]&lt;br /&gt;
thread [ 140530420492032 ] data [ 7 ]&lt;br /&gt;
thread [ 140530410002176 ] data [ 8 ]&lt;br /&gt;
thread [ 140530420492032 ] data [ 9 ]&lt;br /&gt;
thread [ 140530410002176 ] data [ 10 ]&lt;br /&gt;
Fin du thread 140530420492032&lt;br /&gt;
Fin du thread 140530410002176&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Magsss</name></author>
	</entry>
</feed>