PDA

View Full Version : [socket C/C++] Capire che il cavo di rete e' staccato


mr_hyde
23-03-2006, 13:26
Ciao a tutti!

Sto lavorando ad un applicazione (su Unix, in particolare HP-UX, ma sto facendo le prove su Linux) che scambia dati con un server via socket.

In generale, la connessione tra client e server viene sempre mantenuta attiva, e' non bloccante e puo' rimanere in idle (ovvero senza scambiare alcun dato) per ore.

Il mio problema e' che, dal lato CLIENT, devo potermi accorgere che la connessione e' "morta" (caso classico: il cavo di rete viene staccato) entro un tempo limitato (nell'ordine di 2/3 minuti dal verificarsi del problema) per mandare una segnalazione ecc. ecc.

Posso intervenire solo sul client.

Ho provato un po' di tutto e al momento:
1) creo il socket e lo imposto come non bloccante
2) uso una select con timeout per controllare se ho qualcosa nell'fd_set di input (in cui ho messo l'id del socket) o nell'fd_set degli "errori"
3) se la select indica che ho qualcosa in input leggo con recv e processo i dati, se la select ha indicato un errore (fd_set degli errori) chiudo la connessione
4) se invece scatta il timeout, per vedere se in realta' la connessione e' ancora viva, provo a spedire un byte con

send(sockid, &the_byte, 1, MSG_OOB)

scrivo con MSG_OOB per evitare che questo byte di prova possa essere scambiato dal server per un dato "vero e proprio" da processare

Mi aspetto a questo punto che, in caso di cavo di rete staccato, la send fallisca o che, almeno, fallisca la select alla prossima iterazione.

Per testare il tutto ho creato un piccolo programmino che, in un ciclo infinito, ripete i passi scritti sopra e in caso di errori o select fallite mi stampa anche la variabile erro. Inizio quindi lanciando il programmino con cavo di rete attaccato: il server non trasmette alcun dato, ma la connessione e' presente.

In questo caso noto che:
1) la select scatta per il timeout
2) la send viene eseguita correttamente

A questo punto, mentre il programmino sta ancora nadando, stacco il cavo di rete e noto che:
1) la select scatta per il timeout
2) la send viene eseguita correttamente

Insomma... non noto differenze! Non riesco a differenziare il caso "idle" dal caso "problemi di connessione".

Sospettando che per diminuire il traffico di rete le scritture effettive sul socket vengano in realta' bufferizzate (poi ho letto che e' effettivamente cosi') ho provato a spedire un quantitativo molto maggiore di un byte (provato con circa 6000 byte) e ho visto che, a volte, effettivamente la send fallisce.

Ma e' questo il modo corretto di intercettare questo tipo di problema?

Altra cosa: ho provato anche impostando l'opzione SO_KEEPALIVE (comunque le 2 ore previste sono troppe) e la TCP_NODELAY, ma non ho avuto apprezzabili risutlati.

Grazie a tutti in anticipo delle risposte, critiche e commenti
Mr Hyde

ilsensine
23-03-2006, 13:40
Situazioni di questo tipo vanno gestite con messaggi di keepalive a livello del tuo protocollo. Se non è possibile, quello che puoi fare è usare dei poll di controllo indiretti (ad es. dei periodici ping sul server, o qualsiasi altra cosa che ti viene in mente).

mr_hyde
23-03-2006, 13:52
Situazioni di questo tipo vanno gestite con messaggi di keepalive a livello del tuo protocollo. Se non è possibile, quello che puoi fare è usare dei poll di controllo indiretti (ad es. dei periodici ping sul server, o qualsiasi altra cosa che ti viene in mente).

Ahime ormai lo sospettavo, ma speravo di sbagliare io qualcosa...

Per l'idea del ping: da specifiche ho la certezza che il server remoto risponda ai ping (nel senso: non ci sono firewall che sbarrano ICMP). Posso quindi implementare nel mio codice _FACILMENTE_ (a questo livello non sono mai arrivato) una sorta di ping minimale (quindi creazione/ricezione di ICMP)? Se non ricordo male, HP-UX NON ha i socket RAW IP...
Nel caso hai qualche link utile sull'argomento?

Grazie ancora della risposta

ilsensine
23-03-2006, 14:06
Non ti reinventare la ruota; invoca direttamente "ping -c 1 <server>" e esamina il valore ritornato.
Tieni conto che il protocollo icmp non è affidabile; quindi aspetta il fallimento consecutivo di un certo numero di ping prima di dichiarare il link down.

mr_hyde
23-03-2006, 14:18
Non ti reinventare la ruota; invoca direttamente "ping -c 1 <server>" e esamina il valore ritornato.
Tieni conto che il protocollo icmp non è affidabile; quindi aspetta il fallimento consecutivo di un certo numero di ping prima di dichiarare il link down.

Non e' che ho voglia di inventare la ruota, e' che, PURTOPPO NON ho garanzie sulle macchine dove verra' installato il client (quindi nessuna garanzia sul path del comando PING, nessuna garanzia sulle variabili d'ambiente - e quindi della $PATH - dell'utente che eseguira' il comando).

Ciao,
Mr Hyde

ilsensine
23-03-2006, 14:21
Ancora devo incontrare un s/o diverso dal DOS che è sprovvisto di ping.

Tieni inoltre conto che mandare pacchetti icmp richiede i privilegi di root. Il programma ping infatti è root suid.

mr_hyde
23-03-2006, 14:42
No, intendevo dire che non e' detto che l'utente (intendo l'utente UNIX) che eseguira' il client abbia in $PATH il ping!
E non e' detto che il ping sia sempre nello stesso percorso assoluto.

Comunque la faccenda dei diritti di root mi taglia le gambe: a questo punto cambiero' completamente approccio e inviero' al server ogni 2/3 minuti un qualche comando che implica che il server ri-spedisca indietro una risposta...

Grazie comunque per la disponibilita'

Ciao,
Mr Hyde

ilsensine
23-03-2006, 14:59
ping sta in /bin, punto :)

sottovento
23-03-2006, 15:10
No, intendevo dire che non e' detto che l'utente (intendo l'utente UNIX) che eseguira' il client abbia in $PATH il ping!
E non e' detto che il ping sia sempre nello stesso percorso assoluto.

Comunque la faccenda dei diritti di root mi taglia le gambe: a questo punto cambiero' completamente approccio e inviero' al server ogni 2/3 minuti un qualche comando che implica che il server ri-spedisca indietro una risposta...

Grazie comunque per la disponibilita'

Ciao,
Mr Hyde

Ciao,
ho appena sviluppato un'applicazione nella quale, a specifica, c'era proprio la realizzazione di un meccanismo di watchdog: ogni 10 secondi di inattivita' si prevedeva l'invio di un messaggio, e la controparte doveva fare la stessa cosa.
Nel caso di non ricezione di alcun dato per un tempo superiore all'intervallo stabilito, la connessione doveva esser chiusa e ne doveva essere stabilita una nuova.

Potrebbe esserti utile implementare una soluzione simile (non e' per niente pesante, ed e' facile da implementare). Il problema del ping e' che non hai programmaticamente alcuna informazione (a meno di non decodificare l'output di ping, ma mi sembra che come complessita' non sia tanto inferiore).

High Flying
Sottovento

ilsensine
23-03-2006, 15:15
Il problema del ping e' che non hai programmaticamente alcuna informazione (a meno di non decodificare l'output di ping, ma mi sembra che come complessita' non sia tanto inferiore).
int result = system("/bin/ping -c 1 <host>");
if (result!=0) {
// ping fallito
}

sottovento
23-03-2006, 15:24
int result = system("/bin/ping -c 1 <host>");
if (result!=0) {
// ping fallito
}

Result:
"Bad option -c."

ilsensine
23-03-2006, 15:56
Result:
"Bad option -c."
Su un sistema unix!?

sottovento
23-03-2006, 16:01
ping(1M)

NAME
ping - send ICMP ECHO_REQUEST packets to network hosts

SYNOPSIS
ping [-r] [-v] [-o] host [packetsize] [count]

ilsensine
23-03-2006, 16:04
ping(1M)

NAME
ping - send ICMP ECHO_REQUEST packets to network hosts

SYNOPSIS
ping [-r] [-v] [-o] host [packetsize] [count]
mmm....

NAME
ping, ping6 - send ICMP ECHO_REQUEST to network hosts

SYNOPSIS
ping [ -LRUbdfnqrvVaAB] [ -c count] [ -i interval] [ -l preload] [ -p pattern] [ -s packet-
size] [ -t ttl] [ -w deadline] [ -F flowlabel] [ -I interface] [ -M hint] [ -Q tos] [ -S
sndbuf] [ -T timestamp option] [ -W timeout] [ hop ...] destination

Che io sappia, tutte le versioni unix di ping hanno -c disponibile...

ilsensine
23-03-2006, 16:05
ping(1M)

NAME
ping - send ICMP ECHO_REQUEST packets to network hosts

SYNOPSIS
ping [-r] [-v] [-o] host [packetsize] [count]
Comunque anche il tuo "strano" ping va bene, basta settare il parametro count. -c 1 imposta appunto count=1.

Luca69
23-03-2006, 16:07
Man ping
....
-c traffic_class
Specify the traffic class of probe packets. The
value must be an integer in the range from 0 to
255. Gateways along the path may route the probe
packet differently depending upon the value of
traffic_class set in the probe packet. This
option is valid only on IPv6
....

Su SunOS 5.8

ilsensine
23-03-2006, 16:11
Man ping
....
-c traffic_class
Specify the traffic class of probe packets. The
value must be an integer in the range from 0 to
255. Gateways along the path may route the probe
packet differently depending upon the value of
traffic_class set in the probe packet. This
option is valid only on IPv6
....

Su SunOS 5.8
Ok altra versione. Chi offre di più? :D

In ogni caso però il ping deve disporre di un modo per indicare il numero di pacchetti echo da inviare; basta utilizzare la forma giusta per il ping che si ha a disposizione.

sottovento
23-03-2006, 16:12
Comunque anche il tuo "strano" ping va bene, basta settare il parametro count. -c 1 imposta appunto count=1.

Beh, in effetti, si.
Cmq resto dell'idea (magari sbagliata) che la possibilita' di spedire programmaticamente (conseguentemente leggere) dia piu' possibilita' di controllo.

Ci sono, per esempio, dei casi in cui anche il ping resta "appeso". Non avresti possibilita' di dominare questa situazione (a me capita se spengo di colpo una delle macchine). Un timeout del tipo di cui sopra permette di uscire da questa situazione.
Mi e' capitato di sperimentarla in un software "critico" (forse troppo).

Il ping che ti ho riportato e' di HP-UX (non ho controllato la versione).

High Flying
Sottovento

ilsensine
23-03-2006, 16:15
Beh, in effetti, si.
Cmq resto dell'idea (magari sbagliata) che la possibilita' di spedire programmaticamente (conseguentemente leggere) dia piu' possibilita' di controllo.
Sì infatti - come ho detto prima - una soluzione a livello di protocollo sarebbe l'ideale.

Ci sono, per esempio, dei casi in cui anche il ping resta "appeso".
Il mio "potentissimo" ping dispone del parametro -W. In generale, c'è un timeout di alcuni secondi -- non è infinito.

Il ping che ti ho riportato e' di HP-UX (non ho controllato la versione).
Quindi un qualcosa tipo "/bin/ping <host> 256 1" dovrebbe funzionare sul tuo ping...

mr_hyde
23-03-2006, 16:20
Ciao,
ho appena sviluppato un'applicazione nella quale, a specifica, c'era proprio la realizzazione di un meccanismo di watchdog: ogni 10 secondi di inattivita' si prevedeva l'invio di un messaggio, e la controparte doveva fare la stessa cosa.
Nel caso di non ricezione di alcun dato per un tempo superiore all'intervallo stabilito, la connessione doveva esser chiusa e ne doveva essere stabilita una nuova.

Potrebbe esserti utile implementare una soluzione simile (non e' per niente pesante, ed e' facile da implementare). Il problema del ping e' che non hai programmaticamente alcuna informazione (a meno di non decodificare l'output di ping, ma mi sembra che come complessita' non sia tanto inferiore).

High Flying
Sottovento

Grazie per l'idea, piu' o meno sto implementando una cosa simile...

Per il ping: sulla mia HP di prova NON e' in /bin, inoltre a causa del particolare tipo di applicazioni per cui vengono usate queste HP (e gli altri tipi di Unix destinati allo stesso scopo) non e' detto che l'utente che fara' girare questo client abbia accesso al ping (purtroppo per me)

Grazie a tutti dei consigli.
Ciao,
Mr Hyde

BountyKiller
24-03-2006, 13:06
sicuro di non aver cancellato il programma ping?
sul mio HP-ux è in /bin

magari prova a fare un updatedb e poi un ping .......