Domanda

È possibile annullare l'ascolto su un socket dopo aver chiamato hear (fd, backlog)?

Modifica: il mio errore per non essermi chiarito. Mi piacerebbe essere in grado di non ascoltare temporaneamente sul socket. Chiamare close () lascerà il socket nello stato M2LS e mi impedirà di riaprirlo (o peggio, qualche programma malvagio potrebbe legarsi a quel socket)

L'ascolto temporaneo sarebbe un modo (forse non il modo migliore) per segnalare a un bilanciamento del carico a monte che questa app non poteva accettare ulteriori richieste per il momento

È stato utile?

Soluzione

Alcune librerie socket consentono di rifiutare specificamente le connessioni in entrata. Ad esempio: GNU CommonC ++: TCPsocket Class ha un rifiuta .

I socket BSD non hanno questa funzionalità. Puoi accettare la connessione e quindi immediatamente chiudere , lasciando aperto il socket:

while (running) {

  int i32ConnectFD = accept(i32SocketFD, NULL, NULL);
  while (noConnectionsPlease) {
    shutdown(i32ConnectFD, 2);
    close(i32ConnectFD);
    break;
  }

}

Altri suggerimenti

Dopo aver chiuso il socket, i tuoi programmi potrebbero ancora dirti che il socket è "in uso", a causa di una stranezza di cui non so esattamente. Ma la manpage sui socket mostra che c'è una bandiera per riutilizzare lo stesso socket, chiamato pigramente: " SO_REUSEADDR " ;. Impostalo usando " setsockopt () " ;.

Chiudilo. Come ricordo,

close(fd);

In base alla versione modificata della domanda, non sono sicuro che devi "non ascoltare" " o chiudi (). Mi vengono in mente due opzioni:

1) Dopo aver richiamato Listen (), le connessioni non vengono effettivamente accettate fino a quando (abbastanza logicamente) non si chiama Accept (). Puoi " non ascoltare " semplicemente ignorando l'attività del socket e rimandando qualsiasi accetta () fino a quando non si è pronti per loro. Qualsiasi connessione in entrata tenta di eseguire il backlog sulla coda creata all'apertura della porta in modalità di ascolto. Una volta che la coda di backlog è piena nello stack, ulteriori tentativi di connessione vengono semplicemente eliminati sul pavimento. Quando riprendi con accept (), rimuoverai rapidamente il backlog e sarai pronto per ulteriori connessioni.

2) Se vuoi davvero che la porta appaia completamente chiusa temporaneamente, potresti applicare dinamicamente il filtro di pacchetti a livello di kernel alla porta per evitare che i tentativi di connessione in entrata raggiungano lo stack di rete. Ad esempio, è possibile utilizzare Berkeley Packet Filter (BPF) sulla maggior parte delle piattaforme * nix. Cioè si desidera eliminare i pacchetti in entrata che arrivano alla porta di interesse utilizzando le funzionalità firewall della piattaforma. Questo, ovviamente, varia a seconda della piattaforma, ma è un possibile approccio.

Non credo sia un buon modo per segnalare un bilanciamento del carico a monte. Dovrebbe effettivamente inviare alcune connessioni al tuo server prima che il messaggio arrivi, probabilmente tali connessioni verrebbero rifiutate.

Allo stesso modo, tutte le connessioni in sospeso alla chiusura del socket di ascolto verranno chiuse senza dati.

Se vuoi segnalare il bilanciamento del carico a monte, dovresti avere un protocollo per farlo. Non tentare di abusare di TCP per farlo.

Fortunatamente se i client sono normali browser web, puoi farcela con un sacco di cose - semplicemente chiudere socket in genere porta a riprovare in modo trasparente per l'utente (fino a un certo punto).

Non esiste un metodo esplicito per annullare l'ascolto!

Puoi chiudere (fd) o shutdown (fd, how)

fd is the socket file descriptor you want to shutdown, and how is one of the following:

0 Further receives are disallowed

1 Further sends are disallowed

2 Further sends and receives are disallowed (like close())

A livello di base, i socket sono aperti o chiusi (ignoreremo qui i dettagli del diagramma di stato TCP / IP).

Se il socket è chiuso, nulla può inviargli dati. Se è aperto, i dati in arrivo saranno accettati e riconosciuti dallo stack TCP / IP fino a quando l'algoritmo di buffering non grida "abbastanza!". A quel punto, ulteriori dati non saranno riconosciuti.

Hai due scelte che posso vedere. Chiudete il socket () quando volete "rimuovere l'ascolto" e riaprirlo in seguito - Utilizzate setsockopt () con il flag SO_REUSEADDR per consentire di ricollegarvi alla porta nota prima della scadenza di TIME_WAIT2.

L'altra scelta è quella di mantenere aperto il socket ma semplicemente non accettarlo () mentre sei 'occupato'. Supponendo di avere un riconoscimento a livello di applicazione alle richieste, il bilanciamento del carico si accorgerebbe che non sta ottenendo una risposta e agirà di conseguenza.

Ecco un approccio piuttosto brutto basato sulla tua domanda modificata:

Apri un socket per l'ascolto con un normale backlog. Procedere.

Se vuoi " chiudere ", aprine un secondo con un backlog di 1 e SO_REUSEADDR. Chiudi il primo. Quando sei pronto per riprendere, esegui un altro juggle con un normale backlog.

I dettagli schizzinosi che riguardano lo svuotamento della coda di accettazione dal socket che stai chiudendo saranno l'assassino qui. Probabilmente abbastanza di un killer per rendere questo approccio non praticabile.

Non penso necessariamente che sia una buona idea, ma ...

Potresti essere in grado di chiamare l'ascolto una seconda volta. Le specifiche POSIX non dicono di non farlo. Forse potresti chiamarlo una seconda volta con un parametro di backlog di 0 quando vuoi "non ascoltare" "

Ciò che accade quando viene chiamato l'ascolto con un backlog di 0 sembra essere definito dall'implementazione. Le specifiche POSIX dicono che potrebbe consentire l'accettazione delle connessioni, il che implica che alcune implementazioni potrebbero scegliere di rifiutare tutte le connessioni se il parametro backlog è 0. Più probabilmente, l'implementazione sceglierà un valore positivo quando si passa in 0 (probabilmente 1 o SOMAXCONN).

La domanda non diceva che tipo di socket. Se si tratta di un socket unix, è possibile interrompere e iniziare l'ascolto con rinomina (2). Puoi anche interrompere definitivamente l'ascolto con unlink (2) e poiché il socket rimane aperto puoi continuare a servire il tuo backlog. Questo approccio sembra abbastanza utile, anche se non l'ho mai visto usato prima e lo sto esplorando da solo.

Hai già ricevuto alcune risposte sull'impossibilità di farlo tramite l'API socket.

È possibile utilizzare altri metodi del sistema operativo (ad esempio Host firwewall / iptables / ipfilter) per impostare una regola di rifiuto temporanea.

Ho scoperto che la maggior parte dei sistemi di bilanciamento del carico sono un po 'limitati nelle possibilità che offrono di riconoscere i problemi di connessione (la maggior parte di essi riconosce un RST solo nel probe di connessione, non come risposta a un tentativo di connessione legittimo.)

Tuttavia, se si è limitati dalle sonde che rilevano l'indisponibilità, si imposta un probe a livello di applicazione che esegue una richiesta HTTP o un login FTP o cose simili che riconoscerà se si chiude semplicemente dopo aver accettato. Potrebbe persino interpretare messaggi di errore come "500 servizio non disponibile", che mi sembra comunque più pulito. Con SNMP alcuni bilanciatori del carico possono anche utilizzare il risultato come suggerimento per il carico.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top