PDA

View Full Version : [C] stupido stupido socket


luppolo
08-12-2007, 13:40
Ciao ragazzi,
ormai non ricordo più da quanto sto sbattendo la testa su questo applicativo...
devo semplicemente creare 2 applicativi (sotto linux) il primo che crea dei socket tcp dai quali spedisco pacchetti, il secondo che riceve questi pacchetti e li ridirige a seconda del tipo di pacchetto che arriva.

funziona praticamente tutto, il problema pero' è che il socket che riceve i pacchetti di tanto in tanto diventa lentissimo a leggere (cioè passa da 10/20 millisecondi a 1/2 secondi per la lettura di un intero pacchetto di circa 2000byte!!!).

Di questo sono abbastanza sicuro... nel senso che ho fatto centinaia di controlli, e posso affermare con certezza che il problema SICURAMENTE non risiede ne nell'applicativo che spedisce i dati, e nemmeno risiede nel pezzo di codice che effettua le write dell'applicativo che legge i dati...
un'altra cosa un po' anomala, è che il problema a volte si presenta da subito... a volte non si presenta... altre volte funziona per un po' e poi inizia a rallentare.....

ho ridotto all'osso il codice, provando ad usare un solo socket ... ma niente...
sembra una maledizione + che un errore...

riuscite a darci un occhio? io l'ho controllato fino alla nausea e secondo me è corretto....

per semplicità inserisco solo la parte incriminata....

Grazie a chiunque riuscirà a darmi un indizio....

PS:i socket sono NON BLOCCANTI e utilizzo il polling sulla select... e comunque anche con tecniche di i/o multiplexing non ho ottenuto miglioramenti.

for(;;)
{
//******************************************************
//** LETTURA **
//******************************************************
rdset=all;
do {
ris=select(maxfd+1,&rdset,NULL,NULL,&noWaiting);
} while ((ris<0) && (errno==EINTR));
if(ris<0)
{
perror("select failed: ");
exit(1);
}
else if (ris==0) {/* debug */}
else //un socket è pronto in lettura!!
{
for(i=0;i<numconnentrata;i++)
{
//********************************************************
//** TENTATIVI DI CONNESSIONE **
//********************************************************
if ((listenfdattivo[i]) && (FD_ISSET(listenfd[i],&rdset )))
{
OMISSIS
qui dentro c'e' il codice con cui accetto le
connessioni in entrata sui socket in ascolto
e con cui creo i socket da cui verranno ridiretti
i vari pacchetti giunti sin qui
qui non ci sono errori e comunque la funzione
entra qua dentro solo quando un socket prova
a collegarsi
}
//********************************************************
//** TENTATIVI DI SCRITTURA **
//********************************************************
L'ERRORE DEVE ESSERE QUI.... MA SEMBRA TUTTO OK!!!!!!!!!!!!!!
else if( (connectedfdattivo[i]) && (FD_ISSET(connectedfd[i],&rdset )) )
{
//Provo a leggere un messaggio intero,
//oppure i byte necessari a completarlo.
do {
ris=read(connectedfd[i], p_msgR[i], byteleftR[i]);
} while ( (ris<0) && (errno==EINTR) );
if(ris<0)
{
perror("read canale failed: ");exit (1);
fflush(stdout);
return (0);
}
else if(ris==0)
{
//Non ho più dati da leggere
printf ("Connessione n. %d chiusa \n", i);
close(connectedfd[i]);
FD_CLR(connectedfd[i], &all);
connectedfdattivo[i] = 0;
}
else
{
p_msgR[i] += ris;
byteleftR[i] -= ris;
}
if (byteleftR[i] == 0)
{
OMISSIS
nel caso abbia finito di leggere un
messaggio intero, lo inserisco in una cosa
e reinizializzo le variabili p_msgR e byteleftR[i]
}
}
}


vedete??? e talmente semplice che non mi capacito di come possa esserci un errore...

se avete bisogno di altre informazioni chiedete pure... e grazie ancora...

ilsensine
08-12-2007, 14:15
Ad una prima occhiata, non mi sembra che ci siano sviste nella parte di codice che hai postato. Se puoi, dovresti allegare due esempi minimali ma compilabili, che possono permettere di riprodurre il problema.


un'altra cosa un po' anomala, è che il problema a volte si presenta da subito... a volte non si presenta... altre volte funziona per un po' e poi inizia a rallentare.....
Stessi sintomi sia utilizzano l'interfaccia di loopback che l'interfaccia ethernet?

L'unica cosa che ti chiedo di verificare, ma solo perché non è evidente dal codice che hai postato, è che maxfd sia sempre >= degli fd dei singoli socket.

luppolo
09-12-2007, 02:28
ciao ilsensine,
inizio a risponderti dalle domande più semplici:

- maxfd ha sempre un valore > di qualsiasi file descriptor da me creato.
questo perchè banalmente aggiungo dapo la creazione di ciascun fd una linea di codice del tipo

if(maxfd<XXXfd) maxfd=XXXfd;



Stessi sintomi sia utilizzano l'interfaccia di loopback che l'interfaccia ethernet?

-a questo non ti so rispondere.... onestamente non so come modificare l'interfaccia per eseguire il test da te richiesto.

-per quanto riguarda il primo appunto...
entro domani modificherò i miei file in modo da poterti presentare una versione il più possibile semplificata ....
ti posterei anche gli attuali files, ma sono troppo grossi.... non voreri farti perdere più tempo del necessario...


Per adesso grazie dell'interessamento :)

ilsensine
09-12-2007, 08:45
-a questo non ti so rispondere.... onestamente non so come modificare l'interfaccia per eseguire il test da te richiesto.
L'interfaccia di loopback (lo) ha indirizzo 127.0.0.1

luppolo
09-12-2007, 14:30
si scusami... sono proprio fuori.... l'applicazione gira in locale... uso sempre l'ip del localhost....

bene... ho finito...
non è proprio la quinta essenza della programmazione ma dovrebbe essere abbastanza chiaro...

allego tutto come in uno zip...
ho messo anche il makefile.
col comando make yatta, fai partire tutte le applicazioni...

nel file leggimi ci sono un paio di note...

grazie di nuovo...

ilsensine
09-12-2007, 17:36
Non riesco a riprodurre sul mio computer (che è una macchina linux cmq) l'effetto che descrivi, almeno non nella tua proporzione. Il problema credo che sia nella ProxySender.c, dove per qualche motivo masochistico decidi di spedire pacchetti di 10 byte alla volta, rifacendo la select ad ogni volta. Così generi un flood di pacchetti tcp, che a quanto pare la tua macchina non riesce a generare al tasso dei messaggi in arrivo e così accumuli latenze su latenze. Prova ad innalzare quel 10 byte a valori ragionevoli.

luppolo
09-12-2007, 19:00
si, hai perfettamente ragione.. è stupido mandare solo 10 byte a volta, il fatto è che queste sono le specifiche.
cioè in realtà nel progetto completo esisterebbe un altra applicazione a metà fra
i due proxy, una specie di simulatore di internet che introduce ritardi casuali su un canale a caso per simulare congestione (per semplicità ne ho messo solo 1 canale in quei file, ma in realtà ci sarebbero + socket aperti e io dovrei con discernimento inviare i pacchetti verso le connessioni + veloci) ma questo non creerebbe problemi se non fosse che questo simulatore spedisce pacchetti di 10 byte...
se io eliminassi la limitazione dal mio file me la ritroverei comunque.

ma per curiosità sulla tua macchina, i pacchetti arrivano sempre in tempo?
e + o - quanti ms ci impiegano mediamente??

anch'io se devo essere sincero davo la colpa al troppo lavoro (inutile), ma se così fosse, non mi spiego come mai a volte capiti che tutto va a buon fine e a volte invece va pianissimo....

guarda domani che sono a casa ti passo il progetto originale, che prevede + socket (ma dove comunque ne uso solo 1 per questioni di debug).
Ovviamente non pretendo che tu te lo guardi e analizzi, vorrei solamente che tu lo lanciassi per farti capire cosa intndo.
Sul progetto c sono + commenti ed è + veloce capire cosa stanno facendo i vari applicativi.

ilsensine
09-12-2007, 20:22
si, hai perfettamente ragione.. è stupido mandare solo 10 byte a volta, il fatto è che queste sono le specifiche.
cioè in realtà nel progetto completo esisterebbe un altra applicazione a metà fra
i due proxy, una specie di simulatore di internet che introduce ritardi casuali su un canale a caso per simulare congestione
Allora direi che la congestione l'hai simulata, non trovi?

se io eliminassi la limitazione dal mio file me la ritroverei comunque.
Dipende...spedendo 10 byte alla volta non simuli esattamente la presenza di ritardi, ma generi un tcp flood, un fenomeno diverso che in caso di congestione ha effetti ancora peggiori.

ma per curiosità sulla tua macchina, i pacchetti arrivano sempre in tempo?
e + o - quanti ms ci impiegano mediamente??
Arrivano quasi sempre in tempo. Questo perché lo stack tcp cerca di raggruppare assieme le scritture "piccole" prima di spedire il pacchetto. Se per qualche motivo non lo fa, le latenze aumentano, ma qui da me accade abbastanza di rado. Credo sia lo stesso fenomeno che osservi qui:

non mi spiego come mai a volte capiti che tutto va a buon fine e a volte invece va pianissimo....

luppolo
10-12-2007, 01:03
Allora direi che la congestione l'hai simulata, non trovi?
esatto, ma nonostante ciò e possibile avere performance migliori...
so per certo che altri ci siano riusciti... :)

Dipende...spedendo 10 byte alla volta non simuli esattamente la presenza di ritardi, ma generi un tcp flood, un fenomeno diverso che in caso di congestione ha effetti ancora peggiori.
anche qui hai perfettamente ragione...
però d'altro canto questa anomalia la possiede anche l'applicativo che ritarda...
questo è una errore della simulazione di quello che dovrebbe essere una vera rete congestionata...
ma anche questo fa parte del gioco ed è un'aspetto delle specifiche che non posso cambiare purtroppo...
se potessi usare pacchetti anche solo di 200byte non avrei nessunissimo problema, ma non posso :(

Questo perché lo stack tcp cerca di raggruppare assieme le scritture "piccole" prima di spedire il pacchetto. Se per qualche motivo non lo fa, le latenze aumentano, ma qui da me accade abbastanza di rado.
In realtà non li raggruppa :)
glielo dico io di non farlo con questo setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char *)&OptVal, sizeof(OptVal)) che disabilita l'algoritmo di nagle....pensavo di far bene dato che comunque i pacchetti erano piccoli per imposizione non volevo aggiungere inutilmente il lavoro di raggrupparli...
dici che è meglio se lo riabilito questo Negle??

ilsensine
10-12-2007, 10:47
Credo però che così generi delle latenze ben poco prevedibili. Con il tcp flood poi diventi soggetto a qualsiasi comportamento o luna (ben poco deterministico) del sistema. TCP_NODELAY, a meno che non devi trasmettere in effetti pochi byte, non lo vedo appropriato.

Un pò più di controllo lo avresti decidendo un delay tra quando il pacchetto arriva a quando viene ritrasmesso; puoi quindi simulare la congestione regolando questo delay (che può anche avere una forma statistica). Almeno così generi un risultato riproducibile.

luppolo
13-12-2007, 16:21
Ciao di nuovo,
scusa il ritardo, ma negli ultimi 3 gg ho lavorato fuori sede, e non ho avuto modo di seguire questa discussione.

Oggi ho applicato tutti i tuoi suggerimenti, ho riattivato l'algoritmo di nagle, ho ho migliorato il codice del programma per eliminare sprechi di CPU, ho messo a posto alcuni cicli che spesso andavano a vuoto, ho eliminato un po' di fflush inutili, ho eliminato il polling sulle select e via discorrendo....
e devo dire che avevi ragione nell'imputare la causa del problema al sovraccarico di lavoro non necessario, piuttosto che alla trasmissione vera e propria dei dati...

io davo per scontato che comunque quelli fossero calcoli talmente veloci da non pregiudicare le performance generali.....
ne ero così convinto che non avrei mai fatto test in quella direzione...

che dire?
grazie di nuovo per l'aiuto..

Luppolo.

Ps: un ultimo appunto, forse leggermete OT, sai spiegarmi cosa significano questo codice?

FD_SET(0,&all);
if( FD_ISSET(0,&rdset) )

su internet ho trovato diversi documenti in cui era usato, ma nessun commento a riguardo..
non capisco a quale file descriptor è associato lo 0...
forse rappresenta un fd speciale??
Grazie :confused:

ilsensine
13-12-2007, 21:50
Sui sistemi unix, l'fd 0 in genere è lo stdin. Su windows non so...