Bienvenue dans le monde merveilleux de la portabilité... ou plutôt de son absence. Avant de commencer à analyser ces deux options en détail et d'examiner plus en détail comment différents systèmes d'exploitation les gèrent, il convient de noter que l'implémentation de socket BSD est la mère de toutes les implémentations de socket. Fondamentalement, tous les autres systèmes ont copié l'implémentation du socket BSD à un moment donné (ou du moins ses interfaces) et ont ensuite commencé à la faire évoluer par eux-mêmes. Bien sûr, l'implémentation du socket BSD a également évolué en même temps et donc les systèmes qui l'ont copié plus tard ont obtenu des fonctionnalités qui manquaient aux systèmes qui l'ont copié plus tôt. Comprendre l'implémentation de socket BSD est la clé pour comprendre toutes les autres implémentations de socket, vous devriez donc lire à ce sujet même si vous ne vous souciez pas d'écrire du code pour un système BSD.
Il y a quelques notions de base que vous devez connaître avant d'examiner ces deux options. Une connexion TCP/UDP est identifiée par un tuple de cinq valeurs :
{<protocol>, <src addr>, <src port>, <dest addr>, <dest port>}
Toute combinaison unique de ces valeurs identifie une connexion. Par conséquent, deux connexions ne peuvent pas avoir les cinq mêmes valeurs, sinon le système ne serait plus en mesure de distinguer ces connexions.
Le protocole d'un socket est défini lors de la création d'un socket avec le socket()
fonction. L'adresse source et le port sont définis avec le bind()
fonction. L'adresse et le port de destination sont définis avec le connect()
fonction. Comme UDP est un protocole sans connexion, les sockets UDP peuvent être utilisés sans les connecter. Pourtant, il est permis de les connecter et dans certains cas très avantageux pour votre code et la conception générale de l'application. En mode sans connexion, les sockets UDP qui n'étaient pas explicitement liés lorsque des données leur sont envoyées pour la première fois sont généralement automatiquement liés par le système, car un socket UDP non lié ne peut recevoir aucune donnée (de réponse). Il en va de même pour un socket TCP non lié, il est automatiquement lié avant d'être connecté.
Si vous liez explicitement un socket, il est possible de le lier au port 0
, ce qui signifie "n'importe quel port". Puisqu'un socket ne peut pas vraiment être lié à tous les ports existants, le système devra choisir lui-même un port spécifique dans ce cas (généralement à partir d'une plage de ports source prédéfinie et spécifique au système d'exploitation). Un caractère générique similaire existe pour l'adresse source, qui peut être "n'importe quelle adresse" (0.0.0.0
en cas d'IPv4 et ::
en cas d'IPv6). Contrairement au cas des ports, un socket peut vraiment être lié à "n'importe quelle adresse", ce qui signifie "toutes les adresses IP source de toutes les interfaces locales". Si le socket est connecté ultérieurement, le système doit choisir une adresse IP source spécifique, car un socket ne peut pas être connecté et en même temps être lié à une adresse IP locale. En fonction de l'adresse de destination et du contenu de la table de routage, le système choisira une adresse source appropriée et remplacera la liaison "any" par une liaison à l'adresse IP source choisie.
Par défaut, deux sockets ne peuvent pas être liés à la même combinaison d'adresse source et de port source. Tant que le port source est différent, l'adresse source n'est en fait pas pertinente. Liaison socketA
à ipA:portA
et socketB
à ipB:portB
est toujours possible si ipA != ipB
est vrai, même lorsque portA == portB
. Par exemple. socketA
appartient à un programme de serveur FTP et est lié à 192.168.0.1:21
et socketB
appartient à un autre programme de serveur FTP et est lié à 10.0.0.1:21
, les deux liaisons réussiront. Gardez à l'esprit, cependant, qu'un socket peut être lié localement à "n'importe quelle adresse". Si un socket est lié à 0.0.0.0:21
, il est lié à toutes les adresses locales existantes en même temps et dans ce cas aucun autre socket ne peut être lié au port 21
, quelle que soit l'adresse IP spécifique à laquelle il essaie de se lier, comme 0.0.0.0
est en conflit avec toutes les adresses IP locales existantes.
Tout ce qui a été dit jusqu'à présent est à peu près égal pour tous les principaux systèmes d'exploitation. Les choses commencent à devenir spécifiques au système d'exploitation lorsque la réutilisation des adresses entre en jeu. Nous commençons avec BSD, car comme je l'ai dit plus haut, c'est la mère de toutes les implémentations de socket.
BSD
SO_REUSEADDR
Si SO_REUSEADDR
est activé sur un socket avant de le lier, le socket peut être lié avec succès à moins qu'il n'y ait un conflit avec un autre socket lié à exactement la même combinaison d'adresse source et de port. Maintenant, vous vous demandez peut-être en quoi cela est-il différent d'avant ? Le mot-clé est "exactement". SO_REUSEADDR
modifie principalement la façon dont les adresses génériques ("toute adresse IP") sont traitées lors de la recherche de conflits.
Sans SO_REUSEADDR
, contraignant socketA
à 0.0.0.0:21
puis la liaison socketB
à 192.168.0.1:21
échouera (avec l'erreur EADDRINUSE
), puisque 0.0.0.0 signifie "toute adresse IP locale", ainsi toutes les adresses IP locales sont considérées comme utilisées par ce socket et cela inclut 192.168.0.1
, aussi. Avec SO_REUSEADDR
il réussira, puisque 0.0.0.0
et 192.168.0.1
ne sont pas exactement la même adresse, l'un est un joker pour toutes les adresses locales et l'autre est une adresse locale très spécifique. Notez que la déclaration ci-dessus est vraie quel que soit l'ordre dans lequel socketA
et socketB
sont liés; sans SO_REUSEADDR
il échouera toujours, avec SO_REUSEADDR
ça réussira toujours.
Pour vous donner un meilleur aperçu, faisons ici un tableau et listons toutes les combinaisons possibles :
SO_REUSEADDR socketA socketB Result --------------------------------------------------------------------- ON/OFF 192.168.0.1:21 192.168.0.1:21 Error (EADDRINUSE) ON/OFF 192.168.0.1:21 10.0.0.1:21 OK ON/OFF 10.0.0.1:21 192.168.0.1:21 OK OFF 0.0.0.0:21 192.168.1.0:21 Error (EADDRINUSE) OFF 192.168.1.0:21 0.0.0.0:21 Error (EADDRINUSE) ON 0.0.0.0:21 192.168.1.0:21 OK ON 192.168.1.0:21 0.0.0.0:21 OK ON/OFF 0.0.0.0:21 0.0.0.0:21 Error (EADDRINUSE)
Le tableau ci-dessus suppose que socketA
a déjà été lié avec succès à l'adresse donnée pour socketA
, puis socketB
est créé, soit obtient SO_REUSEADDR
défini ou non, et enfin est lié à l'adresse donnée pour socketB
. Result
est le résultat de l'opération de liaison pour socketB
. Si la première colonne indique ON/OFF
, la valeur de SO_REUSEADDR
n'est pas pertinent pour le résultat.
D'accord, SO_REUSEADDR
a un effet sur les adresses génériques, bon à savoir. Pourtant, ce n'est pas son seul effet. Il y a un autre effet bien connu qui est aussi la raison pour laquelle la plupart des gens utilisent SO_REUSEADDR
dans les programmes serveur en premier lieu. Pour l'autre utilisation importante de cette option, nous devons approfondir le fonctionnement du protocole TCP.
Si un socket TCP est en cours de fermeture, une poignée de main à trois voies est normalement effectuée ; la séquence s'appelle FIN-ACK
. Le problème ici est que le dernier ACK de cette séquence peut être arrivé de l'autre côté ou ne pas être arrivé et seulement si c'est le cas, l'autre côté considère également la prise comme étant complètement fermée. Pour éviter de réutiliser une combinaison adresse + port, qui peut encore être considérée comme ouverte par un pair distant, le système ne considérera pas immédiatement une socket comme morte après l'envoi du dernier ACK
mais placez plutôt le socket dans un état communément appelé TIME_WAIT
. Il peut être dans cet état pendant quelques minutes (paramètre dépendant du système). Sur la plupart des systèmes, vous pouvez contourner cet état en activant la persistance et en définissant un temps de persistance de zéro1 mais il n'y a aucune garantie que cela soit toujours possible, que le système honorera toujours cette demande, et même si le système l'honore, cela provoque le socket à fermer par un reset (RST
), ce qui n'est pas toujours une bonne idée. Pour en savoir plus sur le temps d'attente, jetez un œil à ma réponse à ce sujet.
La question est de savoir comment le système traite un socket dans l'état TIME_WAIT
? Si SO_REUSEADDR
n'est pas défini, un socket dans l'état TIME_WAIT
est considéré comme étant toujours lié à l'adresse et au port source et toute tentative de liaison d'un nouveau socket à la même adresse et au même port échouera jusqu'à ce que le socket soit réellement fermé. Ne vous attendez donc pas à pouvoir relier l'adresse source d'un socket immédiatement après sa fermeture. Dans la plupart des cas, cela échouera. Cependant, si SO_REUSEADDR
est défini pour le socket que vous essayez de lier, un autre socket lié à la même adresse et au même port dans l'état TIME_WAIT
est simplement ignoré, après tout, il est déjà "à moitié mort", et votre socket peut se lier exactement à la même adresse sans aucun problème. Dans ce cas, le fait que l'autre socket ait exactement la même adresse et le même port ne joue aucun rôle. Notez que la liaison d'un socket à exactement la même adresse et le même port qu'un socket mourant dans TIME_WAIT
l'état peut avoir des effets secondaires inattendus, et généralement indésirables, au cas où l'autre socket est toujours "au travail", mais cela dépasse le cadre de cette réponse et heureusement, ces effets secondaires sont plutôt rares dans la pratique.
Il y a une dernière chose que vous devez savoir sur SO_REUSEADDR
. Tout ce qui est écrit ci-dessus fonctionnera tant que la socket à laquelle vous souhaitez vous lier a la réutilisation d'adresse activée. Il n'est pas nécessaire que l'autre socket, celle qui est déjà liée ou qui est dans un TIME_WAIT
state, avait également cet indicateur défini lorsqu'il était lié. Le code qui décide si la liaison réussira ou échouera inspecte uniquement le SO_REUSEADDR
drapeau de la prise introduite dans le bind()
call, pour toutes les autres sockets inspectées, cet indicateur n'est même pas examiné.
SO_REUSEPORT
SO_REUSEPORT
est ce à quoi la plupart des gens s'attendraient SO_REUSEADDR
être. En gros, SO_REUSEPORT
vous permet de lier un nombre arbitraire de sockets à exactement la même adresse source et le même port tant que tous les sockets liés précédents avaient également SO_REUSEPORT
mis avant qu'ils ne soient liés. Si le premier socket lié à une adresse et un port n'a pas SO_REUSEPORT
défini, aucune autre socket ne peut être liée exactement à la même adresse et au même port, que cette autre socket ait ou non SO_REUSEPORT
défini ou non, jusqu'à ce que le premier socket libère à nouveau sa liaison. Contrairement au cas de SO_REUESADDR
le code gérant SO_REUSEPORT
vérifiera non seulement que le socket actuellement lié a SO_REUSEPORT
mais il vérifiera également que le socket avec une adresse et un port en conflit avait SO_REUSEPORT
défini quand il a été lié.
SO_REUSEPORT
n'implique pas SO_REUSEADDR
. Cela signifie que si un socket n'avait pas SO_REUSEPORT
défini quand il était lié et qu'un autre socket a SO_REUSEPORT
défini lorsqu'il est lié exactement à la même adresse et au même port, la liaison échoue, ce qui est attendu, mais elle échoue également si l'autre socket est déjà en train de mourir et est en TIME_WAIT
Etat. Pour pouvoir lier un socket aux mêmes adresses et port qu'un autre socket en TIME_WAIT
l'état nécessite soit SO_REUSEADDR
à définir sur ce socket ou SO_REUSEPORT
doit avoir été défini sur les deux prises avant de les relier. Bien sûr, il est permis de définir les deux, SO_REUSEPORT
et SO_REUSEADDR
, sur une prise.
Il n'y a pas grand-chose de plus à dire sur SO_REUSEPORT
à part cela, il a été ajouté après SO_REUSEADDR
, c'est pourquoi vous ne le trouverez pas dans de nombreuses implémentations de sockets d'autres systèmes, qui "forkaient" le code BSD avant l'ajout de cette option, et qu'il n'y avait aucun moyen de lier deux sockets à exactement la même adresse de socket dans BSD avant cela option.
Connect() renvoie EADDRINUSE ?
La plupart des gens savent que bind()
peut échouer avec l'erreur EADDRINUSE
, cependant, lorsque vous commencez à jouer avec la réutilisation d'adresses, vous pouvez vous retrouver dans la situation étrange où connect()
échoue également avec cette erreur. Comment se peut-il? Comment une adresse distante, après tout ce que connect ajoute à une socket, peut-elle déjà être utilisée ? Connecter plusieurs sockets à exactement la même adresse distante n'a jamais été un problème auparavant, alors qu'est-ce qui ne va pas ici ?
Comme je l'ai dit tout en haut de ma réponse, une connexion est définie par un tuple de cinq valeurs, vous vous souvenez ? Et j'ai aussi dit que ces cinq valeurs doivent être uniques sinon le système ne peut plus distinguer deux connexions, n'est-ce pas ? Eh bien, avec la réutilisation des adresses, vous pouvez lier deux sockets du même protocole à la même adresse source et au même port. Cela signifie que trois de ces cinq valeurs sont déjà les mêmes pour ces deux sockets. Si vous essayez maintenant de connecter ces deux sockets également à la même adresse de destination et au même port, vous créerez deux sockets connectés, dont les tuples sont absolument identiques. Cela ne peut pas fonctionner, du moins pas pour les connexions TCP (les connexions UDP ne sont de toute façon pas de vraies connexions). Si des données arrivaient pour l'une ou l'autre des deux connexions, le système ne pouvait pas dire à quelle connexion les données appartiennent. Au moins l'adresse de destination ou le port de destination doit être différent pour l'une ou l'autre des connexions, afin que le système n'ait aucun problème à identifier à quelle connexion appartiennent les données entrantes.
Donc, si vous liez deux sockets du même protocole à la même adresse source et au même port et essayez de les connecter tous les deux à la même adresse et au même port de destination, connect()
échouera en fait avec l'erreur EADDRINUSE
pour la deuxième socket que vous essayez de connecter, ce qui signifie qu'une socket avec un tuple identique de cinq valeurs est déjà connectée.
Adresses multidiffusion
La plupart des gens ignorent le fait que les adresses de multidiffusion existent, mais elles existent. Alors que les adresses unicast sont utilisées pour la communication un à un, les adresses multicast sont utilisées pour la communication un à plusieurs. La plupart des gens ont pris connaissance des adresses de multidiffusion lorsqu'ils ont entendu parler d'IPv6, mais les adresses de multidiffusion existaient également dans IPv4, même si cette fonctionnalité n'a jamais été largement utilisée sur l'Internet public.
La signification de SO_REUSEADDR
change pour les adresses de multidiffusion car il permet à plusieurs sockets d'être liés exactement à la même combinaison d'adresse et de port de multidiffusion source. En d'autres termes, pour les adresses multicast SO_REUSEADDR
se comporte exactement comme SO_REUSEPORT
pour les adresses unicast. En fait, le code traite SO_REUSEADDR
et SO_REUSEPORT
identique pour les adresses multicast, cela signifie que vous pourriez dire que SO_REUSEADDR
implique SO_REUSEPORT
pour toutes les adresses multicast et inversement.
FreeBSD/OpenBSD/NetBSD
Ce sont tous des forks plutôt tardifs du code BSD d'origine, c'est pourquoi ils offrent tous les trois les mêmes options que BSD et ils se comportent également de la même manière que dans BSD.
macOS (MacOS X)
À la base, macOS est simplement un UNIX de style BSD nommé "Darwin ", basé sur un fork plutôt tardif du code BSD (BSD 4.3), qui a ensuite été resynchronisé avec la base de code FreeBSD 5 (actuelle à l'époque) pour la version Mac OS 10.3, afin qu'Apple puisse gagner conformité POSIX complète (macOS est certifié POSIX). Bien qu'il ait un micro-noyau en son cœur ("Mach "), le reste du noyau ("XNU ") est fondamentalement juste un noyau BSD, et c'est pourquoi macOS offre les mêmes options que BSD et ils se comportent également de la même manière que dans BSD.
iOS/watchOS/tvOS
iOS n'est qu'un fork macOS avec un noyau légèrement modifié et réduit, un ensemble d'outils d'espace utilisateur quelque peu dépouillé et un ensemble de framework par défaut légèrement différent. watchOS et tvOS sont des fourches iOS, qui sont encore plus réduites (en particulier watchOS). À ma connaissance, ils se comportent tous exactement comme macOS.
Linux
Linux <3.9
Avant Linux 3.9, seule l'option SO_REUSEADDR
existait. Cette option se comporte généralement de la même manière que dans BSD avec deux exceptions importantes :
-
Tant qu'un socket TCP d'écoute (serveur) est lié à un port spécifique, le
SO_REUSEADDR
L'option est entièrement ignorée pour tous les sockets ciblant ce port. Lier un deuxième socket au même port n'est possible que si c'était également possible dans BSD sans avoirSO_REUSEADDR
Positionner. Par exemple. vous ne pouvez pas vous lier à une adresse générique puis à une adresse plus spécifique ou inversement, les deux sont possibles dans BSD si vous définissezSO_REUSEADDR
. Ce que vous pouvez faire, c'est que vous pouvez vous lier au même port et à deux adresses différentes non génériques, car cela est toujours autorisé. Sous cet aspect, Linux est plus restrictif que BSD. -
La deuxième exception est que pour les sockets client, cette option se comporte exactement comme
SO_REUSEPORT
dans BSD, tant que les deux avaient cet indicateur défini avant d'être liés. La raison de cette autorisation était simplement qu'il est important de pouvoir lier plusieurs sockets exactement à la même adresse de socket UDP pour différents protocoles et qu'il n'y avait pas deSO_REUSEPORT
avant 3.9, le comportement deSO_REUSEADDR
a été modifié en conséquence pour combler cette lacune. Sous cet aspect, Linux est moins restrictif que BSD.
Linux>=3.9
Linux 3.9 a ajouté l'option SO_REUSEPORT
à Linux aussi. Cette option se comporte exactement comme l'option dans BSD et permet de se lier exactement à la même adresse et au même numéro de port tant que toutes les sockets ont cette option définie avant de les lier.
Pourtant, il y a encore deux différences avec SO_REUSEPORT
sur d'autres systèmes :
-
Pour empêcher le "détournement de port", il existe une limitation spéciale :Tous les sockets qui souhaitent partager la même combinaison d'adresse et de port doivent appartenir à des processus qui partagent le même ID utilisateur effectif ! Ainsi, un utilisateur ne peut pas "voler" les ports d'un autre utilisateur. C'est une magie spéciale pour compenser quelque peu le
SO_EXCLBIND
manquant /SO_EXCLUSIVEADDRUSE
drapeaux. -
De plus, le noyau effectue une "magie spéciale" pour
SO_REUSEPORT
sockets introuvables dans d'autres systèmes d'exploitation :pour les sockets UDP, il essaie de distribuer les datagrammes de manière égale, pour les sockets d'écoute TCP, il essaie de distribuer les demandes de connexion entrantes (celles acceptées en appelantaccept()
) uniformément sur tous les sockets partageant la même combinaison d'adresse et de port. Ainsi, une application peut facilement ouvrir le même port dans plusieurs processus enfants, puis utiliserSO_REUSEPORT
pour obtenir un équilibrage de charge très peu coûteux.
Android
Même si l'ensemble du système Android est quelque peu différent de la plupart des distributions Linux, son cœur fonctionne avec un noyau Linux légèrement modifié, donc tout ce qui s'applique à Linux devrait également s'appliquer à Android.
Windows
Windows ne connaît que le SO_REUSEADDR
option, il n'y a pas de SO_REUSEPORT
. Réglage SO_REUSEADDR
sur un socket dans Windows se comporte comme le réglage SO_REUSEPORT
et SO_REUSEADDR
sur un socket sous BSD, à une exception près :
Avant Windows 2003, un socket avec SO_REUSEADDR
pourrait toujours être lié exactement à la même adresse source et au même port qu'un socket déjà lié, même si l'autre socket n'avait pas cette option définie lorsqu'il était lié . Ce comportement permettait à une application de "voler" le port connecté d'une autre application. Inutile de dire que cela a des implications majeures en matière de sécurité !
Microsoft s'en est rendu compte et a ajouté une autre option de socket importante :SO_EXCLUSIVEADDRUSE
. Réglage SO_EXCLUSIVEADDRUSE
sur un socket s'assure que si la liaison réussit, la combinaison de l'adresse source et du port appartient exclusivement à ce socket et qu'aucun autre socket ne peut se lier à eux, pas même s'il a SO_REUSEADDR
ensemble.
Ce comportement par défaut a été modifié pour la première fois dans Windows 2003, Microsoft l'appelle "Enhanced Socket Security" (nom amusant pour un comportement par défaut sur tous les autres principaux systèmes d'exploitation). Pour plus de détails, visitez simplement cette page. Il y a trois tableaux :le premier montre le comportement classique (toujours utilisé lors de l'utilisation des modes de compatibilité !), le second montre le comportement de Windows 2003 et plus lorsque le bind()
les appels sont effectués par le même utilisateur, et le troisième lorsque le bind()
les appels sont passés par différents utilisateurs.
Solaris
Solaris est le successeur de SunOS. SunOS était à l'origine basé sur un fork de BSD, SunOS 5 et plus tard était basé sur un fork de SVR4, cependant SVR4 est une fusion de BSD, System V et Xenix, donc jusqu'à un certain degré Solaris est également un fork BSD, et un plutôt précoce. Par conséquent, Solaris ne connaît que SO_REUSEADDR
, il n'y a pas de SO_REUSEPORT
. Le SO_REUSEADDR
se comporte à peu près de la même manière que dans BSD. Autant que je sache, il n'y a aucun moyen d'obtenir le même comportement que SO_REUSEPORT
sous Solaris, cela signifie qu'il n'est pas possible de lier deux sockets à exactement la même adresse et le même port.
Semblable à Windows, Solaris a une option pour donner à un socket une liaison exclusive. Cette option est nommée SO_EXCLBIND
. Si cette option est définie sur un socket avant de le lier, définissez SO_REUSEADDR
sur un autre socket n'a aucun effet si les deux sockets sont testés pour un conflit d'adresse. Par exemple. si socketA
est lié à une adresse générique et socketB
a SO_REUSEADDR
activé et est lié à une adresse non générique et au même port que socketA
, cette liaison réussira normalement, sauf si socketA
avait SO_EXCLBIND
activé, auquel cas il échouera quel que soit le SO_REUSEADDR
drapeau de socketB
.
Autres systèmes
Au cas où votre système ne figurerait pas dans la liste ci-dessus, j'ai écrit un petit programme de test que vous pouvez utiliser pour savoir comment votre système gère ces deux options. Aussi, si vous pensez que mes résultats sont erronés , veuillez d'abord exécuter ce programme avant de publier des commentaires et éventuellement de faire de fausses déclarations.
Tout ce dont le code a besoin pour construire est un peu d'API POSIX (pour les parties réseau) et un compilateur C99 (en fait, la plupart des compilateurs non-C99 fonctionneront aussi bien tant qu'ils offrent inttypes.h
et stdbool.h
; par exemple. gcc
pris en charge les deux bien avant d'offrir une prise en charge complète de C99).
Tout ce dont le programme a besoin pour s'exécuter est qu'au moins une interface de votre système (autre que l'interface locale) ait une adresse IP attribuée et qu'une route par défaut soit définie qui utilise cette interface. Le programme recueillera cette adresse IP et l'utilisera comme deuxième "adresse spécifique".
Il teste toutes les combinaisons possibles auxquelles vous pouvez penser :
- Protocole TCP et UDP
- Prises normales, prises d'écoute (serveur), prises multidiffusion
SO_REUSEADDR
défini sur socket1, socket2 ou les deux socketsSO_REUSEPORT
défini sur socket1, socket2 ou les deux sockets- Toutes les combinaisons d'adresses que vous pouvez créer à partir de
0.0.0.0
(caractère générique),127.0.0.1
(adresse spécifique) et la deuxième adresse spécifique trouvée sur votre interface principale (pour la multidiffusion, c'est juste224.1.2.3
dans tous les tests)
et imprime les résultats dans un joli tableau. Cela fonctionnera également sur les systèmes qui ne connaissent pas SO_REUSEPORT
, auquel cas cette option n'est tout simplement pas testée.
Ce que le programme ne peut pas facilement tester, c'est comment SO_REUSEADDR
agit sur les sockets en TIME_WAIT
état car il est très difficile de forcer et de maintenir une socket dans cet état. Heureusement, la plupart des systèmes d'exploitation semblent simplement se comporter ici comme BSD et la plupart du temps, les programmeurs peuvent simplement ignorer l'existence de cet état.
Voici le code (je ne peux pas l'inclure ici, les réponses ont une taille limite et le code pousserait cette réponse au-delà de la limite).
La réponse de Mecki est absolument parfaite, mais il convient d'ajouter que FreeBSD prend également en charge SO_REUSEPORT_LB
, qui imite le SO_REUSEPORT
de Linux comportement - il équilibre la charge; voir setsockopt(2)