[VB.NET] BeginConnect() incontrollabile....

mercoledì 24 giugno 2009 - 09.43

spredicatore Profilo | Newbie

bene ho capito dove sta l'errore... il problema è che non so come risolverlo...
nel file allegato vi ho semplificato il progetto e se lo aprite occhio al task che succhia 150 mb di ram in una 15ina di secondi. certo il processo è velocizzato a ciclo while ma è per farvi vedere come succhia ram... il problema è proprio la procedura BeginAccept()
guardate guardate e vi rendete conto... cosa posso fare per fare in modo che non si mangi la ram in quel modo?? grazie!!!
è un errore mio o il vb.net prende tutta sta ram da solo?? grazie ancora

http://www.mediafire.com/?sharekey=3634de39eaa528c19bf8d6369220dcabe04e75f6e8ebb871

luigidibiasi Profilo | Guru

Credo che ci sia un problema di fondo...

BeginAccept è asincrona

Quando la richiami ritorna e ti passa il controllo dell'applicazione.

In questo caso non necessiti del thread per gestirti l'arrivo delle connessioni... richiamala dalla main principale e poi pensa lei a richiamarti la accetta().. (togli quel while che non serve... tanto le richieste di connessione vengono processate comunque 1 alla volta dallo stack tcp e poi ti vengono passate)..

il thread lo utilizzi se vuoi usare la accept ( che e bloccante... e che ti ritorna il nuovo socket direttamente all'uscita... ) la beginAccept invece te lo ritorna come parametro IAsyncResult...

Parlavi di velocizzare il programma.. forse sono io che non ho capito il senso di ciò che vuoi fare... ?


Il codice sorgente non è stato renderizzato qui
perchè non c'è sufficiente spazio.
Clicca qui per visualizzarlo in una nuova finestra

spredicatore Profilo | Newbie

se facessi come hai fatto tu in quel codice potrei accettare solamente una connessione... io ne devo accettare anche 50.000 al giorno... percio ho problemi di ram... sembra che la procedura beginaccept occupi un macello di ram ogni volta che entra un utente (ossia che accetta una connessione) e non capisco se sia un mio problema o se sia quella procedura e che quindi non dipenda da me... il while serve per accettare piu connessioni contemporaneamente senno potrei accettarne solamente una

luigidibiasi Profilo | Guru

Credo tu abbia fatto un pò di confusione con l'oggetto socket di .net...

Usando la beginAccept puoi accettarne quante ne vuoi.. il 2000 specifica la lunghezza della coda di backlog... (cioè quando connessioni aspetteranno una tua accept nella coda, prima di essere scartate...
se si collegano 6 utenti al secondo è inutile avere una coda di 2000... se si collegano 1950 utenti al secondo ha qualche senso... )

[per secondo intendo il tempo richiesto da windows e dalla rete per effettuare l'handshake completo]

Tieni presente che i socket si mettono in coda indifferentemente dalla accept o meno.. (dopo che hai effettuato la listen..)


Il metodo così descritto accetta la connessione e recupera il socket di comunicazione

Public Sub accetta(ByVal ar As IAsyncResult)
Dim ncon As Socket = ar.AsyncState.endAccept(ar) // Rileva una connessione e ti restiuisce il socket a quest'ultima...


' invia un messaggio di test
Dim hello As Byte() = System.Text.Encoding.ASCII.GetBytes("hello connection " & connNumber & vbCrLf)
ncon.Send(hello)
connNumber += 1
clientgen.BeginAccept(p, clientgen)
End Sub


Puoi provare ad aprire 2000 connessioni... le accetta tutte senza istanziare "subito" memoria inutile (ovviamente se si colleano 20000 socket è normale un consumo di memoria alto)

E' chiaro che per ogni connessione accettata recuperi solo il socket associato.. è compito tuo scrivere le routine che effettuano la comunicazione per ogni socket.. ( un thread che implementa la logica della comunicazione..)


ti posto un codice modificato:

Il codice sorgente non è stato renderizzato qui
perchè non c'è sufficiente spazio.
Clicca qui per visualizzarlo in una nuova finestra
Luigi Di Biasi
http://blogs.dotnethell.it/luigidibiasi/
http://www.dibiasi.it/

spredicatore Profilo | Newbie

Il codice sorgente non è stato renderizzato qui
perchè non c'è sufficiente spazio.
Clicca qui per visualizzarlo in una nuova finestra

questo è il codice originale... cosi ti fai una idea... il mio codice era una semplificazione di questo... non è la gestione delle utenze a occupare ram ma la chiamata della procedura BeginAccept()... come è possibile che questa procedura occupi ram?? io la avevo a ciclo while infinito perche cosi se velocizzava il fatto di richiamarla migliaia di volte... quello che succede dopo 4-5 giorni con l'esecuzione del programma normalmente.... ora hai inteso meglio quale è il mio problema?

luigidibiasi Profilo | Guru

Allora...

beginAccept registra la funzione di callback quindi richiamandola N volte, come fai, ipotizzo registri N funzioni..

Per il resto non capisco perché tu usi beginAccept in quel modo per velocizzare la cosa... che cosa vuoi ottimizzare? (la creazione dei socket di comuncazione?)

Il socket bindato è 1 solo .. è inutile che richiami all'infinito beginAccept perché risponderà sempre e comunque ad una richiesta sola per volta ( sulla porta 512 ) ... tirandola fuori dalla coda di attesa...

Per quanto riguarda le connessioni in arrivo tu hai controllo solo sulla dimensione della coda e sul momento in cui effettuare l'accept (cioè quando leggere dalla coda...) in coda c'è li mette il S.O.

Personalmente (mio modo di vedere eh!) ti consiglio di rivedere la parte relativa al beginAccept ... o almeno modularizzare un po' il codice separando la fase di accettazione dalla fase di utilizzo dei socket accettati..


Luigi Di Biasi
http://blogs.dotnethell.it/luigidibiasi/
http://www.dibiasi.it/

spredicatore Profilo | Newbie

ora ti spiego il mio problema... questo server... deve restare in piedi sempre e cmq... non è un server per clienti o per gente con la testa sulle spalle... deve essere a prova di attacchi sulla porta e cose del genere... percio uso il begin accept in modo da accettare richieste nel modo piu veloce possibile perche devo poter rispondere a piu richieste possibili... infatti ho usato il manualraisevent in modo da eseguire la begin accept appena ricevo una richiesta di connessione in modo che la porta non saturi mai e il server risponda a quante piu richieste possibili... capisci la mia esigenza ora?

spredicatore Profilo | Newbie

ho fatto una prova malefica e ho notato il particolare.....

us.client.BeginSend(buffer, 0, buffer.Length, SocketFlags.None, chiamata, Nothing)

us.client.Send(buffer, buffer.Length, SocketFlags.None)


la differenza tra queste 2 procedure apparte che una è sincrona e l'altra asincrona è che quella asincrona prende un macello di ram e una volta che ha finito nemmeno libera piu quella ram... è la stessa cosa del begin accept....

quindi la domanda è spontanea... perche il beginaccept e il beginsend prendono cosi tanta ram? volendo.... cosa posso usare come alternativa in modo da restare sempre in ambito asincrono?

luigidibiasi Profilo | Guru

Cerco di darti qualche consiglio poi mi fermo qui perché non vorrei partecipare allo sviluppo di un server per persone "senza la testa sulle spalle"

Il numero di richieste che puoi ricevere è limitato dalla banda della connessione di cui dispone il server.

Quindi, se la tua banda non supporta più di N connessioni al secondo puoi anche non dichiarare una coda più grande di questo numero.

N lo puoi determinare in linea di massima come [BANDWIDTH in BIT / DimensionePacchettoSYN_TCP in BIT]

TUTTA la fase di :
- Ricezione SYN,handshake, messa in coda e segnalazione ( nel tuo caso ) tramite la funzione callback la svolge windows. Inoltre c'è il framework di mezzo (altro tempo perso).

Tu prendi il controllo quando la beginAccept ritorna... durante questo periodo è INUTILE richiamarla in continuazione (specialmente se non ci sono connessioni in attesa) perché fai perdere tempo sia al thread che allo stack tcp .... quando la connessione arriva è lo stesso Stack (win32) a segnalartelo... quindi "togli" quel ciclo e richiama la beginAccept quando ritorna la precedente.. oppure usa un thread separato per l'accettazione delle connessioni usando la accept normale... meglio ancora passa a vc++ e usa winsock così bypassi .net e vai più veloce

>>e richieste nel modo piu veloce possibile perche devo poter >>rispondere a piu richieste possibili... infatti ho usato il >>manualraisevent in modo da eseguire la begin accept appena >>ricevo una richiesta di connessione in modo che la porta non >>saturi mai

Da Msdn:
>>"L'oggetto ManualResetEvent consente ai thread di comunicare tra loro tramite segnalazioni. Di solito questa >>comunicazione riguarda un'attività che deve essere completata da un thread perché gli altri thread possano >>procedere."

Per quanto riguarda la saturazione della porta tu non puoi farci nulla... Hai un limite al numero di porte che puoi aprire... una volta superato Winsock ti butta fuori.. altro che beginAccept...

Per quanto tu possa processare velocemente le richieste quando hai a che fare con attacchi tipo DoS o DDoS ( credo tu voglia combattere questi? prima di tutto devi individuarli ....) ti vanno a creare una miriade di connessioni e ti mettono a sedere il server. (e visto che ci riescono che le server farm ....)

>>deve essere a prova di attacchi sulla porta e cose del >>genere...
Un firewall hardware forse sarebbe meglio che un programma in .net?

Luigi Di Biasi
http://blogs.dotnethell.it/luigidibiasi/
http://www.dibiasi.it/

spredicatore Profilo | Newbie

lasciamo a parte il beginaccept... ho notato un'altra cosa come dicevo nel 2° post... se uso us.client.send (sincrono) per inviare messaggi la ram non sale..... se uso us.client.beginsend() la ram schizza alle stelle.... credo sia quello il mio problema... almeno ho capito perche la ram sale dopo 2-3 giorni... che tu sappia ce un altro modo per inviare stringhe in modo asincrono senza usare il beginsend che sembrerebbe occupare un sacco di ram?

sai cosè che i metodi sincroni devo scartarli a priori.... ti faccio capire meglio....

ho un array client fatto cosi

client1
client2
client3
client4
client5
client6

for each asd as socket in client //qui il messaggio viene mandato molto
asd.beginsend(ciao) //velocemente ma occupa molta ram
next

oppure

for each asd as socket in client
asd.send(ciao)
next

//in questo invece il tempo che passa tra l'invio del messaggio al client1 e al client6 è eterno.. dato che il send nn prosegue finche non è sicuro che il send precedente sia andato a buon fine... e io non ho 4 client nell'array... me 800-900...

luigidibiasi Profilo | Guru

Per il progetto che hai in mente (800/900/10000/20000 sock aperti) ti tocca:

* usare winsock nudo e crudo....

* progettare per bene il tutto.... ( e per bene intendo tipo progetto su commessa... non li gestisci per hobby tutti quei client... gli hub si pagano)

* magari prendere in considerazione il fatto che 10000 sock aperti in contemporanea, su un unico server sono un pò troppi e forse devi scrivere il programma in modo scalabile .... che riesca a bilanciare il carico su + server tipo i server IRC ( una connessione p2p tra i server e multipoint tra server e client coi server che si scambiano informazioni di stato... )

Magari posta uno schema di funzionamento o qualcosa che faccia capire che deve fare in realtà questo programma... io da banIp, spoof etc non ho capito se stai sviluppando per irc o stai creando un hub privato per dc+... o cosa?? (cmq sappi che in .net ste cose non le rendi performati... a meno di progettare... come detto prima... VERAMENTE BENE il tutto e già quei goto non mi ispirano fiducia...)

Mi raccomando a cosa ci condividi....

Luigi Di Biasi
http://blogs.dotnethell.it/luigidibiasi/
http://www.dibiasi.it/

spredicatore Profilo | Newbie

non pensavo conoscessi gli hub della rete dc++... bene... questo sarà un hubsoft per quella rete... so che regge 1000 utenti e passa ma il problema era la ram che saliva... ho risolto il problema eliminando il beginsend (su msdn dice che occupa abbastanza risosrse) e l'ho sostituito con il sendasync

pero' mi sono imbattuto in un altro problema....



Public Sub completo(ByVal ar As Socket, ByVal asd As SocketAsyncEventArgs)
'set del manualresetevent (in teoria)
End Sub

Public Sub manda(ByRef us As User, ByVal data As String)
data = data.Replace(Chr(10) & Chr(10), "").Replace("||", "")
If data.EndsWith("|") = False Then
data &= "|"
End If
Dim buffer() As Byte = Encoding.Default.GetBytes(data)
impostazioni.banda += buffer.Length
protocol.byteinviati += buffer.Length
Try
AddHandler us.sae.Completed, AddressOf completo
us.sae.SetBuffer(buffer, 0, buffer.Length)
us.client.SendAsync(us.sae)
Catch '"È già in corso un'operazione di socket asincrono che utilizza questa istanza SocketAsyncEventArgs.";
End Try
End Sub

so che mi da quell'errore perche il soft cerca di inviare piu messaggi nello stesso istante.... ma a me serve proprio quello..... come faccio a dirgli che deve aspettare che il socketeventargs sia pronto?? avevo pensato al manualresetevent... ma non deve essere lento senno non ho risolto nulla.....

spredicatore Profilo | Newbie

Public Sub completo(ByVal ar As Socket, ByVal asd As SocketAsyncEventArgs) asd.UserToken.set() End Sub Public Sub manda(ByRef us As User, ByVal data As String) Try us.evento.WaitOne() data = data.Replace(Chr(10) & Chr(10), "").Replace("||", "") If data.EndsWith("|") = False Then data &= "|" End If Dim buffer() As Byte = Encoding.Default.GetBytes(data) impostazioni.banda += buffer.Length protocol.byteinviati += buffer.Length us.sae.UserToken = us.evento us.sae.SetBuffer(buffer, 0, buffer.Length) us.client.SendAsync(us.sae) us.evento.Reset() Catch End Try End Sub


non ho risolto proprio nulla invece..... ho modificato il codice come meglio credevo... mettendo un handler per ogni user e con 1 funziona ma come ne entrano 10-15 crasha tutto..... dove sbaglio?

Partecipa anche tu! Registrati!
Hai bisogno di aiuto ?
Perchè non ti registri subito?

Dopo esserti registrato potrai chiedere
aiuto sul nostro Forum oppure aiutare gli altri

Consulta le Stanze disponibili.

Registrati ora !
Copyright © dotNetHell.it 2002-2024
Running on Windows Server 2008 R2 Standard, SQL Server 2012 & ASP.NET 3.5