PDA

View Full Version : [C/VB] perdita di pacchetti


misterx
09-09-2013, 09:13
ciao,
mi interessava capire quali sono le cause principali della perdita di pacchetti a livello applicativo. Il protocollo TCP/IP garantisce che tutti i pacchetti vengono inviati/ricevuti da due macchine in rete ma nel caso in cui una macchina client fatica ad elaborare i pacchetti in arrivo per ragioni a me ancora ignote, dove finiscono tali pacchetti?

Faccio un esempio:
ho due thread i quali inviano rispettivamente ogni 200 ms:

thread A
127 x 8 bit = 1016 bit
ogni bit è accompagnato da alcune informazioni utili a capire a chi appartiene tale bit, la lunghezza di ogi messaggio è quindi mediamente lunga 30 byte quindi:
1016 x 30 x 5 = 148,82 KB/s circa

thread B
4000 x 8 bit = 32000 bit
ogni bit è accompagnato da alcune informazioni utili a capire a chi appartiene tale bit, la lunghezza di ogi messaggio è quindi mediamente lunga 30 byte quindi:
32000 x 30 x 5 = 4687,5 KB/s circa


p.s.
ho editato e corretto i dati

Oceans11
09-09-2013, 11:12
ciao,
mi interessava capire quali sono le cause principali della perdita di pacchetti a livello applicativo. Il protocollo TCP/IP garantisce che tutti i pacchetti vengono inviati/ricevuti da due macchine in rete ma nel caso in cui una macchina client fatica ad elaborare i pacchetti in arrivo per ragioni a me ancora ignote, dove finiscono tali pacchetti?

Che intendi a livello applicativo? a livello "applicazione" del modello tcp/ip? (lvl 5,6 e 7 osi) oppure a livello di trasporto?

Faccio un esempio:
ho due thread i quali inviano rispettivamente ogni 200 ms:

thread A
127 x 8 bit x 3 = 3048 bit
ogni bit è accompagnato d alcune informazioni utili a capire a chi appartiene tale bit, la lunghezza di ogi messaggio è quindi mediamente lunga 30 byte quindi:
3048 x 30 = 89,29 KB circa

thread B
4000 x 8 bit x 3 = 96000 bit
ogni bit è accompagnato d alcune informazioni utili a capire a chi appartiene tale bit, la lunghezza di ogi messaggio è quindi mediamente lunga 30 byte quindi:
96000 x 30 = 2812,5 KB circa

Questa parte non mi è chiara, qual'è la domanda ed il nesso?
"ogni bit è accompagnato da alcune informazioni utili"
cioè altri bit??
un pò troppo overhead, non credi?

misterx
09-09-2013, 11:24
ciao,
siccome sto lavorando con le socket penso di trovarmi al livello 7 dove regnano ftp e compagnia bella :)

Mi sono accorto che con i miei conti sono stato poco chiaro; riassumendo ho nella pratica due cicli nidificati:

sorta di pseudocodice

int c,d,e,f;
char msg[1024];

for(int byte=0; byte < 4000; byte++)
{
for(int bit=0; bit<8;bit++) {
sprintf(msg"%d, %d, %d, %d %d, %d",byte,bit,c,d,e,f);
SendText(msg);
}
}
Sleep(200); // attende 200 ms prima di inviare i prossimi 'n' messaggi


dove msg è lungo mediamente 30 byte

Quindi: 4000 x 8 = 32000 byte ogni 200 ms, quindi ogni secondo:
32000 x 5 = 156,25 KB/s

Oceans11
09-09-2013, 11:43
ok, livello "applicazione" e usi le socket. Suppongo che tu stia usando SOCK_STREAM e non SOCK_DGRAM. Suppongo ancora che l'applicazione comunichi con un endpoint appartenente alla stessa LAN, così da escludere i problemi connessi ai router etc.
Allora puoi avere perdite di pacchetto per i seguenti motivi:

- la scheda di rete non riesce a smaltire i pacchetti in entrata abbastanza velocemente. (scenario quasi impossibile)

- il sistema operativo non riesce a smaltire i buffer del kernel (anche qui difficile che accada)

- lo switch non riesce a smaltire il traffico, i suoi buffer sono pieni.

in tutti questi casi i pacchetti vengono droppati, in tutti i casi il tcp/ip provvede al recovery. In più c'è da dire che le trame non vengono inviate dalla scheda di rete con la velocità con cui tu invii pacchetti tramite codice, il sistema operatvo intercede praticamente sempre. E poi esiste anche la "finestra" del tcp.

misterx
09-09-2013, 11:58
ok, livello "applicazione" e usi le socket. Suppongo che tu stia usando SOCK_STREAM e non SOCK_DGRAM. Suppongo ancora che l'applicazione comunichi con un endpoint appartenente alla stessa LAN, così da escludere i problemi connessi ai router etc.
Allora puoi avere perdite di pacchetto per i seguenti motivi:

- la scheda di rete non riesce a smaltire i pacchetti in entrata abbastanza velocemente. (scenario quasi impossibile)

- il sistema operativo non riesce a smaltire i buffer del kernel (anche qui difficile che accada)

- lo switch non riesce a smaltire il traffico, i suoi buffer sono pieni.

in tutti questi casi i pacchetti vengono droppati, in tutti i casi il tcp/ip provvede al recovery. In più c'è da dire che le trame non vengono inviate dalla scheda di rete con la velocità con cui tu invii pacchetti tramite codice, il sistema operatvo intercede praticamente sempre. E poi esiste anche la "finestra" del tcp.

si, sto usando le socket; l'applicativo client e server girano sulla stessa macchina, quindi nessuno switch o router a complicare le cose. Il server riceve dati da una macchina via ethernet direttamente connessa il quale, una volta ricevuti, li impacchetta e li invia all'applicativo client scritto in visual basic.
Ho notato però che i pacchetti ricevuti dal client, a volte sono lunghi 1460 byte, ed a volte solo 1 byte. Ogni pacchetto ricevuto dal client scatena un evento; sembra quasi che siano i troppi eventi a causare la perdita di pacchetti e quindi vengono scartati.

Ho notato anche che se porto il tempo da 200 ms a 50 ms l'applicativo in VB non è più governabile; potrebbe essere anche un limite del linguaggio, anche se ho più di qualche dubbio?

Considerando la relativa quantità di dati che invio al client a mio avviso dovrebbe funzionare tutto tranquillamente.

Oceans11
09-09-2013, 12:07
non conosco affatto vb, ma forse è un problema di applicazione.
se scateni un evento per pacchetti di un byte è ovvio che carichi parecchio l'applicazione così da non riuscire a star dietro ai pacchetti ricevuti. Ad ogni modo, a livello di codice, se ovviamente non fai errori, non hai alcuna perdita. Puoi comunque causarla ai buffer del kernel, scheda di rete etc.

misterx
09-09-2013, 12:11
non conosco affatto vb, ma forse è un problema di applicazione.
se scateni un evento per pacchetti di un byte è ovvio che carichi parecchio l'applicazione così da non riuscire a star dietro ai pacchetti ricevuti. Ad ogni modo, a livello di codice, se ovviamente non fai errori, non hai alcuna perdita. Puoi comunque causarla ai buffer del kernel, scheda di rete etc.


però temo che i pacchetti da 1 byte vengano deciso dal sistema operativo in quanto io a livello applicativo invio messaggi completi.

Oceans11
09-09-2013, 12:17
però temo che i pacchetti da 1 byte vengano deciso dal sistema operativo in quanto io a livello applicativo invio messaggi completi.

non so, mi pare proprio strano. Di solito capita che crei ed invii un pacchetto troppo piccolo che il SO aspetta ad inviarlo e lo unisce ad altri.

misterx
09-09-2013, 14:03
non so, mi pare proprio strano. Di solito capita che crei ed invii un pacchetto troppo piccolo che il SO aspetta ad inviarlo e lo unisce ad altri.

sto indagando e pare che il problema sia lato server.
Ho del tutto tralasciato il fatto di verificare quanti dati sono stati realmente inviati attraverso la SendText().
La documentazione dice che è possibile capire quanti dati sono stati inviati ma non mi è ancora chiaro come gestire il problema.

Per lo sviluppo uso Borland builder.

Oceans11
09-09-2013, 15:05
se vuoi un consiglio spassionato, apri 2 istanze di wireshark e cattura il traffico del server e del client, così puoi vedere esattamente cosa sta succedendo.

Cos'è SendText()?l'hai scritta tu?pensavo fosse una funzione di libreria ma non la trovo in rete.

misterx
09-09-2013, 15:18
se vuoi un consiglio spassionato, apri 2 istanze di wireshark e cattura il traffico del server e del client, così puoi vedere esattamente cosa sta succedendo.

Cos'è SendText()?l'hai scritta tu?pensavo fosse una funzione di libreria ma non la trovo in rete.

SendText() è un metodo di un componente di Borland.

Ho provato come suggerito dalla lettura della documentazione una cosa del tipo:


int test;
test = SendText(buffer); // lunghezza dei dati inviati

if(test < strlen(buffer)) VisualizzaMessaggio("Problema");


dopo centinaia di migliaia di invii l'if non è mai vero, il che significa che viene inviato tutto correttamente. Ho scoperto che di default le socket sono del tipo bloccante.

Oceans11
09-09-2013, 21:13
SendText() è un metodo di un componente di Borland.

Ho provato come suggerito dalla lettura della documentazione una cosa del tipo:


int test;
test = SendText(buffer); // lunghezza dei dati inviati

if(test < strlen(buffer)) VisualizzaMessaggio("Problema");


dopo centinaia di migliaia di invii l'if non è mai vero, il che significa che viene inviato tutto correttamente. Ho scoperto che di default le socket sono del tipo bloccante.

Guarda, insisto nel consigliarti uno sniffer di rete per testare l'applicazione, da cui riesci anche a vedere il numero di pacchetti inviati (bastano una manciata di messaggi, non ne servono centinaia) e come il SO. li raggruppa. Ricorda che non è vero che ad ogni chiamata a send() corrisponde un pacchetto.
Per il resto non so autarti, non conosco VB, aspettiamo che passa qualcuno ferrato.

misterx
09-09-2013, 22:05
Guarda, insisto nel consigliarti uno sniffer di rete per testare l'applicazione, da cui riesci anche a vedere il numero di pacchetti inviati (bastano una manciata di messaggi, non ne servono centinaia) e come il SO. li raggruppa. Ricorda che non è vero che ad ogni chiamata a send() corrisponde un pacchetto.
Per il resto non so autarti, non conosco VB, aspettiamo che passa qualcuno ferrato.

grazie, è una soluzione che farò domani anche se mi crea un problema in quanto dovrei installare lo sniffer su una macchina remota.

Quello che dici lo avevo notato, avevo provato a contare ogni send lato server ed ogni gettext lato client ed ho notato che non esiste un rapporto 1:1.
Ora stavo leggendo anche che la SendText() ritorna il numero di caratteri che è riuscita a scrivere sulla socket, per questo motivo avevo effettuato il test sopra. Mi è parso anche di capire che sto usando una socket bloccante.

Comunque domani provo a sniffare quanto arriva sulla scheda del client; grazie ancora. Se non ricordo male wireshark esiste anche in versione portable.

misterx
10-09-2013, 18:21
rettifico, avendo poca destrezza con l'uso dei componenti di bcb ho scoperto che stavo usando il componente TServerSocket in modalità NON bloccante ed in questo modo usando Putty ad esempio, perdevo dati.
Dopo aver settato la socket bloccante i dati arrivano tutti.

Anche dopo aver letto diversi articoli in rete non mi è del tutto chiara la differenza tra socket bloccante e non.

Oceans11
11-09-2013, 01:20
rettifico, avendo poca destrezza con l'uso dei componenti di bcb ho scoperto che stavo usando il componente TServerSocket in modalità NON bloccante ed in questo modo usando Putty ad esempio, perdevo dati.
Dopo aver settato la socket bloccante i dati arrivano tutti.

Anche dopo aver letto diversi articoli in rete non mi è del tutto chiara la differenza tra socket bloccante e non.

Bloccante e non si riferisce alla funzione di lettura dei dati dalla socket (read).
Quando è bloccante (in genere è il comportamento di default) tale chiamata a funzione sospende l'esecuzione del programma finchè non sono stati ricevuti dati (quanti dipende dal SO) in ingresso sulla socket.
Non bloccante vuol dire che la funzione ritorna immediatamente, sia nel caso in cui sono stati letti dei dati sia che non sia arrivato niente. Anzi, in generale, i dati devono ancora arrivare quando la funzione ritorna. Questa modalità viene utilizzata in applicazioni parallele dove il controllo è critico.

Usa la socket bloccante ;)

misterx
11-09-2013, 06:56
Bloccante e non si riferisce alla funzione di lettura dei dati dalla socket (read).
Quando è bloccante (in genere è il comportamento di default) tale chiamata a funzione sospende l'esecuzione del programma finchè non sono stati ricevuti dati (quanti dipende dal SO) in ingresso sulla socket.
Non bloccante vuol dire che la funzione ritorna immediatamente, sia nel caso in cui sono stati letti dei dati sia che non sia arrivato niente. Anzi, in generale, i dati devono ancora arrivare quando la funzione ritorna. Questa modalità viene utilizzata in applicazioni parallele dove il controllo è critico.

Usa la socket bloccante ;)

nel mio caso specifico però mi è ancora poco chiaro. Ho un processo server nel quale ho implementato un thread che produce dati. Tale processo server rimane in ascolto su una certa porta; nel momeno in cui un client si collega al server, questo inizia ad impacchettare ed inviare dati. Per l'invio uso la funzione SendText() la quale consente, attraverso un valore di ritorno, di tenere sotto controllo il numero di byte inviati per ogni chiamata della stessa. Non è mai accaduto che il numero di byte inviati sia inferiore alla dimensione del buffer che io passo a tale funzione, questo mi portava a pensare che il mio processo server inviasse sempre al client tutti i dati presenti nel buffer ed invece non era così.
Settando la proprietà bloccante le cose sembrano essersi sistemate ma rimane per me ancora un mistero.

Essendo il server implementato attraverso un thread mi è chiaro che a livello di interfaccia non mi accorgo che il codice in esso viene bloccato sino alla ultimazione della SendText(). Ho letto ogni tipo di manuale di BCB ed ancora non ne sono venuto a capo, mi piacerebbe riuscire a trattare anche la socket non bloccante.

ciao

misterx
13-09-2013, 18:11
oggi ho sperimentato la limitazione delle socket bloccanti; se due client si collegano al server ed uno dei due si blocca per una qualche ragione, anche all'altro client non arrivano più dati

Oceans11
13-09-2013, 19:33
oggi ho sperimentato la limitazione delle socket bloccanti; se due client si collegano al server ed uno dei due si blocca per una qualche ragione, anche all'altro client non arrivano più dati

Se si blocca il server! scusa ma stai usando un server multithread, giusto?

misterx
13-09-2013, 20:21
Se si blocca il server! scusa ma stai usando un server multithread, giusto?

il server implementa un thread che cicla all'infinito. Quando un client si collega riceve i pacchetti dallo stesso thread.

Oceans11
13-09-2013, 23:49
il server implementa un thread che cicla all'infinito. Quando un client si collega riceve i pacchetti dallo stesso thread.

A me sta cosa non torna. Guarda, non ci metterei la mano sul fuoco, ma mi sembra che non funzioni così. Cerco di spiegarti quello che credo sia il funzionamento:

se il server usa un solo thread (quello principale del processo) si bloccherà sulla accept() in attesa di un client. Quando un client si connette, il server "ritorna" dalla accept e comincia la comunicazione. Anche utilizzando la solita struttura con il ciclo infinito:


int client = -1;
while (client = accept(server, (struct sockaddr *) &address, &address_len)) != -1)
{
... tuo protocollo
}


quello che succede è che di client ne viene servito solo uno alla volta, gli altri si mettono in coda, coda lunga in funzione del secondo parametro della listen() e del sistema operativo in uso.

In sintesi 2 o più client senza server multithread non possono essere serviti contemporaneamente.

marco.r
13-09-2013, 23:52
nel mio caso specifico però mi è ancora poco chiaro. Ho un processo server nel quale ho implementato un thread che produce dati. Tale processo server rimane in ascolto su una certa porta; nel momeno in cui un client si collega al server, questo inizia ad impacchettare ed inviare dati. Per l'invio uso la funzione SendText() la quale consente, attraverso un valore di ritorno, di tenere sotto controllo il numero di byte inviati per ogni chiamata della stessa. Non è mai accaduto che il numero di byte inviati sia inferiore alla dimensione del buffer che io passo a tale funzione, questo mi portava a pensare che il mio processo server inviasse sempre al client tutti i dati presenti nel buffer ed invece non era così.
Settando la proprietà bloccante le cose sembrano essersi sistemate ma rimane per me ancora un mistero.

Essendo il server implementato attraverso un thread mi è chiaro che a livello di interfaccia non mi accorgo che il codice in esso viene bloccato sino alla ultimazione della SendText(). Ho letto ogni tipo di manuale di BCB ed ancora non ne sono venuto a capo, mi piacerebbe riuscire a trattare anche la socket non bloccante.

ciao
Quando scrivi su un socket in modalita' non bloccante, se non ce' spazio nel buffer di invio per immagazzinare i dati, la chiamata ritorna subito con una notifica della cosa (il come lo notifichi il Borlan C++ Builder non ne ho idea...); la cosa parrebbe confermata dal fatto che ogni tanto lato client ti trovi con messaggi piu' corti. Come fai a verificare il numero di byte inviati con la chiamata ? Ho paura che sbagli qualcosa li'. A maggior ragione se in modalita' bloccante i conti tornano.

marco.r
13-09-2013, 23:54
oggi ho sperimentato la limitazione delle socket bloccanti; se due client si collegano al server ed uno dei due si blocca per una qualche ragione, anche all'altro client non arrivano più dati

Anche a me sfugge qualcosa.... non e' che per caso apri un socket nuovo per ogni pacchetto che vuoi leggere ? ...

misterx
14-09-2013, 07:17
A me sta cosa non torna. Guarda, non ci metterei la mano sul fuoco, ma mi sembra che non funzioni così. Cerco di spiegarti quello che credo sia il funzionamento:

se il server usa un solo thread (quello principale del processo) si bloccherà sulla accept() in attesa di un client. Quando un client si connette, il server "ritorna" dalla accept e comincia la comunicazione. Anche utilizzando la solita struttura con il ciclo infinito:


int client = -1;
while (client = accept(server, (struct sockaddr *) &address, &address_len)) != -1)
{
... tuo protocollo
}


quello che succede è che di client ne viene servito solo uno alla volta, gli altri si mettono in coda, coda lunga in funzione del secondo parametro della listen() e del sistema operativo in uso.

In sintesi 2 o più client senza server multithread non possono essere serviti contemporaneamente.

se non ho capito male quello che vuoi dire è proprio quello che accade; se un client entra in crisi in quanto non riesce ad elaboarare tutti i pacchetti che gli arrivano blocca anche gli altri che stanno ricevendo pacchetti. Una volta spento il client bloccante la trasmissione continua.

Spiego il programma con un codice di esempio ma molto fedelle alla mia implementazione, allo start del server si ha un thread che cicla all'infinito:


DWORD WINAPI ThreadGeneratore( LPVOID lpParameter)
{
char msg[512];

while(!bAbort) // ciclo infinito o fino a bAbort=true
{
for(int byte = 0; byte < 1000; ++byte)
{
for(int bit = 0; bit < 8; ++bit)
{
sprintf(msg, "%d, %d, %d, %d %d, %d", byte, bit, c, d, e, f);
SpedisciPacchetto( msg);
}
}
Sleep(200); // attendo 200 ms prima di inviare nuovi dati
}
}


void SpedisciPacchetto(char *msg)
{
// il ciclo for invia dati a tutti i client connessi
try{
for(int actconn = 0; actconn < Form1->ServerSocket3->Socket->ActiveConnections; actconn++) {
Form1->ServerSocket3->Socket->Connections[actconn]->SendText(msg); }
} catch(...) { }
}

misterx
14-09-2013, 07:30
Quando scrivi su un socket in modalita' non bloccante, se non ce' spazio nel buffer di invio per immagazzinare i dati, la chiamata ritorna subito con una notifica della cosa (il come lo notifichi il Borlan C++ Builder non ne ho idea...); la cosa parrebbe confermata dal fatto che ogni tanto lato client ti trovi con messaggi piu' corti. Come fai a verificare il numero di byte inviati con la chiamata ? Ho paura che sbagli qualcosa li'. A maggior ragione se in modalita' bloccante i conti tornano.

ho approfondito il problema e per la socket non bloccante posso usare la funzione windows WSAGetLastError(); che mi dice se c'è stato un errore. Difatti viene tornato l'errore 10035 ( http://msdn.microsoft.com/en-us/library/windows/desktop/ms740668(v=vs.85).aspx ) risorsa temporaneamente non disponibile. A questo punto, se ho capito come funziona il mondo delle socket, una volta appreso del problema dovrei inviare nuovamente i dati sino allo svuotamento del buffer; svuotamento che penso dovrei gestire io attraverso un puntatore che eviti di inviare nuovamente la medesima porzione di dati. E' una mia ipotesi non verificata.

Comunque lato client avvengonon le cose che citi tu.

misterx
14-09-2013, 07:33
Anche a me sfugge qualcosa.... non e' che per caso apri un socket nuovo per ogni pacchetto che vuoi leggere ? ...

è aperto un solo socket in ascolto.

Oceans11
14-09-2013, 11:06
ho approfondito il problema e per la socket non bloccante posso usare la funzione windows WSAGetLastError(); che mi dice se c'è stato un errore. Difatti viene tornato l'errore 10035 ( http://msdn.microsoft.com/en-us/library/windows/desktop/ms740668(v=vs.85).aspx ) risorsa temporaneamente non disponibile. A questo punto, se ho capito come funziona il mondo delle socket, una volta appreso del problema dovrei inviare nuovamente i dati sino allo svuotamento del buffer; svuotamento che penso dovrei gestire io attraverso un puntatore che eviti di inviare nuovamente la medesima porzione di dati. E' una mia ipotesi non verificata.

Comunque lato client avvengonon le cose che citi tu.

Per la socket non bloccante, l'errore è la normalità; voglio dire che la funzione ritorna sempre e comunque quando la chiami, anche quando la socket non è pronta (perchè ancora nessun client si è connesso o perchè ancora non ti è possibile leggere dal canale).
Quindi, direi che devi fare "polling" delle client-socket per vedere a che punto stanno e, a quelle disponibili, invii i pacchetti.
Una funzione meno cpu-intensive del polling attivo è la select().

ps: continuo a suggerirti la socket bloccante, per me è tanto più semplice.

marco.r
14-09-2013, 11:33
ho approfondito il problema e per la socket non bloccante posso usare la funzione windows WSAGetLastError(); che mi dice se c'è stato un errore. Difatti viene tornato l'errore 10035 ( http://msdn.microsoft.com/en-us/library/windows/desktop/ms740668(v=vs.85).aspx ) risorsa temporaneamente non disponibile. A questo punto, se ho capito come funziona il mondo delle socket, una volta appreso del problema dovrei inviare nuovamente i dati sino allo svuotamento del buffer; svuotamento che penso dovrei gestire io attraverso un puntatore che eviti di inviare nuovamente la medesima porzione di dati. E' una mia ipotesi non verificata.

No e' proprio il funzionamento dei socket non bloccanti.
La chiamata ritorna sempre ,a prescindere da quanti dati sono stati spediti e/o sono stati spediti. E' tuo compito ogni volta controllare quanta roba e' stata spedita.
Ha senso principalmente quando devi spedire su piu' socket dallo stesso processo o thread. In caso contrario (1 thread per 1 socket) e' piu' una scomodita' e ti conviene usare il socket bloccante.

marco.r
14-09-2013, 11:41
void SpedisciPacchetto(char *msg)
{
// il ciclo for invia dati a tutti i client connessi
try{
for(int actconn = 0; actconn < Form1->ServerSocket3->Socket->ActiveConnections; actconn++) {
Form1->ServerSocket3->Socket->Connections[actconn]->SendText(msg); }
} catch(...) { }
}


scusa solo ora ho visto questo pezzo di codice
quindi hai un singolo thread che gestisce tutti i client.
Il discorso del socket non bloccante allora va bene; devi pero' decidere cosa fare quando un client non puo' ricevere un pacchetto. Nel caso di flusso di dati la cosa piu' ragionevole e' "buttare via" il pacchetto per quel client; tanto ora che si libera probabilmente ci saranno gia' nuovi dati da inviarli.

misterx
14-09-2013, 12:56
Per la socket non bloccante, l'errore è la normalità; voglio dire che la funzione ritorna sempre e comunque quando la chiami, anche quando la socket non è pronta (perchè ancora nessun client si è connesso o perchè ancora non ti è possibile leggere dal canale).
Quindi, direi che devi fare "polling" delle client-socket per vedere a che punto stanno e, a quelle disponibili, invii i pacchetti.
Una funzione meno cpu-intensive del polling attivo è la select().

ps: continuo a suggerirti la socket bloccante, per me è tanto più semplice.

in un certo senso la socket bloccante semplifica un pò le cose ma ha il limite che ho esposto. L'alternativa sarebbe un thread per client con socket bloccante, il problema è che sto usando un mixi di API e classi di Borland Builder e mi sto forse complicando la vita non poco.
Gestire n connessioni e i dati persi con le socket non bloccanti per me sarebbe un massacro.

misterx
14-09-2013, 12:59
scusa solo ora ho visto questo pezzo di codice
quindi hai un singolo thread che gestisce tutti i client.
Il discorso del socket non bloccante allora va bene; devi pero' decidere cosa fare quando un client non puo' ricevere un pacchetto. Nel caso di flusso di dati la cosa piu' ragionevole e' "buttare via" il pacchetto per quel client; tanto ora che si libera probabilmente ci saranno gia' nuovi dati da inviarli.

potrei anche decidere di buttare il pacchetto ma nel momento in cui il client si blocca per un qualche motivo il server si blocca. Dovrei aggiungere una sorta di controllo che eviti tale situazione in modo che ad esempio il secondo client connesso continui a ricevere dati.

p.s.
se riuscissi a non perdere nulla sarebbe l'ideale

marco.r
14-09-2013, 13:47
potrei anche decidere di buttare il pacchetto ma nel momento in cui il client si blocca per un qualche motivo il server si blocca. Dovrei aggiungere una sorta di controllo che eviti tale situazione in modo che ad esempio il secondo client connesso continui a ricevere dati.

p.s.
se riuscissi a non perdere nulla sarebbe l'ideale

Se usi i socket non bloccanti e' impossibile che un client blocchi gli altri; perche' la chiamata ritorna SEMPRE subito (mentre con il socket bloccante il client, se il server si blocca e' per qualche altro motivo.
Semmai devi decidere che politica adottare nel caso un socket ritorni errore perche' non puo' aggiungere dati.
In questo caso dovresti teneer una coda (per ogni client) con i pacchetti non ancora spediti e riprovare il passaggio successivo.
O in alternativa, se i client non sono molti, passi ad un approccio 1 client = 1 thread con socket bloccante

misterx
14-09-2013, 15:44
Se usi i socket non bloccanti e' impossibile che un client blocchi gli altri; perche' la chiamata ritorna SEMPRE subito (mentre con il socket bloccante il client, se il server si blocca e' per qualche altro motivo.
Semmai devi decidere che politica adottare nel caso un socket ritorni errore perche' non puo' aggiungere dati.
In questo caso dovresti teneer una coda (per ogni client) con i pacchetti non ancora spediti e riprovare il passaggio successivo.
O in alternativa, se i client non sono molti, passi ad un approccio 1 client = 1 thread con socket bloccante

a parte che ho letto che anche con le socket bloccanti si possono perdere dati, ma questa è un'altra storia che risolverò.

Studierò il metodo 1 client 1 thread

marco.r
14-09-2013, 15:59
a parte che ho letto che anche con le socket bloccanti si possono perdere dati, ma questa è un'altra storia che risolverò.

Studierò il metodo 1 client 1 thread
E' una soluzione decisamente piu' semplice se hai poche decine di client; il costo e' un maggior consumo di memoria (perche' ogni thread ha bisogno della sua memoria).
Se i socket sono TCP mi sembra strano il discorso del perdere dati . Puo' essere che non venga spedito l'intero pacchetto e allora in tal caso ti viene solitamente indicato in qualche modo.

misterx
15-09-2013, 11:33
edit

lorenzo001
15-09-2013, 11:50
Con TCP non si possono perdere dati senza che la cosa non ti venga segnalata con qualche messaggio d'errore (o valore d'errore restituito dalle funzioni chiamate).

misterx
15-09-2013, 12:08
Con TCP non si possono perdere dati senza che la cosa non ti venga segnalata con qualche messaggio d'errore (o valore d'errore restituito dalle funzioni chiamate).

grazie anche a te ma ne abbiamo già discusso :)

lorenzo001
15-09-2013, 12:11
Di nulla ... ma se dici che si possono perdere pacchetti ...

misterx
16-09-2013, 07:10
No e' proprio il funzionamento dei socket non bloccanti.
La chiamata ritorna sempre ,a prescindere da quanti dati sono stati spediti e/o sono stati spediti. E' tuo compito ogni volta controllare quanta roba e' stata spedita.
Ha senso principalmente quando devi spedire su piu' socket dallo stesso processo o thread. In caso contrario (1 thread per 1 socket) e' piu' una scomodita' e ti conviene usare il socket bloccante.

intendi dire che il socket bloccante impedisce l'uso della risorsa (porta) ad un altro thread concorrente?

misterx
16-09-2013, 09:39
DWORD WINAPI ThreadGeneratore( LPVOID lpParameter)
{
char msg[512];

while(!bAbort) // ciclo infinito o fino a bAbort=true
{
for(int byte = 0; byte < 1000; ++byte)
{
for(int bit = 0; bit < 8; ++bit)
{
sprintf(msg, "%d, %d;", byte, bit);
SpedisciPacchetto( msg);
}
}
Sleep(1000); // attendo 1000 s prima di inviare nuovi dati
}
}


ho usato l'evento OnClientConnect allo scopo di creare 1 thread per ogni client e sembra funzionare, non mi spiego però perchè all'aumentare del numero di client e quindi di thread aumenta la velocità di invio dei pacchetti; come se il tempo da me assegnato Sleep(1000) venisse suddiviso con i vari thread!!!

marco.r
17-09-2013, 14:25
DWORD WINAPI ThreadGeneratore( LPVOID lpParameter)
{
char msg[512];

while(!bAbort) // ciclo infinito o fino a bAbort=true
{
for(int byte = 0; byte < 1000; ++byte)
{
for(int bit = 0; bit < 8; ++bit)
{
sprintf(msg, "%d, %d;", byte, bit);
SpedisciPacchetto( msg);
}
}
Sleep(1000); // attendo 1000 s prima di inviare nuovi dati
}
}


ho usato l'evento OnClientConnect allo scopo di creare 1 thread per ogni client e sembra funzionare, non mi spiego però perchè all'aumentare del numero di client e quindi di thread aumenta la velocità di invio dei pacchetti; come se il tempo da me assegnato Sleep(1000) venisse suddiviso con i vari thread!!!
Questo non dovrebbe succedere, pero' ora che le spedizioni avvengono su thread separati i pacchetti possono venire "sparati" in istanti diversi.

marco.r
17-09-2013, 14:27
Questo detto, riguardando il tuo codice iniziale,
spero che il mandare un burst di 8000 pacchetti ad ogni client (ogni 200 ms) non sia troppo fedele al codice che stai scrivendo ;)

misterx
17-09-2013, 16:00
Questo detto, riguardando il tuo codice iniziale,
spero che il mandare un burst di 8000 pacchetti ad ogni client (ogni 200 ms) non sia troppo fedele al codice che stai scrivendo ;)

cosa intendi dire?

Storti
17-09-2013, 16:29
cosa intendi dire?
Credo intenda dire che se i messaggi sono lunghi 30 byte (come ipotizzato) stai occupando una banda teorica di 9.6Mb/s per ogni client...

marco.r
17-09-2013, 16:32
cosa intendi dire?

I tuoi pacchetti sono grandi 30 bytes.
A questo ci devi pero' aggiungere lóverhead dovuto al pacchetto IP e Frame Ethernet. Vedi ad esempio qua.
http://wiki.networksecuritytoolkit.org/nstwiki/index.php/LAN_Ethernet_Maximum_Rates,_Generation,_Capturing_%26_Monitoring#Ethernet_Maximum_Rates
In sostanza l'overhead e' ti circa 84 bytes per pacchetto (contanto anche il tempo che deve passare tra un pacchetto e il successivo).
Visto che lavori in TCP aggiungi anche anche il pacchetto ACK di risposta (altri 84 bytes).
Questo vuol dire che per ogni bit che mandi "consumi" circa 200 byte di banda ethernet

32000 * 200 * 5 = 32MB/s per client.

Questo vuol dire che con 3 client saturi la banda di una 10/100.
Con 30 client saturi una Gigabit.
Questo senza tenere conto di eventuali collisioni, errori di rete, altre macchine che trasmettono sullo stesso segmento o anche solo la presenza di switch sulla rete.

Potrebbe essere che i rallentamenti di cui parli siano dovuti semplicemente alla rete intasata.

misterx
17-09-2013, 17:02
I tuoi pacchetti sono grandi 30 bytes.
A questo ci devi pero' aggiungere lóverhead dovuto al pacchetto IP e Frame Ethernet. Vedi ad esempio qua.
http://wiki.networksecuritytoolkit.org/nstwiki/index.php/LAN_Ethernet_Maximum_Rates,_Generation,_Capturing_%26_Monitoring#Ethernet_Maximum_Rates
In sostanza l'overhead e' ti circa 84 bytes per pacchetto (contanto anche il tempo che deve passare tra un pacchetto e il successivo).
Visto che lavori in TCP aggiungi anche anche il pacchetto ACK di risposta (altri 84 bytes).
Questo vuol dire che per ogni bit che mandi "consumi" circa 200 byte di banda ethernet

32000 * 200 * 5 = 32MB/s per client.

Questo vuol dire che con 3 client saturi la banda di una 10/100.
Con 30 client saturi una Gigabit.
Questo senza tenere conto di eventuali collisioni, errori di rete, altre macchine che trasmettono sullo stesso segmento o anche solo la presenza di switch sulla rete.

Potrebbe essere che i rallentamenti di cui parli siano dovuti semplicemente alla rete intasata.

grazie,
avevo fatto anch'io un pò di conti e quindi ho ridotto drasticamente il numero do pacchetti.
Ora sono a 1336 pacchetti da 30 byte ogni 100 ms. Facendo i conti ottengo 0,38 MB/s e realmente, cioè salvando i dati in arrivo direttamente su file 0,36 MB/s; valori ben lontani dal saturare una rete da 100 Mbit/s.

Provando il server con n client connessi a socket bloccante i pacchetti arrivano a tutti i client, però se per qualche ragione un client si blocca, si blocca anche l'invio dei pacchetti agli altri client; non ho ancora scoperto un metodo per evitare tale blocco.

Se durante il blocco termino il processo bloccato, l'invio/ricezione riprende normalmente.

Se uso la socket non bloccante tale problema non esiste, quello del blocco, ma ho perdita di pacchetti.

Storti
17-09-2013, 17:15
però se per qualche ragione un client si blocca, si blocca anche l'invio dei pacchetti agli altri client; non ho ancora scoperto un metodo per evitare tale blocco.
Probabilmente stai gestendo tutti i socket nello stesso thread. Usa un thread per ogni socket (cioè per ogni connessione/client).

misterx
17-09-2013, 18:24
Probabilmente stai gestendo tutti i socket nello stesso thread. Usa un thread per ogni socket (cioè per ogni connessione/client).

io creo una socket statica al lancio del server e poi ciclo le connessioni attive inviando pacchetti per intenderci:

ServerSocket1->Active;
ServerSocket1->port=1000;

creo il pacchetto e lo invio

for(int actconn = 0; actconn < ServerSocket1->Socket->ActiveConnections; actconn++)
Form1->ServerSocket3->Socket->Connections[actconn]->SendTex(msg);


Tu invece mi dici di istanziare n socket dinamicamente associate a thread anche'essi dinamici.
In questo caso, se il thread padre apre un handle di libreria, il thread figlio usa l'handle del thread padre o ne genera uno nuovo?

Storti
18-09-2013, 09:18
Tu invece mi dici di istanziare n socket dinamicamente associate a thread anche'essi dinamici.
No, dico di sfruttare l'evento OnGetThread per instanziare ogni thread che deve essere una classe derivata da TServerClientThread. A quel punto hai un thread per ogni connessione, mentre come stai facendo ora ne hai uno per tutte.

marco.r
18-09-2013, 09:27
Provando il server con n client connessi a socket bloccante i pacchetti arrivano a tutti i client, però se per qualche ragione un client si blocca, si blocca anche l'invio dei pacchetti agli altri client; non ho ancora scoperto un metodo per evitare tale blocco.

Se durante il blocco termino il processo bloccato, l'invio/ricezione riprende normalmente.

Se uso la socket non bloccante tale problema non esiste, quello del blocco, ma ho perdita di pacchetti.

Come dicevo sopra e' perfettamente normale che se usi un unico thread con N socket bloccanti ti trovi che un client possa bloccare gli altri, perche' se non riesci a scrivere su un socket resti fermo in attesa e non procedi con gli altri.
Le alternative come dicevo sono tre:

1 - Continui ad usare socket non bloccanti. Quando il socket ti dice che non riesce a scrivere ignori l'errore e scarti l'intero pacchetto. Questo ha senso se la perdita di un pacchetto non pregiudica il funzionamento del client (e.g.: ti interessa sapere il valore corrente e se perdi un pacchetto ti limiti ad aspettare il successivo)
2 - Continui ad usare socket non bloccanti. Ad ogni socket associ una coda di pacchetti per cui quando non riesci a scrivere perche' il client e' bloccato metti il pacchetto in coda. Ad ogni iterazione ovviamente prima di scrivere il pacchetto corrente devi provare con i vecchi.
3 - Utilizzi socket bloccanti con un thread per socket. Nello specifico anche un thread per accettare solo client. In questo caso il problema, al di la' delle risorse maggiori necessarie, e' capire come sincronizzare l'accesso ai dati da spedire (da dove li prendi).


In ogni caso se vuoi mantenere una frequenza elevata valuta almeno l'idea di mandare un byte intero alla volta (30*8 = 240 bytes)

misterx
18-09-2013, 10:57
No, dico di sfruttare l'evento OnGetThread per instanziare ogni thread che deve essere una classe derivata da TServerClientThread. A quel punto hai un thread per ogni connessione, mentre come stai facendo ora ne hai uno per tutte.

molto interessante, ma non ho una conoscenza così approfondita circa la programmazione ad oggetti in ambiente Borland Builder, chiaramente non è una scusa per non approfondire l'argomento

misterx
18-09-2013, 11:00
Come dicevo sopra e' perfettamente normale che se usi un unico thread con N socket bloccanti ti trovi che un client possa bloccare gli altri, perche' se non riesci a scrivere su un socket resti fermo in attesa e non procedi con gli altri.
Le alternative come dicevo sono tre:

1 - Continui ad usare socket non bloccanti. Quando il socket ti dice che non riesce a scrivere ignori l'errore e scarti l'intero pacchetto. Questo ha senso se la perdita di un pacchetto non pregiudica il funzionamento del client (e.g.: ti interessa sapere il valore corrente e se perdi un pacchetto ti limiti ad aspettare il successivo)
2 - Continui ad usare socket non bloccanti. Ad ogni socket associ una coda di pacchetti per cui quando non riesci a scrivere perche' il client e' bloccato metti il pacchetto in coda. Ad ogni iterazione ovviamente prima di scrivere il pacchetto corrente devi provare con i vecchi.
3 - Utilizzi socket bloccanti con un thread per socket. Nello specifico anche un thread per accettare solo client. In questo caso il problema, al di la' delle risorse maggiori necessarie, e' capire come sincronizzare l'accesso ai dati da spedire (da dove li prendi).


In ogni caso se vuoi mantenere una frequenza elevata valuta almeno l'idea di mandare un byte intero alla volta (30*8 = 240 bytes)

il punto 2 mi sembra il più interessante. Dovrei scoprire quale client è in difficolta anche se credo che posso evincerlo usando il ciclo


for(int actconn = 0; actconn < ServerSocket1->Socket->ActiveConnections; actconn++)
Form1->ServerSocket3->Socket->Connections[actconn]->SendTex(msg);


l'indice atconn dovrebbe informarmi sul client in difficoltà, anche se mi sembra un metodo un pò debole.

misterx
25-09-2013, 07:07
ciao,
necessito di un suggerimento; sto approfondendo la strada 1 clent 1 thread e socket bloccanti ma mi chiedo; se voglio evitare contese con codice di questo tipo:


void SpedisciPacchetto(char *msg)
{
// il ciclo for invia dati a tutti i client connessi
try{
for(int actconn = 0; actconn < Form1->ServerSocket3->Socket->ActiveConnections; actconn++) {
Form1->ServerSocket3->Socket->Connections[actconn]->SendText(msg); }
} catch(...) { }
}


la prima soluzione che mi viene in mente è implementare una funzione SpedisciPacchetto per ogni thread, per nulla efficiente o sbaglio?

Meglio incorporare il codice SpedisciPacchetto in ogni thread?

Oceans11
25-09-2013, 12:19
ciao,
necessito di un suggerimento; sto approfondendo la strada 1 clent 1 thread e socket bloccanti ma mi chiedo; se voglio evitare contese con codice di questo tipo:


void SpedisciPacchetto(char *msg)
{
// il ciclo for invia dati a tutti i client connessi
try{
for(int actconn = 0; actconn < Form1->ServerSocket3->Socket->ActiveConnections; actconn++) {
Form1->ServerSocket3->Socket->Connections[actconn]->SendText(msg); }
} catch(...) { }
}


la prima soluzione che mi viene in mente è implementare una funzione SpedisciPacchetto per ogni thread, per nulla efficiente o sbaglio?

Meglio incorporare il codice SpedisciPacchetto in ogni thread?

Secondo me c'è un problema a monte. Se ogni thread è connesso ad un solo client, ogni thread invia ad un solo client! Cioè sta funzione che cicla sui client deve "ciclare sui thread".
In pseudo codice ecco cosa dovrebbe fare il server:


while ( 1 ) {
if ( (client = accept(...) == -1 ) {
printf("errore connessione\n");
break;
}
printf("cliente connesso"):
if ( pthread_create(..., client_handler, ....) != 0 ) {
printf("errore creazione thread\n");
break;
}

}


dove client_handler è quella funzione che ogni thread al suo avvio richiama. Lì ci metti l'invio di 1 (o n) pacchetti al solo client connesso che quel thread gestisce.

misterx
26-09-2013, 19:17
Secondo me c'è un problema a monte. Se ogni thread è connesso ad un solo client, ogni thread invia ad un solo client! Cioè sta funzione che cicla sui client deve "ciclare sui thread".

non ho capito se stai suggerendo che i thread devono diventare in un certo senso il corpo della funzione

Già che ci sono vi chiedo lumi su una cosa dle genere

HANDLE threadproduttore;

threadproduttore=CreateThread(.................);

Ho provato a lanciaro n volte cioè:

threadproduttore=CreateThread(.................);
threadproduttore=CreateThread(.................);
threadproduttore=CreateThread(.................);

mi chiedo se un memoria vi sono 3 thread distinti che eseguono concorrentemente la funzione

void SpedisciPacchetto(char *msg)
{
// il ciclo for invia dati a tutti i client connessi
try{
for(int actconn = 0; actconn < Form1->ServerSocket3->Socket->ActiveConnections; actconn++) {
Form1->ServerSocket3->Socket->Connections[actconn]->SendText(msg); }
} catch(...) { }
}

misterx
28-09-2013, 11:38
nel frattempo vhi chiedo anche se avete mai usato questa funzione AllocateHWnd() e della sua utilità