Marco Giunio Silano
29-08-2006, 08:35
si giusto
tempo = (s - p)*1000;
Velocita = BytesRicevuti / tempo;
questa invece è la velocità di trasferimento
ma il modo di calcolare gli ms tramite FD_READ a te sembra strano o è una buona cosa?
La logica è giusta, sei però legato alle latenze dell'os. Quindi non sei veramente preciso, almeno, dipende dalla precisione che tu vuoi ottenere, se +/- 100ms per te è accettabile. Quando la periferica ha ricevuto un pacchetto completo, scatena un interrupt che viene rigirato più volte, fino a che l'os non inizia a schedulare questo evento. Quindi puoi capire che di tempo ne passa, specie se lo scheduler è impegnato. Pure gli interupt software sono rallentati.
Avevo eseguito la misura scrivendo istruzioni nella routine di interrupt, ma avevo un hw costruito da noi (Motorola Coldfire + uCLinux) , quindi potevo fare ciò che volevo. Misurando i colpi di clk fra un in e il successivo ero preciso al us, misura verificata con oscilloscopio "piantato" sull'hw. Ma le nostre erano esigenze estreme.
Se tu devi fare una misura di massima, credo che possa andare bene ciò che hai fatto.
La logica è giusta, sei però legato alle latenze dell'os. Quindi non sei veramente preciso, almeno, dipende dalla precisione che tu vuoi ottenere, se +/- 100ms per te è accettabile. Quando la periferica ha ricevuto un pacchetto completo, scatena un interrupt che viene rigirato più volte, fino a che l'os non inizia a schedulare questo evento. Quindi puoi capire che di tempo ne passa, specie se lo scheduler è impegnato. Pure gli interupt software sono rallentati.
Avevo eseguito la misura scrivendo istruzioni nella routine di interrupt, ma avevo un hw costruito da noi (Motorola Coldfire + uCLinux) , quindi potevo fare ciò che volevo. Misurando i colpi di clk fra un in e il successivo ero preciso al us, misura verificata con oscilloscopio "piantato" sull'hw. Ma le nostre erano esigenze estreme.
Se tu devi fare una misura di massima, credo che possa andare bene ciò che hai fatto.
ti ringrazio...
una cosa...(riformulo la domanda):
Non so se sei pratico di winsock TCP ecc ecc.
Ho sviluppato un netcode con winsock senza thread ne sui clients e ne sul server.
I modi di operare sono svariati ovvero si può costruire o in modo bloccante o non-bloccante o asincrono.
Ecco io ho scelto il modo asincrono lo puoi appunto vedere dall'evento di notifica FD_READ gli altri sono FD_WRITE FD_CLOSE ecc ecc.
In questo modo che ho scelto si ha la possibilità, appunto asincrona di gestire + client in contemporanea (ecco perchè ho scelto questo metodo).
Facendo i test in locale lancio il server e 3 client sul server uso una listbox dove stampo i valori dei tempi in ms del pacchetto inviato.
nell'evento FD_READ del server arrivano i pacchetti di 58 bytes dove con recv() leggo il pacchetto del client 1 poi il 2 e il client 3, prendo il tempo (come esposto sopra) e stampo sulla listbox i valori in ms.
I valori in ms sono buoni essi oscillano in un range di 16/60 ms.
la mia domanda del thread era sapere se era giusto calcolare gli ms nella routine FD_READ ovvero tra un pacchetto ricevuto e un'altro. Ecco questo intendevo sapere oppure c'era un'altro modo?
IL TEST valutato da me:
Innanzitutto mi sembra lento, non capisco se è la stampa nella listbox che rallenta mi fà sembrare tutto rallentato in ricezione.
In + nella listbox vedo i clients stampati in ordine all'inizio tipo:
nome 0 16 ms
nome 1 37 ms
nome 2 24 ms
... e così via
... poi tutto ad un tratto stampa così:
nome 0 38 ms
nome 0 45 ms
nome 0 33 ms
nome 0 22 ms
... e così via per parecchio dove sembra che gli altri 2 client l'1 e il 2 non inviino dati. Poi inizia a stampare il client 2 poi di nuovo tutti e 3 e poi di nuovo un client qualsiasi.
Spero che questo sotto sia uno spunto per una discussione:
Questo è quanto ho letto su google a riguardo la tecnica dell'asincrono che è scritta in inglese e ci ho capito poco:
The price you pay for using asynchronous sockets
There is one disadvantage when using asynchronous sockets. Say you decide
to send something to the computer you are connected with, and one
command after, you try to perform another operation on the socket, chances
are that the socket will return an error. The error is WSAEWOULDBLOCK
(error number 10035). The good news is that this error is not a fatal error,
but just means, "Try again later". The bad news is that in theory, you should
be testing for this error specifically and trying to perform your action until it
actually works and this error is no longer returned. Now this is annoying,
since you can test for errors by just saying if (socketaction(s)
==SOCKET_ERROR) (here, socketaction() doesn’t mean anything. It is just an
example). But WSAEWOULDBLOCK is an error also. That means you will have
to specially test (call WSAGetLastError()) if WSAEWOULDBLOCK occurs and
just repeatedly try again.
I have found a cheap way around this which is not 100% guaranteed to
work. When you perform your action and there was an error, test if
WSAEWOULDBLOCK was returned. If it was returned then just Sleep (750)
and try again. What’s so great about this? Well, you don’t have to loop,
instead you just delay and try once again. Chances are, that after 750
milliseconds, the socket will be ready to perform your action.
Questa in italiano e da quello che leggo mi sembra di capire che il modo non-bloccante asincrono sia il migliore ma se c'è una PENDENZA DI DATI che per un dato motivo deve essere ripetuta, per via dell'evento WSAEWOULDBLOCK che pare che non sia un errore, ma se si verifica questo evento c'è da aspettare un tot per il nuovo invio del pacchetto:
3.4.2 Modo Operativo Non-Bloccante
In riferimento all'esempio precedentemente introdotto, con il modo operativo
non-bloccante, ci troviamo nella situazione in cui, verificata l'indisponibilità
dell'utente cercato, si decida di interrompere la comunicazione e richiamare
successivamente. Chiaramente, tale operazione, se effettuata in maniera
continuativa, porta ad uno spreco di tempo e di risorse. Sarebbe, quindi,
meglio spaziare equamente le nuove chiamate, in maniera tale da poter, nel
frattempo, compiere altre azioni. In tal modo, inoltre. non si è vincolati al
telefono, potendolo quindi adoperare per altre chiamate.
Tutto ciò, è simile ad un'operazione socket non-bloccante : è necessario
verificare a turno (poll), il completamento delle operazioni, ma, tale verifica,
non deve essere effettuata troppo spesso per non sprecare le risorse di
sistema.
Nel nonblocking operation mode, una funzione Windows Sockets, appena
chiamata, restituisce immediatamente il suo valore di ritorno. In alcuni casi,
tale valore, indicherà un successo nell'esecuzione dell'operazione richiesta, in
altri casi, esso indicherà un fallimento. Però, nel modo operativo non-
bloccante, un fallimento non è necessariamente indice di un fatto negativo.
Infatti il valore di ritorno della funzione che ha fallito, che sarà un valore di
errore (ottenuto tramite una chiamata alla funzione WSAGetLastError()), può
essere pari alla costante WSAEWOULDBLOCK, che letteralmente
significa : "la funzione potrebbe essere bloccata, se ha da attendere fino al
completamento di un'operazione, prima di ritornare". In tal caso, possono
verificarsi due situazioni, in dipendenza della funzione chiamata. Nel primo
caso, il valore di ritorno WSAEWOULDBLOCK indica che la DLL WinSock ha
cominciato l'operazione, ma che, in questo momento, non è ancora stata
completata (cioè, l'operazione è, come si dice, pendente). In tal caso,
l'operazione verrà, eventualmente, completata successivamente, avendo
modo (vedi successivo par. 3.5) di rilevare il completamento della stessa.
Nella seconda situazione, invece, Il valore di ritorno già visto, indicherà che la
DLL WinSock ha tentato di eseguire la richiesta fatta, ma ciò non è risultato
attualmente possibile. In tale situazione, l'errore suddetto, indicherà il fatto
che sarà necessario effettuare, successivamente, una nuova chiamata alla
funzione di interesse.
Il problema, con il modo non-bloccante, è che un'applicazione, per portare a
termine un'operazione, o per rilevare il suo completamento, ha bisogno di
richiamare una funzione più e più volte. Tutto ciò, implica un notevole
sovraccarico del sistema, se il polling viene effettuata troppo spesso, oppure
influenza in maniera avversa le prestazioni dell'applicazione, se tale verifica
non viene effettuata sufficientemente spesso. Oltre a ciò, tale modalità
operativa, complica anche l'implementazione del codice necessario.
Il vantaggio, invece, con tale modo operativo, è quello di poter adoperare più
socket, che hanno simultaneamente delle operazioni pendenti. Molte
applicazioni, non-bloccanti, sono ibride, in quanto combinano l'uso delle
socket non-bloccanti con la funzione bloccante select(), per multiplexare, le
prime, in maniera efficiente.
3.4.3 Modo Operativo Asincrono
Considerando, ancora, l'esempio di partenza, con la modalità operativa
asincrona, ci ritroviamo nel terzo caso, in cui si è lasciato un messaggio alla
persona cercata, per essere richiamati. In un tale caso, si delega, quindi, ad
un altro, il compito di stabilire la comunicazione richiesta, avendo, nel
frattempo, la libertà di poter eseguire qualsiasi altra azione, incluso l'uso del
mezzo comunicativo per effettuare altre chiamate diverse. In quanto, se
accadesse che la persona incaricata, trovasse la nostra linea occupata, sarà,
ancora, suo compito quello di richiamare successivamente. L'asynchronous
operation mode, è non-bloccante, in quanto, la funzione chiamata ritorna
immediatamente, prima che l'azione richiesta sia stata completata. Ma, a
differenza del modo operativo non-bloccante regolare, il valore di ritorno
WSAEWOULDBLOCK indica che la DLL WinSock invierà un messaggio per
notificare il caso in cui un'operazione pendente è stata completata, o quando
un'operazione deve essere ritentata. Tutto ciò rende le operazioni molto più
efficienti, consentendo anche delle prestazioni molto elevate.
Risulta chiaro che, un tale modo operativo, è basato proprio sulle
caratteristiche dell'ambiente operativo Windows, di conseguenza il suo codice
sorgente non è compatibile con le Berkeley Sockets (che per tale modalità
operativa adoperano la funzione signal()). Tuttavia, la sua semplicità ed
efficienza, sono tali da rendere trascurabile tale mancanza di portabilità.
Come dicevamo, la modalità operativa asincrona si avvantaggia della natura,
basata sul sistema dei messaggi, dell'architettura Windows. Tale sistema è
basato sugli oggetti (vedi par. 4.-.-); gli oggetti comunicano tra di loro
passandosi dei messaggi. Tali messaggi, giungono agli oggetti in maniera
asincrona, e, per ogni messaggio ricevuto, l'oggetto reagirà o compiendo
un'azione specifica o inviando, sempre in maniera asincrona, dei messaggi di
risposta. La modalità asincrona, delle Windows Sockets, adopera proprio tale
sistema di messaggi, piuttosto che una continua ripetizione di chiamate di
funzioni, superando, quindi, gli svantaggi, in termini di sfruttamento delle
risorse di sistema, del precedente modo operativo. Per contro, c'è da dire che
tale sistema risulta leggermente inferiore, rispetto a quello non-bloccante, in
termini di prestazioni, in quanto, proprio il sistema dei messaggi richiede, di
per sè, un certo ammontare di tempo nell'esecuzione delle notifiche e della
loro successiva elaborazione, riducendo quindi la capacità complessiva di
scambio dei dati (data throughput). Vedremo, nel paragrafo successivo,
trattando gli stati delle socket, in che maniera vengano adoperate le funzioni
asincrone, ed i tipi di messaggi elaborati, ed appositamente implementati per
le Windows Sockets.
C'è qualcuno che mi può illuminare su questo evento WSAEWOULDBLOCK facendo in modo di saltarlo senza aspettare tot tempo per il reinvio dei dati?
Anche perchè nella ricezione dei pacchetti non ho bisogno di reinviarli se sono persi o il send non è andato a buon fine non uso UDP ma TCP molto + affidabile infatti uso nella ricezione questa tecnica:
case FD_READ:
if( (bytes) < sizeof(GAMEMSG_GENERIC) )
return TRUE;
Ovvero se il pacchetto è < di 58 bytes salto la lettura del pacchetto leggendo invece il pacchetto se è appunto di 58 bytes come da struttura.
In questo modo evito, oltretutto, di processare pacchetti fasulli se qualche malintezionato vuole sendare dei byte arbitrari sul server
la soluzione proposta (attendere 750 ms) fa schifo: quello che devi attendere per andare a colpo sicuro non sono i 750 ms, è il messaggio FD_WRITE; sennò a che serve?
se non ti va di attendere allora apri un altro thread e invia da là, magari usando socket bloccanti.
i 750 ms non lo sò ma credo che sia un esempio per dire che è troppo attendere.
Mentre FD_WRITE io per esempio non lo uso proprio, fosse questo il fatto?
Voglio dire: sul server io uso FD_READ per ricevere il pacchetto e subito tramite ciclo (for i) li reinvio a tutti i client il tutto sempre dall'evento FD_READ e come finisco il ciclo (for i) di invio esco con return TRUE; dai messaggi. Il server dopo tutto deve smistare il pacchetto ai clients.
In FD_WRITE, così come sul client, non ci passo mai. Ci passo uno sola volta nel server quando si collega un nuovo client.
In pratica funziona così:
Il client si collega e nel server entro nell'evento FD_ACCEPT: faccio che il client è true poi il server invia con send l'id al client ora come esco dal ciclo dei messaggi mi entra in FD_WRITE una sola volta.
Forse da FD_READ, che serve per ricevere, non devo inviare con il (for i) da FD_READ in quanto il send si deve fare da FD_WRITE quando è pronto a scrivere.
mi servirebbe qualcuno che mi spiegasse meglio.
Il problema è che come ho fatto a me funziona tutto. Per esempio inviando come per una chat è perfetto.
Ma se mando i dati in modo continuo vedo che gli exe (il server e 3 client aperti in locale) sembrano rallentare. Il server ad un certo punto sembra fermarsi e mettendo il focus sul form di un client il server ricomincia a stampare dati... non lo sò. Se passo il focus su un'altro client (cliccando con il mouse sul form) la finestra si ridisegna piano piano (intendo i bottoni la listbox ecc ecc) questo vale per tutti e 4 gli exe anzi il server.exe è ancora + lento a ridisegnarsi ed è anche naturale.
Non ci sono errori di nessun tipo.
Fosse che il send deve essere attuato da FD_WRITE:...??
mha... guarda non sò proprio.
peròho questo link in inglese che mi intriga:
http://www.gamedev.net/reference/articles/article1297.asp
in cui questa è la parte interessante:
//Sending and Receiving Data
The general idea is to create an infinite while loop in which you will
continuously send data until you max out the buffer. When is happens, send
() will return the error WSAWOULDBLOCK. This means that if it were a
blocking socket, it would wait (stop execution) for more room in the buffer
and then send. But since it isn’t, you get the error. So now that you’ve filled
up the buffer, you just have to wait until more room becomes available so
you can write again. And bingo! Up pops another FD_WRITE event. Do you
have any idea how much trouble I had to go through to figure this out? You
people are so darned lucky! Here’s an example of an FD_WRITE event handler:
case FD_WRITE: // we can send data
{
// enter an infinite loop
while(TRUE)
{
// read in more data from the file and store it in packet.data.
in.read((char*)&packet.data, MAX_PACKET_SIZE);
// increment the amount of data sent
data_sent += strlen(packet.data);
// send the packet off to the Server if it is filled
if (send(wparam, (char*)(&packet), sizeof(PACKET), 0) == SOCKET_ERROR)
{
// check if the network buffer is full and can send no more
// data. If so then break from the loop
if (WSAGetLastError() == WSAEWOULDBLOCK)
{
// break from the loop – buffer is full
break;
}
else // another error
{
// display an error message and clean up
CleanUp();
return(0);
}
}
}
} break;
vBulletin® v3.6.4, Copyright ©2000-2025, Jelsoft Enterprises Ltd.