GNU/Linux >> Linux Esercitazione >  >> Linux

In cosa differiscono SO_REUSEADDR e SO_REUSEPORT?

Benvenuti nel meraviglioso mondo della portabilità... o meglio della sua mancanza. Prima di iniziare ad analizzare queste due opzioni in dettaglio e dare uno sguardo più approfondito a come i diversi sistemi operativi le gestiscono, va notato che l'implementazione del socket BSD è la madre di tutte le implementazioni del socket. Fondamentalmente tutti gli altri sistemi hanno copiato l'implementazione del socket BSD a un certo punto nel tempo (o almeno le sue interfacce) e poi hanno iniziato a evolverlo da soli. Ovviamente anche l'implementazione del socket BSD si è evoluta allo stesso tempo e quindi i sistemi che l'hanno copiata in seguito hanno ottenuto funzionalità che mancavano nei sistemi che l'hanno copiata in precedenza. Comprendere l'implementazione del socket BSD è la chiave per comprendere tutte le altre implementazioni del socket, quindi dovresti leggerlo anche se non ti interessa mai scrivere codice per un sistema BSD.

Ci sono un paio di nozioni di base che dovresti sapere prima di esaminare queste due opzioni. Una connessione TCP/UDP è identificata da una tupla di cinque valori:

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

Qualsiasi combinazione univoca di questi valori identifica una connessione. Di conseguenza, due connessioni non possono avere gli stessi cinque valori, altrimenti il ​​sistema non sarebbe più in grado di distinguere queste connessioni.

Il protocollo di un socket viene impostato quando viene creato un socket con socket() funzione. L'indirizzo di origine e la porta sono impostati con bind() funzione. L'indirizzo di destinazione e la porta sono impostati con connect() funzione. Poiché UDP è un protocollo senza connessione, i socket UDP possono essere utilizzati senza collegarli. Tuttavia è consentito collegarli e in alcuni casi molto vantaggioso per il codice e la progettazione generale dell'applicazione. In modalità senza connessione, i socket UDP che non erano esplicitamente vincolati quando i dati vengono inviati su di essi per la prima volta vengono solitamente vincolati automaticamente dal sistema, poiché un socket UDP non associato non può ricevere alcun dato (di risposta). Lo stesso vale per un socket TCP non associato, viene automaticamente associato prima di essere connesso.

Se si associa esplicitamente un socket, è possibile associarlo alla porta 0 , che significa "qualsiasi porto". Poiché un socket non può essere effettivamente associato a tutte le porte esistenti, in tal caso il sistema dovrà scegliere lui stesso una porta specifica (di solito da un intervallo di porte di origine predefinito e specifico del sistema operativo). Esiste un carattere jolly simile per l'indirizzo di origine, che può essere "qualsiasi indirizzo" (0.0.0.0 in caso di IPv4 e :: nel caso di IPv6). Diversamente dal caso delle porte, un socket può davvero essere associato a "qualsiasi indirizzo" che significa "tutti gli indirizzi IP di origine di tutte le interfacce locali". Se il socket viene connesso successivamente, il sistema deve scegliere un indirizzo IP di origine specifico, poiché un socket non può essere connesso e allo stesso tempo essere associato a un indirizzo IP locale. A seconda dell'indirizzo di destinazione e del contenuto della tabella di routing, il sistema sceglierà un indirizzo di origine appropriato e sostituirà l'associazione "qualsiasi" con un'associazione all'indirizzo IP di origine scelto.

Per impostazione predefinita, non è possibile associare due socket alla stessa combinazione di indirizzo di origine e porta di origine. Finché la porta di origine è diversa, l'indirizzo di origine è effettivamente irrilevante. Legatura socketA a ipA:portA e socketB a ipB:portB è sempre possibile se ipA != ipB vale, anche quando portA == portB . Per esempio. socketA appartiene a un programma server FTP ed è associato a 192.168.0.1:21 e socketB appartiene a un altro programma server FTP ed è associato a 10.0.0.1:21 , entrambe le associazioni avranno esito positivo. Tieni presente, tuttavia, che un socket può essere associato localmente a "qualsiasi indirizzo". Se un socket è associato a 0.0.0.0:21 , è associato a tutti gli indirizzi locali esistenti contemporaneamente e in tal caso nessun altro socket può essere associato alla porta 21 , indipendentemente dall'indirizzo IP specifico a cui tenta di collegarsi, come 0.0.0.0 è in conflitto con tutti gli indirizzi IP locali esistenti.

Tutto ciò che è stato detto finora è praticamente uguale per tutti i principali sistemi operativi. Le cose iniziano a diventare specifiche del sistema operativo quando entra in gioco il riutilizzo degli indirizzi. Iniziamo con BSD, poiché come ho detto sopra, è la madre di tutte le implementazioni dei socket.

BSD

SO_REUSEADDR

Se SO_REUSEADDR è abilitato su un socket prima di associarlo, il socket può essere associato correttamente a meno che non vi sia un conflitto con un altro socket associato a esattamente la stessa combinazione di indirizzo sorgente e porta. Ora potresti chiederti in che modo è diverso da prima? La parola chiave è "esattamente". SO_REUSEADDR cambia principalmente il modo in cui gli indirizzi jolly ("qualsiasi indirizzo IP") vengono trattati durante la ricerca di conflitti.

Senza SO_REUSEADDR , legando socketA a 0.0.0.0:21 e quindi legare socketB a 192.168.0.1:21 fallirà (con errore EADDRINUSE ), poiché 0.0.0.0 significa "qualsiasi indirizzo IP locale", quindi tutti gli indirizzi IP locali sono considerati in uso da questo socket e questo include 192.168.0.1 , anche. Con SO_REUSEADDR avrà successo, poiché 0.0.0.0 e 192.168.0.1 non sono esattamente stesso indirizzo, uno è un carattere jolly per tutti gli indirizzi locali e l'altro è un indirizzo locale molto specifico. Nota che l'affermazione precedente è vera indipendentemente dall'ordine in cui socketA e socketB sono vincolati; senza SO_REUSEADDR fallirà sempre, con SO_REUSEADDR avrà sempre successo.

Per darti una panoramica migliore, creiamo una tabella qui ed elenchiamo tutte le possibili combinazioni:

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)

La tabella sopra presuppone che socketA è già stato collegato con successo all'indirizzo fornito per socketA , quindi socketB viene creato, ottiene SO_REUSEADDR impostato o meno, e infine è associato all'indirizzo fornito per socketB . Result è il risultato dell'operazione di bind per socketB . Se la prima colonna riporta ON/OFF , il valore di SO_REUSEADDR è irrilevante per il risultato.

Ok, SO_REUSEADDR ha effetto sugli indirizzi jolly, buono a sapersi. Eppure questo non è l'unico effetto che ha. C'è un altro effetto ben noto che è anche il motivo per cui la maggior parte delle persone usa SO_REUSEADDR nei programmi server in primo luogo. Per l'altro uso importante di questa opzione dobbiamo dare uno sguardo più approfondito a come funziona il protocollo TCP.

Se un socket TCP viene chiuso, normalmente viene eseguito un handshake a 3 vie; la sequenza si chiama FIN-ACK . Il problema qui è che l'ultimo ACK di quella sequenza potrebbe essere arrivato dall'altra parte o potrebbe non essere arrivato e solo se è arrivato, anche l'altra parte considera il socket come completamente chiuso. Per impedire il riutilizzo di una combinazione indirizzo+porta, che potrebbe essere ancora considerata aperta da qualche peer remoto, il sistema non considererà immediatamente morto un socket dopo aver inviato l'ultimo ACK ma mette invece il socket in uno stato comunemente indicato come TIME_WAIT . Può rimanere in quello stato per minuti (impostazione dipendente dal sistema). Sulla maggior parte dei sistemi è possibile aggirare tale stato abilitando il ritardo e impostando un tempo di ritardo pari a zero1, ma non vi è alcuna garanzia che ciò sia sempre possibile, che il sistema rispetterà sempre questa richiesta e, anche se il sistema la rispetta, ciò causa il socket da chiudere con un reset (RST ), che non è sempre una grande idea. Per saperne di più sul tempo di attesa, dai un'occhiata alla mia risposta su questo argomento.

La domanda è, come tratta il sistema un socket nello stato TIME_WAIT ? Se SO_REUSEADDR non è impostato, un socket nello stato TIME_WAIT è considerato ancora associato all'indirizzo e alla porta di origine e qualsiasi tentativo di associare un nuovo socket allo stesso indirizzo e alla stessa porta fallirà fino a quando il socket non sarà stato effettivamente chiuso. Quindi non aspettarti di poter ricollegare l'indirizzo di origine di un socket immediatamente dopo averlo chiuso. Nella maggior parte dei casi questo fallirà. Tuttavia, se SO_REUSEADDR è impostato per il socket che stai tentando di associare, un altro socket associato allo stesso indirizzo e porta nello stato TIME_WAIT viene semplicemente ignorato, dopotutto è già "mezzo morto" e il tuo socket può collegarsi esattamente allo stesso indirizzo senza alcun problema. In tal caso non ha alcun ruolo il fatto che l'altro socket possa avere esattamente lo stesso indirizzo e la stessa porta. Nota che associare un socket esattamente allo stesso indirizzo e porta di un socket morente in TIME_WAIT può avere effetti collaterali imprevisti e solitamente indesiderati nel caso in cui l'altro socket sia ancora "al lavoro", ma ciò va oltre lo scopo di questa risposta e fortunatamente tali effetti collaterali sono piuttosto rari nella pratica.

C'è un'ultima cosa che dovresti sapere su SO_REUSEADDR . Tutto quanto scritto sopra funzionerà fintanto che il socket a cui vuoi collegarti ha il riutilizzo degli indirizzi abilitato. Non è necessario che l'altro socket, quello che è già associato o sia in un TIME_WAIT state, aveva anche questo flag impostato quando è stato associato. Il codice che decide se il bind avrà esito positivo o negativo controlla solo il SO_REUSEADDR flag del socket immesso nel bind() call, per tutti gli altri socket ispezionati, questo flag non viene nemmeno visto.

SO_REUSEPORT

SO_REUSEPORT è ciò che la maggior parte delle persone si aspetterebbe SO_REUSEADDR essere. Fondamentalmente, SO_REUSEPORT ti consente di associare un numero arbitrario di socket a esattamente lo stesso indirizzo e porta di origine purché tutti anche i socket vincolati precedenti avevano SO_REUSEPORT fissati prima che fossero legati. Se il primo socket associato a un indirizzo e porta non ha SO_REUSEPORT impostato, nessun altro socket può essere associato esattamente allo stesso indirizzo e alla stessa porta, indipendentemente dal fatto che questo altro socket abbia SO_REUSEPORT impostato o meno, fino a quando il primo socket non rilascia nuovamente il suo binding. A differenza del caso di SO_REUESADDR il codice che gestisce SO_REUSEPORT non solo verificherà che il socket attualmente associato abbia SO_REUSEPORT set ma verificherà anche che il socket con indirizzo e porta in conflitto abbia SO_REUSEPORT impostato quando è stato associato.

SO_REUSEPORT non implica SO_REUSEADDR . Ciò significa che se un socket non aveva SO_REUSEPORT impostato quando è stato associato e un altro socket ha SO_REUSEPORT impostato quando è associato esattamente allo stesso indirizzo e alla stessa porta, il collegamento fallisce, come previsto, ma fallisce anche se l'altro socket sta già morendo ed è in TIME_WAIT stato. Essere in grado di associare un socket agli stessi indirizzi e alla stessa porta di un altro socket in TIME_WAIT state richiede SO_REUSEADDR da impostare su quel socket o SO_REUSEPORT deve essere stato impostato su entrambi prese prima di collegarle. Ovviamente è consentito impostare entrambi, SO_REUSEPORT e SO_REUSEADDR , su un socket.

Non c'è molto altro da dire su SO_REUSEPORT a parte il fatto che è stato aggiunto dopo SO_REUSEADDR , ecco perché non lo troverai in molte implementazioni di socket di altri sistemi, che "forcano" il codice BSD prima che questa opzione fosse aggiunta, e che non c'era modo di associare due socket esattamente allo stesso indirizzo di socket in BSD prima di questo opzione.

Connect() Restituisce EADDRINUSE?

La maggior parte delle persone sa che bind() potrebbe fallire con l'errore EADDRINUSE , tuttavia, quando inizi a giocare con il riutilizzo degli indirizzi, potresti imbatterti nella strana situazione in cui connect() fallisce anche con quell'errore. Come può essere? Come può un indirizzo remoto, dopo tutto quello che connect aggiunge a un socket, essere già in uso? Collegare più socket esattamente allo stesso indirizzo remoto non è mai stato un problema prima, quindi cosa c'è che non va?

Come ho detto all'inizio della mia risposta, una connessione è definita da una tupla di cinque valori, ricordi? E ho anche detto che questi cinque valori devono essere unici altrimenti il ​​sistema non può più distinguere due connessioni, giusto? Bene, con il riutilizzo degli indirizzi, puoi associare due socket dello stesso protocollo allo stesso indirizzo e porta di origine. Ciò significa che tre di questi cinque valori sono già gli stessi per questi due socket. Se ora provi a connettere entrambi questi socket anche allo stesso indirizzo di destinazione e porta, creeresti due socket connessi, le cui tuple sono assolutamente identiche. Questo non può funzionare, almeno non per le connessioni TCP (le connessioni UDP non sono comunque connessioni reali). Se i dati sono arrivati ​​per una delle due connessioni, il sistema non è in grado di stabilire a quale connessione appartengono i dati. Almeno l'indirizzo di destinazione o la porta di destinazione devono essere diversi per entrambe le connessioni, in modo che il sistema non abbia problemi a identificare a quale connessione appartengono i dati in entrata.

Quindi, se associ due socket dello stesso protocollo allo stesso indirizzo e alla stessa porta di origine e provi a connetterli entrambi allo stesso indirizzo e alla stessa porta di destinazione, connect() in realtà fallirà con l'errore EADDRINUSE per il secondo socket provi a connetterti, il che significa che un socket con una tupla identica di cinque valori è già connesso.

Indirizzi multicast

La maggior parte delle persone ignora il fatto che gli indirizzi multicast esistano, ma esistono. Mentre gli indirizzi unicast vengono utilizzati per la comunicazione uno-a-uno, gli indirizzi multicast vengono utilizzati per la comunicazione uno-a-molti. La maggior parte delle persone è venuta a conoscenza degli indirizzi multicast quando ha appreso dell'IPv6, ma gli indirizzi multicast esistevano anche in IPv4, anche se questa funzione non è mai stata ampiamente utilizzata nell'Internet pubblico.

Il significato di SO_REUSEADDR modifiche per gli indirizzi multicast in quanto consente a più socket di essere associati esattamente alla stessa combinazione di indirizzo e porta multicast di origine. In altre parole, per gli indirizzi multicast SO_REUSEADDR si comporta esattamente come SO_REUSEPORT per gli indirizzi unicast. In realtà, il codice tratta SO_REUSEADDR e SO_REUSEPORT in modo identico per gli indirizzi multicast, ciò significa che potresti dire che SO_REUSEADDR implica SO_REUSEPORT per tutti gli indirizzi multicast e viceversa.


FreeBSD/OpenBSD/NetBSD

Tutti questi sono fork piuttosto recenti del codice BSD originale, ecco perché tutti e tre offrono le stesse opzioni di BSD e si comportano allo stesso modo di BSD.


macOS (MacOS X)

Alla base, macOS è semplicemente un UNIX in stile BSD chiamato "Darwin ", basato su un fork piuttosto tardivo del codice BSD (BSD 4.3), che in seguito è stato persino risincronizzato con il codice base di FreeBSD 5 (a quel tempo attuale) per la versione Mac OS 10.3, in modo che Apple potesse guadagnare piena conformità POSIX (macOS è certificato POSIX). Nonostante abbia un microkernel al suo interno ("Mach "), il resto del kernel ("XNU ") è fondamentalmente solo un kernel BSD, ed è per questo che macOS offre le stesse opzioni di BSD e si comportano allo stesso modo di BSD.

iOS/watchOS/tvOS

iOS è solo un fork di macOS con un kernel leggermente modificato e tagliato, un set di strumenti per lo spazio utente in qualche modo ridotto e un set di framework predefinito leggermente diverso. watchOS e tvOS sono fork di iOS, che sono ulteriormente ridotti (in particolare watchOS). Per quanto ne so, si comportano tutti esattamente come macOS.


Linux

Linux <3.9

Prima di Linux 3.9, solo l'opzione SO_REUSEADDR esisteva. Questa opzione si comporta generalmente come in BSD con due importanti eccezioni:

  1. Finché un socket TCP in ascolto (server) è associato a una porta specifica, il SO_REUSEADDR l'opzione viene completamente ignorata per tutti i socket destinati a quella porta. Associare un secondo socket alla stessa porta è possibile solo se fosse possibile anche in BSD senza avere SO_REUSEADDR impostare. Per esempio. non puoi collegarti a un indirizzo jolly e poi a uno più specifico o viceversa, entrambi sono possibili in BSD se imposti SO_REUSEADDR . Quello che puoi fare è collegarti alla stessa porta e a due diversi indirizzi senza caratteri jolly, poiché è sempre consentito. Sotto questo aspetto Linux è più restrittivo di BSD.

  2. La seconda eccezione è che per i socket client, questa opzione si comporta esattamente come SO_REUSEPORT in BSD, purché entrambi avessero questo flag impostato prima di essere vincolati. Il motivo per consentire ciò era semplicemente che è importante essere in grado di associare più socket esattamente allo stesso indirizzo socket UDP per vari protocolli e poiché non esisteva SO_REUSEPORT prima della 3.9, il comportamento di SO_REUSEADDR è stato modificato di conseguenza per colmare tale lacuna. Sotto questo aspetto Linux è meno restrittivo di BSD.

Linux>=3.9

Linux 3.9 ha aggiunto l'opzione SO_REUSEPORT anche su Linux. Questa opzione si comporta esattamente come l'opzione in BSD e consente il binding esattamente allo stesso indirizzo e numero di porta purché tutti i socket abbiano questa opzione impostata prima del binding.

Tuttavia, ci sono ancora due differenze rispetto a SO_REUSEPORT su altri sistemi:

  1. Per prevenire il "port hijacking", c'è una limitazione speciale:Tutti i socket che vogliono condividere la stessa combinazione di indirizzo e porta devono appartenere a processi che condividono lo stesso ID utente effettivo! Quindi un utente non può "rubare" le porte di un altro utente. Questa è una magia speciale per compensare in qualche modo il SO_EXCLBIND mancante /SO_EXCLUSIVEADDRUSE flag.

  2. Inoltre il kernel esegue alcune "magie speciali" per SO_REUSEPORT socket che non si trovano in altri sistemi operativi:per i socket UDP, tenta di distribuire i datagrammi in modo uniforme, per i socket di ascolto TCP, tenta di distribuire le richieste di connessione in entrata (quelle accettate chiamando accept() ) in modo uniforme su tutti i socket che condividono la stessa combinazione di indirizzo e porta. Pertanto un'applicazione può facilmente aprire la stessa porta in più processi figlio e quindi utilizzare SO_REUSEPORT per ottenere un bilanciamento del carico molto economico.


Android

Anche se l'intero sistema Android è in qualche modo diverso dalla maggior parte delle distribuzioni Linux, al suo interno funziona un kernel Linux leggermente modificato, quindi tutto ciò che si applica a Linux dovrebbe valere anche per Android.


Finestre

Windows conosce solo SO_REUSEADDR opzione, non c'è SO_REUSEPORT . Impostazione SO_REUSEADDR su un socket in Windows si comporta come impostare SO_REUSEPORT e SO_REUSEADDR su un socket in BSD, con un'eccezione:

Prima di Windows 2003, un socket con SO_REUSEADDR potrebbe sempre essere associato esattamente allo stesso indirizzo di origine e porta di un socket già associato, anche se l'altro socket non aveva questa opzione impostata quando è stato associato . Questo comportamento consentiva a un'applicazione di "rubare" la porta connessa di un'altra applicazione. Inutile dire che questo ha importanti implicazioni per la sicurezza!

Microsoft se ne rese conto e aggiunse un'altra importante opzione socket:SO_EXCLUSIVEADDRUSE . Impostazione SO_EXCLUSIVEADDRUSE su un socket fa in modo che se l'associazione ha esito positivo, la combinazione di indirizzo di origine e porta sia di proprietà esclusiva di questo socket e nessun altro socket possa collegarsi ad essi, nemmeno se ha SO_REUSEADDR impostato.

Questo comportamento predefinito è stato modificato prima in Windows 2003, Microsoft lo chiama "Enhanced Socket Security" (nome divertente per un comportamento predefinito su tutti gli altri principali sistemi operativi). Per maggiori dettagli basta visitare questa pagina. Ci sono tre tabelle:la prima mostra il comportamento classico (ancora in uso quando si usano le modalità di compatibilità!), la seconda mostra il comportamento di Windows 2003 e successivi quando il bind() le chiamate vengono effettuate dallo stesso utente, e la terza quando il bind() le chiamate vengono effettuate da utenti diversi.


Solari

Solaris è il successore di SunOS. SunOS era originariamente basato su un fork di BSD, SunOS 5 e successivi erano basati su un fork di SVR4, tuttavia SVR4 è una fusione di BSD, System V e Xenix, quindi fino a un certo punto anche Solaris è un fork di BSD e un piuttosto precoce. Di conseguenza Solaris conosce solo SO_REUSEADDR , non esiste SO_REUSEPORT . Il SO_REUSEADDR si comporta più o meno come in BSD. Per quanto ne so, non c'è modo di ottenere lo stesso comportamento di SO_REUSEPORT in Solaris, ciò significa che non è possibile associare due socket esattamente allo stesso indirizzo e alla stessa porta.

Analogamente a Windows, Solaris ha un'opzione per assegnare a un socket un'associazione esclusiva. Questa opzione è denominata SO_EXCLBIND . Se questa opzione è impostata su un socket prima di associarlo, impostando SO_REUSEADDR su un altro socket non ha effetto se i due socket vengono testati per un conflitto di indirizzi. Per esempio. se socketA è associato a un indirizzo jolly e socketB ha SO_REUSEADDR abilitato ed è associato a un indirizzo senza caratteri jolly e alla stessa porta di socketA , questo bind avrà normalmente successo, a meno che non sia socketA aveva SO_EXCLBIND abilitato, nel qual caso fallirà indipendentemente dal SO_REUSEADDR flag di socketB .


Altri sistemi

Nel caso in cui il tuo sistema non sia elencato sopra, ho scritto un piccolo programma di test che puoi usare per scoprire come il tuo sistema gestisce queste due opzioni. Anche se ritieni che i miei risultati siano sbagliati , per prima cosa esegui quel programma prima di pubblicare qualsiasi commento e possibilmente fare affermazioni false.

Tutto ciò che il codice richiede per compilare è un po' di API POSIX (per le parti di rete) e un compilatore C99 (in realtà la maggior parte dei compilatori non C99 funzionerà bene purché offra inttypes.h e stdbool.h; per esempio. gcc supportati entrambi molto prima di offrire il pieno supporto C99).

Tutto ciò che il programma deve eseguire è che almeno un'interfaccia nel tuo sistema (diversa dall'interfaccia locale) abbia un indirizzo IP assegnato e che sia impostata una route predefinita che utilizza quell'interfaccia. Il programma raccoglierà quell'indirizzo IP e lo utilizzerà come secondo "indirizzo specifico".

Testa tutte le possibili combinazioni che ti vengono in mente:

  • Protocollo TCP e UDP
  • Prese normali, prese di ascolto (server), prese multicast
  • SO_REUSEADDR impostato su socket1, socket2 o entrambi i socket
  • SO_REUSEPORT impostato su socket1, socket2 o entrambi i socket
  • Tutte le combinazioni di indirizzi che puoi ricavare da 0.0.0.0 (carattere jolly), 127.0.0.1 (indirizzo specifico) e il secondo indirizzo specifico trovato nella tua interfaccia principale (per il multicast è solo 224.1.2.3 in tutti i test)

e stampa i risultati in una bella tabella. Funzionerà anche su sistemi che non riconoscono SO_REUSEPORT , nel qual caso questa opzione semplicemente non è testata.

Ciò che il programma non può verificare facilmente è come SO_REUSEADDR agisce sui socket in TIME_WAIT stato in quanto è molto difficile forzare e mantenere un socket in quello stato. Fortunatamente la maggior parte dei sistemi operativi sembra comportarsi semplicemente come BSD qui e la maggior parte delle volte i programmatori possono semplicemente ignorare l'esistenza di quello stato.

Ecco il codice (non posso includerlo qui, le risposte hanno un limite di dimensione e il codice spingerebbe questa risposta oltre il limite).


La risposta di Mecki è assolutamente perfetta, ma vale la pena aggiungere che FreeBSD supporta anche SO_REUSEPORT_LB , che imita SO_REUSEPORT di Linux comportamento - bilancia il carico; vedere setsockopt(2)


Linux
  1. Come gestire ed elencare i servizi in Linux

  2. Che cos'è Podman e in che cosa differisce da Docker?

  3. Procedura:replica e configurazione DRBD

  4. Come rimuovo una connessione socket CLOSE_WAIT

  5. In che modo ulimit -n e /proc/sys/fs/file-max differiscono?

Come installare e utilizzare lo schermo Linux?

Come rinominare file e directory in Linux

Come comprimere file e directory in Linux

Come installare e configurare SeedDMS

Come installare e configurare Grafana

Come collegare in rete Ubuntu e Windows 10?