Heim  >  Artikel  >  Backend-Entwicklung  >  Besprechen Sie im Detail den Unterschied zwischen so_reuseport und so_reuseaddr in Sockets

Besprechen Sie im Detail den Unterschied zwischen so_reuseport und so_reuseaddr in Sockets

不言
不言Original
2018-04-28 15:15:161676Durchsuche

Der folgende Artikel wird Ihnen eine ausführliche Diskussion über den Unterschied zwischen so_reuseport und so_reuseaddr in Sockets geben. Er hat einen guten Referenzwert und ich hoffe, dass er für alle hilfreich sein wird. Werfen wir gemeinsam einen Blick darauf

Grundlegender Hintergrund von Socket

Wenn wir den Unterschied zwischen diesen beiden Optionen diskutieren, müssen wir die BSD kennen Implementierung Es ist der Ursprung aller Socket-Implementierungen. Grundsätzlich referenzierten alle anderen Systeme bis zu einem gewissen Grad auf die BSD-Socket-Implementierung (oder zumindest deren Schnittstelle) und begannen dann ihre eigene unabhängige Entwicklung. Offensichtlich entwickelt sich BSD selbst im Laufe der Zeit ständig weiter und verändert sich. Daher verfügen Systeme, die später auf BSD verweisen, über mehr Funktionen als Systeme, die früher auf BSD verweisen. Daher ist das Verständnis der BSD-Socket-Implementierung der Grundstein für das Verständnis anderer Socket-Implementierungen. Lassen Sie uns die BSD-Socket-Implementierung analysieren.

Zuvor müssen wir zunächst verstehen, wie man eine TCP/UDP-Verbindung eindeutig identifiziert. TCP/UDP wird durch das folgende Fünf-Tupel eindeutig identifiziert:


{<protocol>, <src addr>, <src port>, <dest addr>, <dest port>}


Jede eindeutige Kombination dieser Werte kann eindeutig identifiziert werden Bestätigen Sie eine Verbindung. Dann können diese fünf Werte für keine Verbindung genau gleich sein. Andernfalls wäre das Betriebssystem nicht in der Lage, zwischen diesen Verbindungen zu unterscheiden.

Das Protokoll eines Sockets wird bei der Initialisierung mit socket() festgelegt. Die Quelladresse und der Quellport werden beim Aufruf von bind() festgelegt. Die Zieladresse und der Zielport werden beim Aufruf von connect() festgelegt. UDP ist verbindungslos und der UDP-Socket kann verwendet werden, ohne mit dem Zielport verbunden zu sein. Allerdings kann in manchen Fällen auch UDP genutzt werden, nachdem eine Verbindung mit der Zieladresse und dem Port hergestellt wurde. Wenn Sie verbindungsloses UDP zum Senden von Daten verwenden und bind () nicht explizit aufgerufen wird, bindet das System beim ersten Senden von Daten automatisch den UDP-Socket an die lokale Adresse und einen bestimmten Port (andernfalls). Das Programm kann keine von ihm beantworteten Daten akzeptieren des Remote-Hosts). Ebenso wird ein TCP-Socket ohne gebundene Adresse automatisch an eine lokale Adresse und einen lokalen Port gebunden, wenn die Verbindung hergestellt wird.

Wenn wir einen Port manuell binden, können wir den Socket an Port 0 binden. Die Bindung an Port 0 bedeutet, dass das System entscheiden muss, welcher Port verwendet werden soll (normalerweise anhand einer Reihe betriebssystemspezifischer Fortschritte innerhalb des festgelegten Ports). Nummernkreis), es handelt sich also um einen beliebigen Port. Ebenso können wir auch einen Platzhalter verwenden, um das System entscheiden zu lassen, welche Quelladresse gebunden werden soll (der IPv4-Platzhalter ist 0.0.0.0, der IPv6-Platzhalter ist::). Im Gegensatz zu einem Port kann ein Socket an jede Adresse gebunden werden, die allen Schnittstellen auf dem Host entspricht. Basierend auf der mit diesem Socket verbundenen Zieladresse und den entsprechenden Informationen in der Routing-Tabelle wählt das Betriebssystem die entsprechende Adresse zum Binden dieses Sockets aus und verwendet diese Adresse, um die vorherige Wildcard-IP-Adresse zu ersetzen.

Standardmäßig können zwei beliebige Sockets nicht an dieselbe Kombination aus Quelladresse und Quellport gebunden werden. Beispielsweise binden wir SocketA an die Adresse A:X und SocketB an die Adresse B:Y, wobei A und B IP-Adressen und X und Y Ports sind. Dann muss X!=Y erfüllt sein, wenn A==B, und A!=B muss erfüllt sein, wenn X==Y. Es ist zu beachten, dass, wenn ein bestimmter Socket an eine Wildcard-IP-Adresse gebunden ist, tatsächlich alle IPs des lokalen Computers vom System als daran gebunden betrachtet werden. Beispielsweise ist ein Socket an 0.0.0.0:21 gebunden. In diesem Fall kann kein anderer Socket, unabhängig von der spezifischen IP-Adresse, an Port 21 gebunden werden. Weil der Platzhalter IP0.0.0.0 mit allen lokalen IPs in Konflikt steht.

Alle oben genannten Punkte sind grundsätzlich auf allen gängigen Betriebssystemen gleich. Jedes SO_REUSEADDR hat unterschiedliche Bedeutungen. Lassen Sie uns zunächst die BSD-Implementierung besprechen. Weil BSD die Quelle aller anderen Socket-Implementierungsmethoden ist.

BSD

SO_REUSEADDR

Wenn an einen Socket gebunden Das Attribut SO_REUSEADDR wird festgelegt, bevor eine bestimmte Adresse und ein bestimmter Port erreicht werden. Wenn der Socket nicht mit einem anderen Socket in Konflikt steht, der versucht, an genau dieselbe Kombination aus Quelladresse und Quellport zu binden, kann der Socket erfolgreich gebunden werden Definieren Sie dieses Adress-Port-Paar. Das hört sich vielleicht genauso an wie zuvor. Aber das Schlüsselwort dort ist vollständig. SO_REUSEADDR ändert hauptsächlich die Art und Weise, wie das System Wildcard-IP-Adresskonflikte behandelt.

Wenn SO_REUSEADDR nicht verwendet wird und wir SocketA an 0.0.0.0:21 binden, führt jeder Versuch, andere Sockets auf diesem Computer an Port 21 zu binden (z. B. Bindung an 192.168.1.1:21), zu EADDRINUSE Fehler. Da 0.0.0.0 eine Wildcard-IP-Adresse ist, d. h. jede IP-Adresse, gilt jede andere IP-Adresse auf diesem Computer als vom System belegt. Wenn die Option SO_REUSEADDR gesetzt ist, weil 0.0.0.0:21 und 192.168.1.1:21 nicht genau dasselbe Adress-Port-Paar sind (eines davon ist eine Wildcard-IP-Adresse und das andere eine spezifische IP-Adresse des lokalen Computers), Eine solche Bindung kann auf jeden Fall erfolgreich sein. Es ist zu beachten, dass die Bindung unabhängig von der Reihenfolge, in der SocketA und SocketB initialisiert werden, erfolgreich ist, solange SO_REUSEADDR festgelegt ist. Solange SO_REUSEADDR nicht festgelegt ist, ist die Bindung nicht erfolgreich.

In der folgenden Tabelle sind einige mögliche Szenarien und deren Folgen aufgeführt.


SO_REUSEADDR socketA socketB Result
ON / OFF 192.168.1.1:21 192.168.1.1:21 ERROR(EADDRINUSE)
ON / OFF 192.168.1.1:21 10.0.1.1:21 OK
ON / OFF 10.0.1.1:21 192.168.1.1:21 OK
OFF 192.168.1.1:21 0.0.0.0:21 ERROR(EADDRINUSE)
OFF 0.0.0.0:21 192.168.1.1:21 ERROR(EADDRINUSE)
ON 192.168.1.1:21 0.0.0.0:21 OK
ON 0.0.0.0:21 192.168.1.1:21 OK
ON / OFF 0.0.0.0:21 0.0.0.0:21 OK

Diese Tabelle geht davon aus, dass SocketA erfolgreich an die entsprechende Adresse in der Tabelle gebunden wurde, dann wird SocketB initialisiert und seine SO_REUSEADDR-Einstellung ist wie in der ersten Spalte der Tabelle gezeigt, und dann versucht SocketB, die entsprechende Adresse in zu binden Tisch. Die Ergebnisspalte ist das Ergebnis ihrer Bindung. Wenn der Wert in der ersten Spalte ON/OFF ist, hat die Frage, ob SO_REUSEADDR gesetzt ist oder nicht, nichts mit dem Ergebnis zu tun.

Die Rolle von SO_REUSEADDR für Wildcard-IP-Adressen wurde oben besprochen, aber es hat nicht nur diese Rolle. Eine weitere Rolle besteht darin, warum jeder bei der serverseitigen Programmierung die Option SO_REUSEADDR verwendet. Um seine weitere Rolle und seine wichtigen Anwendungen zu verstehen, müssen wir zunächst eingehender auf die Funktionsweise des TCP-Protokolls eingehen.

Jeder Socket verfügt über seinen entsprechenden Sendepuffer. Wenn die send()-Methode erfolgreich aufgerufen wird, werden die zum Senden erforderlichen Daten nicht unbedingt sofort gesendet, sondern dem Sendepuffer hinzugefügt. Bei UDP-Sockets werden die Daten im Allgemeinen schnell gesendet, auch wenn sie nicht sofort gesendet werden. Bei TCP-Sockets kann es jedoch nach dem Hinzufügen von Daten zum Sendepuffer relativ lange dauern, bis die Daten tatsächlich gesendet werden. Wenn wir also einen TCP-Socket schließen, kann es sein, dass in seinem Sendepuffer tatsächlich noch Daten darauf warten, gesendet zu werden. Da send() jedoch zu diesem Zeitpunkt den Erfolg zurückgibt, geht unser Code davon aus, dass die Daten tatsächlich erfolgreich gesendet wurden. Wenn der TCP-Socket direkt nach dem Aufruf von close() geschlossen wird, gehen alle diese Daten verloren und unser Code erfährt nie etwas davon. Da es sich bei TCP jedoch um ein zuverlässiges Transportschichtprotokoll handelt, ist es offensichtlich nicht ratsam, die zu übertragenden Daten direkt zu verwerfen. Wenn die Methode close() des Sockets aufgerufen wird, während sich noch Daten im Sendepuffer befinden, die gesendet werden müssen, wechselt sie tatsächlich in den sogenannten TIME_WAIT-Zustand. In diesem Zustand versucht der Socket weiterhin, die Daten im Puffer zu senden, bis alle Daten erfolgreich gesendet wurden oder bis eine Zeitüberschreitung auftritt. Der Socket wird zwangsweise geschlossen.

Die maximale Wartezeit des Betriebssystemkerns vor dem gewaltsamen Schließen eines Sockets wird als Verweilzeit bezeichnet. Die Verzögerungszeit ist auf den meisten Systemen global eingestellt und relativ lang (die meisten Systeme stellen sie auf 2 Minuten ein). Wir können beim Initialisieren eines Sockets auch die Option SO_LINGER verwenden, um die Verzögerungszeit für jeden Socket gezielt festzulegen. Wir können das verzögerte Warten sogar vollständig deaktivieren. Es ist jedoch zu beachten, dass es keine gute Programmierpraxis ist, die Verzögerungszeit auf 0 zu setzen (das Warten auf die Verzögerung vollständig auszuschalten). Da das ordnungsgemäße Schließen eines TCP-Sockets ein relativ komplexer Prozess ist, der den Austausch mehrerer Datenpakete mit dem Remote-Host umfasst (einschließlich verlustfreier Neuübertragung im Falle eines Paketverlusts), ist die für diesen Datenpaketaustauschvorgang erforderliche Zeit ebenfalls in der Verzögerungszeit enthalten . Wenn wir das verzögerte Warten deaktivieren, verwirft der Socket nicht nur alle zu sendenden Daten, wenn er geschlossen wird, sondern wird auch immer zwangsweise geschlossen (da TCP ein verbindungsorientiertes Protokoll ist, führt das Nichtaustauschen schließender Pakete mit dem Remote-Port dazu, dass das Remote-Port befindet sich in einem langen Wartezustand). Daher empfehlen wir normalerweise nicht, dies in der tatsächlichen Programmierung zu tun. Der Prozess der TCP-Trennung geht über den Rahmen dieses Artikels hinaus. Wenn Sie interessiert sind, können Sie auf diese Seite verweisen. Und tatsächlich: Wenn wir das verzögerte Warten deaktivieren und unser Programm beendet wird, ohne den Socket explizit zu schließen, ignoriert BSD (und möglicherweise auch andere Systeme) unsere Einstellung und führt ein verzögertes Warten durch. Zum Beispiel, wenn unser Programm die Methode „exit()“ aufruft oder sein Prozess durch ein Signal beendet wird (einschließlich eines Prozessabsturzes aufgrund eines illegalen Speicherzugriffs und dergleichen). Daher können wir nicht zu 100 % garantieren, dass ein Socket unabhängig von der Verzögerungswartezeit unter allen Umständen beendet wird.

Das Problem hierbei ist, wie das Betriebssystem Sockets in der TIME_WAIT-Phase behandelt. Wenn die Option SO_REUSEADDR nicht festgelegt ist, wird der Socket in der TIME_WAIT-Phase weiterhin als an die ursprüngliche Adresse und den ursprünglichen Port gebunden betrachtet. Bis der Socket vollständig geschlossen ist (die TIME_WAIT-Phase endet), ist jeder andere Versuch, einen neuen Socket an dieses Paar aus Adresse und Port zu binden, erfolglos. Diese Wartezeit kann genauso lang sein wie die verzögerte Wartezeit. Daher können wir einen neuen Socket nicht sofort an das Adress- und Portpaar binden, das dem gerade geschlossenen Socket entspricht. Dieser Vorgang schlägt in den meisten Fällen fehl.

Wenn wir jedoch die Option SO_REUSEADDR für einen neuen Socket festlegen und ein anderer Socket an das aktuelle Adress-Port-Paar gebunden ist und sich in der TIME_WAIT-Phase befindet, wird diese bestehende Bindungsbeziehung ignoriert. Tatsächlich ist der Socket in der TIME_WAIT-Stufe bereits halb geschlossen, und es wird kein Problem sein, einen neuen Socket an dieses Paar aus Adresse und Port zu binden. In diesem Fall hat der an diesen Port gebundene ursprüngliche Socket im Allgemeinen keine Auswirkungen auf den neuen Socket. Es ist jedoch zu beachten, dass das Binden eines neuen Sockets an das Adress-Port-Paar, das einem Socket entspricht, der sich in der TIME_WAIT-Stufe befindet, aber noch funktioniert, irgendwann unbeabsichtigte und unvorhersehbare negative Auswirkungen haben wird. Dieses Problem würde jedoch den Rahmen dieses Artikels sprengen. Und glücklicherweise sind diese negativen Auswirkungen in der Praxis selten zu beobachten.

Abschließend sollten wir noch eine Sache zu SO_REUSEADDR beachten: Alles oben Genannte gilt, solange wir SO_REUSEADDR für den neuen Socket festlegen. Für das Original, das an das aktuelle Paar aus Adresse und Port gebunden wurde, hat es keine Auswirkung, ob der Socket in der TIME_WAIT-Stufe mit SO_REUSEADDR festgelegt ist oder nicht. Der Code, der bestimmt, ob der Bindevorgang erfolgreich ist, überprüft einfach die Option SO_REUSEADDR des neuen Sockets, der an die Methode bind() übergeben wird. Die SO_REUSEADDR-Optionen anderer beteiligter Sockets werden nicht überprüft.

SO_REUSEPORT

Viele Menschen betrachten SO_REUSEADDR als SO_REUSEPORT. Grundsätzlich ermöglicht uns SO_REUSEPORT, eine beliebige Anzahl von Sockets an genau dieselbe Quelladresse und dasselbe Portpaar zu binden, solange für alle zuvor gebundenen Sockets die Option SO_REUSEPORT festgelegt ist. Wenn für den ersten Socket, der an das Paar aus Adresse und Port gebunden ist, SO_REUSEPORT nicht festgelegt ist, kann er nicht an genau dieselbe Adresse wie dieser Adressport gebunden werden, unabhängig davon, ob der nachfolgende Socket SO_REUSEPORT festlegt oder nicht. Es sei denn, der erste Socket, der an dieses Paar aus Adresse und Port gebunden ist, gibt die Bindungsbeziehung frei. Im Gegensatz zu SO_REUSEADDR überprüft der Code, der SO_REUSEPORT verarbeitet, nicht nur den SO_REUSEPORT des Sockets, der gerade eine Bindung versucht, sondern auch die SO_REUSEPORT-Option des Sockets, der zuvor an das Adress-Port-Paar gebunden war, das gerade eine Bindung versucht.

SO_REUSEPORT ist nicht gleich SO_REUSEADDR. Dies bedeutet, dass der Bindungsversuch fehlschlägt, wenn für einen Socket mit einer bereits gebundenen Adresse SO_REUSEPORT nicht festgelegt ist und für einen anderen neuen Socket SO_REUSEPORT festgelegt ist und er versucht, eine Bindung an genau dasselbe Port-Adresspaar wie der aktuelle Socket herzustellen. Wenn sich gleichzeitig der aktuelle Socket bereits in der TIME_WAIT-Phase befindet und der neue Socket mit der Option SO_REUSEPORT versucht, eine Bindung an die aktuelle Adresse herzustellen, schlägt der Bindungsvorgang ebenfalls fehl. Um einen neuen Socket an das Adress-Port-Paar zu binden, das einem Socket entspricht, der sich derzeit in der TIME_WAIT-Phase befindet, müssen wir entweder die Option SO_REUSEADDR des neuen Sockets vor der Bindung festlegen oder wir müssen sie vor der Bindung für beide Sockets festlegen Option. Natürlich ist es auch möglich, die Optionen SO_REUSEADDR und SO_REUSEPORT gleichzeitig für den Socket zu setzen.

SO_REUSEPORT wurde nach SO_REUSEADDR zum BSD-System hinzugefügt. Aus diesem Grund verfügen einige Systeme derzeit nicht über die Option SO_REUSEPORT in ihren Socket-Implementierungen. Weil sie auf die BSD-Socket-Implementierung verwiesen haben, bevor diese Option zum BSD-System hinzugefügt wurde. Bevor diese Option hinzugefügt wurde, gab es unter dem BSD-System keine Möglichkeit, zwei Sockets an genau dieselbe Adresse und dasselbe Portpaar zu binden.

Connect() gibt EADDRINUSE zurück?

Manchmal gibt die bind()-Operation einen EADDRINUSE-Fehler zurück. Aber das Seltsame ist, dass wir beim Aufrufen der connect()-Operation möglicherweise auch einen EADDRINUSE-Fehler erhalten. Warum ist das so? Warum ist eine Remote-Adresse, zu der wir versuchen, eine Verbindung auf dem aktuellen Port herzustellen, ebenfalls belegt? Wird es Probleme geben, wenn mehrere Sockets an dieselbe Remote-Adresse angeschlossen werden?

Wie bereits in diesem Artikel erwähnt, wird eine Verbindungsbeziehung durch ein Fünffach bestimmt. Für jede Verbindungsbeziehung muss dieses Fünffach eindeutig sein. Andernfalls kann das System die beiden Verbindungen nicht unterscheiden. Wenn wir nun die Wiederverwendung von Adressen verwenden, können wir zwei Sockets, die dasselbe Protokoll verwenden, an dasselbe Adress-Port-Paar binden. Das bedeutet, dass für diese beiden Sockets das Fünf-Tupel {a88b79ba1ccee8890e978c768d80530d, 3037a7cee66f0ae45683db6cc2520e8a, 1b9006debff836a11384f3384ae84936} bereits dasselbe ist. Wenn wir in diesem Fall versuchen, beide mit demselben Remote-Adressport zu verbinden, ist das Fünf-Tupel der beiden Verbindungsbeziehungen genau gleich. Das heißt, es entstehen zwei identische Verbindungen. Dies ist im TCP-Protokoll nicht zulässig (UDP ist verbindungslos). Wenn eine dieser beiden identischen Verbindungen Daten empfängt, kann das System nicht erkennen, zu welcher Verbindung die Daten gehören. In diesem Fall können also zumindest die Adressen und Ports der Remote-Hosts, zu denen die beiden Sockets eine Verbindung herstellen möchten, nicht identisch sein. Nur so kann das System weiterhin zwischen den beiden Verbindungsbeziehungen unterscheiden.

Wenn wir also zwei Sockets mit demselben Protokoll an dieselbe lokale Adresse und dasselbe Portpaar binden und versuchen, sie auch mit derselben Zieladresse und demselben Portpaar zu verbinden, versucht der zweite, den Socket der Methode connect() aufzurufen meldet einen EADDRINUSE-Fehler, was bedeutet, dass bereits ein Socket mit genau demselben Fünf-Tupel vorhanden ist.

Multicast-Adresse

Im Gegensatz zur Unicast-Adresse, die für die Eins-zu-eins-Kommunikation verwendet wird, wird die Multicast-Adresse für die Eins-zu-Kommunikation verwendet -viele Kommunikation. Sowohl IPv4 als auch IPv6 verfügen über Multicast-Adressen. Aber Multicast in IPv4 wird in öffentlichen Netzwerken selten verwendet.

Die Bedeutung von SO_REUSEADDR wird im Fall einer Multicast-Adresse eine andere sein als zuvor. In diesem Fall ermöglicht uns SO_REUSEADDR, mehrere Sockets an genau dieselbe Quell-Broadcast-Adresse und dasselbe Port-Paar zu binden. Mit anderen Worten: Für Multicast-Adressen entspricht SO_REUSEADDR SO_REUSEPORT in der Unicast-Kommunikation. Tatsächlich haben SO_REUSEADDR und SO_REUSEPORT im Fall von Multicast genau den gleichen Effekt.

FreeBSD/OpenBSD/NetBSD

Alle diese Systeme beziehen sich auf den neueren nativen BSD-Systemcode. Diese drei Systeme bieten also genau die gleichen Socket-Optionen wie BSD, und die Bedeutung dieser Optionen ist genau die gleiche wie bei nativem BSD.

MacOS Systeme.

iOS

iOS ist eigentlich ein leicht modifiziertes MacOS X, was also für MacOS X funktioniert, funktioniert auch für iOS.

Linux

Vor Linux3.9 gab es nur die Option SO_REUSEADDR. Die Funktion dieser Option ist grundsätzlich dieselbe wie unter BSD-Systemen. Aber es gibt noch zwei wichtige Unterschiede. Der erste Unterschied besteht darin, dass, wenn ein TCP-Socket im Listening-(Server-)Status an eine Wildcard-IP-Adresse und einen bestimmten Port gebunden wurde, unabhängig davon, ob die Option SO_REUSEADDR für diese beiden Sockets gesetzt ist, Nein Andere TCP-Sockets können an denselben Port gebunden werden. Dies funktioniert nicht, selbst wenn der andere Socket eine bestimmte IP-Adresse verwendet (wie in BSD-Systemen zulässig). Für nicht empfangsbereite (Client-)TCP-Sockets gilt diese Einschränkung nicht.

Der zweite Unterschied besteht darin, dass SO_REUSEADDR für UDP-Sockets dieselbe Funktion hat wie SO_REUSEPORT in BSD. Wenn also für zwei UDP-Sockets SO_REUSEADDR festgelegt ist, können sie an genau denselben Satz von Adress- und Portpaaren gebunden werden.

Linux3.9 hat die Option SO_REUSEPORT hinzugefügt. Zwei oder mehr, TCP- oder UDP-abhörende (Server) oder nicht abhörende (Client) Sockets können an genau dieselbe Adresse gebunden werden, sofern für alle Sockets (einschließlich des ersten) diese Option festgelegt ist, bevor die Adresse unter dem Port gebunden wird Kombination. Gleichzeitig gibt es eine besondere Einschränkung, um Port-Hijacking zu verhindern: Alle Sockets, die versuchen, sich an dieselbe Adresse und Port-Kombination zu binden, müssen zum Prozess mit derselben Benutzer-ID gehören. Ein Benutzer kann also einem anderen Benutzer keinen Port „stehlen“.

Darüber hinaus führt der Linux-Kernel für Sockets mit der Option SO_REUSEPORT auch einige spezielle Vorgänge aus, die in anderen Systemen nicht zu finden sind: Für UDP-Sockets, die an dieselbe Kombination aus Adresse und Port gebunden sind, versucht der Kernel „Verteilen“. empfangene Datenpakete gleichmäßig zwischen ihnen verteilen; für TCP-Listening-Sockets, die an dieselbe Adresse und Port-Kombination gebunden sind, versucht der Kernel, empfangene Verbindungsanfragen (Anfragen, die durch Aufruf der Methode „accept()“ erhalten werden) gleichmäßig zu verteilen. Dies bedeutet, dass Linux im Vergleich zu anderen Systemen, die die Wiederverwendung von Adressen ermöglichen, aber empfangene Pakete oder Verbindungsanfragen zufällig an Sockets zuweisen, die mit derselben Adressen- und Portkombination verbunden sind, versucht, die Verkehrsverteilung zu optimieren. Beispielsweise können mehrere unterschiedliche Instanzen eines einfachen Serverprozesses problemlos SO_REUSEPORT verwenden, um einen einfachen Lastausgleich zu implementieren, und der Kernel ist für diesen Lastausgleich verantwortlich, der für das Programm völlig kostenlos ist!

Android

Der Kernbestandteil von Android ist ein leicht modifizierter Linux-Kernel, sodass alles, was für Linux gilt, auch für Android gilt.

Windows

Windows verfügt nur über die Option SO_REUSEADDR. Das Festlegen von SO_REUSEADDR auf einem Socket in Windows hat die gleiche Wirkung wie das gleichzeitige Festlegen von SO_REUSEPORT und SO_REUSEADDR auf einem Socket unter BSD. Der Unterschied besteht jedoch darin: Auch wenn für einen anderen Socket mit einer gebundenen Adresse SO_REUSEADDR nicht festgelegt ist, kann ein Socket mit festgelegtem SO_REUSEADDR immer an genau dieselbe Kombination aus Adresse und Port gebunden werden wie ein anderer gebundener Socket. Dieses Verhalten kann als etwas gefährlich bezeichnet werden. Weil es einer Anwendung ermöglicht, Daten von einer anderen Referenz zu einem verbundenen Port zu stehlen. Microsoft ist sich dieses Problems bewusst und hat eine weitere Socket-Option hinzugefügt: SO_EXCLUSIVEADDRUSE. Durch das Festlegen von SO_EXCLUSIVEADDRUSE für einen Socket wird sichergestellt, dass, sobald der Socket an eine Kombination aus Adresse und Port gebunden ist, jeder andere Socket, unabhängig davon, ob SO_REUSEADDR festgelegt ist oder nicht, nicht mehr an die aktuelle Kombination aus Adresse und Port gebunden werden kann.

Solaris

Solaris ist der Nachfolger von SunOS. SunOS ist in gewisser Weise auch ein Ableger einer früheren BSD-Version. Daher stellt Solaris nur SO_REUSEADDR bereit und seine Leistung ist im Wesentlichen die gleiche wie in BSD-Systemen. Soweit ich weiß, kann die gleiche Funktionalität wie SO_REUSEPORT nicht in Solaris-Systemen implementiert werden. Das bedeutet, dass Sie in Solaris nicht zwei Sockets an genau dieselbe Kombination aus Adresse und Port binden können.

Ähnlich wie Windows bietet auch Solaris eine exklusive Bindungsoption für Sockets – SO_EXCLBIND. Wenn ein Socket diese Option vor dem Binden der Adresse festlegt, können andere Sockets nicht an dieselbe Adresse binden, selbst wenn für sie SO_REUSEADDR festgelegt ist. Beispiel: Wenn SocketA an eine Wildcard-IP-Adresse gebunden ist und SocketB auf SO_REUSEADDR gesetzt und an eine Kombination aus einer bestimmten IP-Adresse und demselben Port wie SocketA gebunden ist, ist dieser Vorgang erfolgreich, wenn SocketA nicht auf SO_EXCLBIND gesetzt ist, andernfalls ist er erfolgreich scheitern.

Referenz:

http://stackoverflow.com/a/14388707/6037083


Das obige ist der detaillierte Inhalt vonBesprechen Sie im Detail den Unterschied zwischen so_reuseport und so_reuseaddr in Sockets. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn