Question FAQ : Que fait exactement l’option « SO_LINGER » ?

L’option « SO_LINGER » modifie le comportement de l’opération close() comme décrit ci-dessous. Aucune autre opération sur les sockets que close() n’est affectée par cette option.
La description des effets de l’option « SO_LINGER » a été extraite des pages d’aide des fonctions setsockopt() et close() de plusieurs systèmes mais pourrait être non applicable à votre système. Les différences dans les implémentations vont de « aucun support de l’option SO_LINGER » ou « support seulement partiel » ou encore « à voir suivant les particularités dans l’implémentation » (voir les notes concernant la portabilité à la fin).
En outre, le but de « SO_LINGER » est très, très spécifique et seule une infime minorité d’applications en ont réellement besoin. Sauf si vous êtes très familier avec les subtilités de TCP et de l'API socket BSD, on peut très facilement se retrouver à utiliser « SO_LINGER » d'une manière pour laquelle il n'a pas été conçu.
Les effets de setsockopt(..., SO_LINGER,...) dépendent des valeurs passées dans la structure linger (le troisième paramètre) :
Cas 1 : linger->l_onoff vaut 0 (linger->l_linger n’est pas utilisé), c’est le fonctionnement par défaut. Après un close(), la pile réseau tente d’arrêter proprement la connexion après s’être assurée que toutes les données en attente d’envoi soient envoyées. Dans le cas d’un protocole orienté connexion comme TCP, la pile réseau s’assure aussi que les données envoyées soient correctement acquittées par le distant. La pile réseau va réaliser la même chose que la socket soit en mode bloquant ou en mode non bloquant.
Cas 2 : linger->l_onoff ne vaut pas 0 et linger->l_linger vaut 0. Un appel à close() retourne immédiatement. La pile réseau ignore toutes les données en attente d’envoi et, dans le cas d’un protocole orienté connexion comme TCP, envoi un paquet RST (reset) au distant (c’est ce que l’on appelle une fermeture dure ou abortive). Toutes les tentatives faites par le distant pour lire ou envoyer des données se traduiront par une erreur « ECONNRESET ».
Cas 3 : linger->l_onoff ne vaut pas 0 et linger->l_linger ne vaut pas 0 non plus. Un appel à close() sera bloquant (si la socket est en mode bloquant) ou générera une erreur « EWOULDBLOCK » (si la socket est en mode non bloquant) jusqu’à ce que la fermeture de la socket soit effective ou jusqu’à l’expiration de la durée spécifiée par la valeur « linger->l_linger ». En cas de timeout, la pile réseau se comporte comme dans le cas 2.
Note de portabilité 1: Quelques implémentations de l’API socket BSD n’implémentent pas du tout l’option « SO_LINGER ». Sur ces systèmes, utiliser cet option va soit échouer avec un code d’erreur « EINVAL » soit être (silencieusement) ignorée. Avoir la définition de « SO_LINGER » définie dans les headers n’est pas une garantie que l’option « SO_LINGER » soit effectivement implémentée.
Note de portabilité 2 : Parce que la documentation BSD sur l’option « SO_LINGER » est rare et insuffisante, il n’est pas surprenant de trouver des implémentations interpréter les effets de cette option de manières si différentes. Par exemple, les effets de « SO_LINGER » sur une socket en mode non bloquant ne sont absolument pas mentionnés et en conséquence sont traités de manières différentes sur les différentes plateformes. Si on regarde le cas 3 par exemple, quelques implémentations se comportent comme décrit ci-dessus. Sur d’autres, une socket non bloquante réussi immédiatement et laisse le soin à la pile réseau de terminer le travail. D’autres ignorent le fait que la socket soit non bloquante et se comportent comme si la socket était bloquante. D’autres encore se comportent comme si « SO_LINGER » était sans effet (comme dans le cas 1) ou ignorent « linger->l_linger » (le cas 3 est traité comme pour le cas 2). Etant donné le manque de documentation, de telles différences ne sont indicateurs d’une implémentation incomplète ou cassée. Elles sont simplement différentes et pas incorrectes.
Note de portabilité 3 : Quelques implémentations de l’API socket BSD n’implémentent pas l’option « SO_LINGER » complètement. Sur ces systèmes, la valeur de « linger->l_linger » est ignorée (toujours traitée comme si elle valait 0).
Note technique de développement : L’option « SO_LINGER » n’affecte pas (et ne devrait pas affecter) l’implémentation faite par la pile réseau de l’état « TIME_WAIT ». Dans tous les cas, l’option « SO_LINGER » n’est pas la solution pour contourner l’état « TIME_WAIT ». Si une application espère ouvrir et fermer des sockets TCP de manière rapide, elle devrait être écrite pour utiliser un nombre fixé ou un intervalle de ports et utiliser uniquement l’option « SO_REUSEPORT » sur les sockets qui utilisent ces ports.
Note en relation : Beaucoup d’implémentation BSD des sockets supportent aussi l’option « SO_DONTLINGER ». Cette option a exactement le sens opposé de l’option « SO_LINGER » et les deux sont équivalentes (après inversion de la valeur de linger->l_onoff). En d’autres mots, « SO_LINGER » avec un 0 dans « linger->l_onoff » est la même chose que « SO_DONTLINGER » avec autre chose que 0 dans « linger->l_onoff » et vice versa.