[ Omega Warrior @ 25.02.2008. 08:01 ] @
Pozdrav,

Radim aplikaciju za prijenos podataka putem GPRS mreže na WIN CE operativnom sustavu.

Sve radi, i primanje i slanje...

Samo jedna stvar me muči, Kad šaljem podatke sa beginSend i EndSend, čim se pozove BeginSend program odmah skoči na endSend. Po nazivu ja sam logički mislio da kada socket pošalje sve podatke, da če onda da ode na EndSend, ali nije tako, on odmah ode na endSend a podatak se i dalje šalje, i onda ja tu moram rješavat stvar sa timerima... ali to je kilavo i nije pouzdano. Ja nikako nemogu da obavijestim korisnike kad je podatak do kraja poslan...

Moje konačno pitanje je, kako da saznam kada je socket kompletno poslao sve podatke?


Hvala!
[ mmix @ 25.02.2008. 10:45 ] @
To je zato sto BeginSend i EndSend predstavljaju asinhrone operacije za Send() metod, medjutim ako ti pozivas EndSend odmah posle BeginSend, to ti dodje na isto kao da si samo koristio Send() posto ce EndSend da zablokira aktivni thread dok se transfer ne zavrsi. Znaci imas dve opcije:

1. Umesto BeginXXX i EndXXX asinhronih operacija koristi sinhronu XXX() operaciju, u ovom slucaju Send() koji jeste podrzan u WindowsCE. Downside je da sinhrone operacije blokiraju thread dok se podaci ne posalju, sto u slucaju dugih timeout-ova moze da ucini tvoju aplikaciju veoma "ukocenom".

2. Napravi zaista ainhroni proces. Iskoristi callback parametar BeginSend metoda da prosledis event koji ce biti pozvan iz worker thread-a posle BeginSend(). Iz njega pozoves EndSend() i zablokrisa worker thread do zavrsetka slanje, i posle EndSend() posalji GUI event call u pozivnu formu da je obavestis da je transfer zavrsen. Za tu svrhu koristis Form.Invoke() koji je takodje podrzan na CE.

Jos jedna vazna stvar, kad koristi Begin/End asinhorne operacije, povratak iz EndSend ne znaci da su svi podatci iz socketa poslati, vec da poslat odredjeni broj bajtova (i taj broj ti se vraca kao rezultat EndSend()). To uporedis sa velicinom bufera i ako nisu svi bajtovi poslati mozes iz samog callback-a da napravis novi BeginSend koji koristi isti callback, u principu petlju dok god se buffer ne isprazni. Dobra stvar sa ovim je sto usput mozes da obavestavas formu da je deo buffera poslat, sto moze da se iskoristi za lepi progress bar u formi
[ Omega Warrior @ 25.02.2008. 11:01 ] @
Hvala ti mmix.

Krivo sam se izrazio, ne pozivam ja endSend, on je u callback funkciji u sam sebe poziva...

Ali ovaj zadnji paragraf što si rekao je baš to rješenje što mi treba. Hvala ti.


[Shadowed: obrisan offtopic.]

[Ovu poruku je menjao Shadowed dana 25.02.2008. u 12:20 GMT+1]
[ mmix @ 25.02.2008. 11:40 ] @
SAmo vodi racuna o GUI thread-u, bilo kakva operacija sa GUI elementima moze da se obavlja samo iz tog thread-a, a tvoj callback se poziva iz worker thread-a i ako direktno iz njega obavis neku operaciju nad kontrolom koja joj ne pripada dobices InvalidOperationExpception sa porukom "Control <xxx>accessed from a thread other than the thread it was created on" dok si u debug modu, u release modu nece uvek da okine ovaj exception, ali ako ga okine u release modu velika je sansa da ce ti puci aplikacija cak iako handlujes exception. Koristi Form.Invoke() da budes siguran.

Inace, nisi prvi kojeg je zabunilo Begin/End u asinhronom modu, to je neko nepisano pravilo za implementaciju asinhornih operacija u frameworku i mozda bi pravilnija imena bila "InitializeSend()" i "DoSend()" . Begin i End se ne odnose na ono sto operacija radi, nego na samu konktest asinhronosti operacije (spremi se za asinhornu operaciju, kompletiraj asinhronu operaciju).
[ mmix @ 25.02.2008. 12:04 ] @
E sad kad mi je vec na pameti, jos jedan savet iz iskustva. Ne znam odakle pozivas BeginSend() ali ako je to iz nekog drugog worker thread-a onda moras da se osiguras da taj thread koji je pozvao BeginSend() bude ziv dok se operacija ne zavrsi u potpunosti. Ako taj thread umre pre nego se podaci posalju iz socekta, pucaju sve asinhrone operacije koje su u toku.

Jednostavan nacin za to je da zablokiras pozivni thread. BeginSend ti vraca IAsyncResult a on ima property AsyncWaitHandle koji mozes da iskoristis. Samo pozoves rezultatBeginSend.AsyncWaitHandle.WaitOne(). Kad callback zavrsi u potpunosti sa operacijom, oslobodis manualevent objekat koji je iza waithandla koristeci ((ManualResetEvent)ar.AsyncWaitHandle).Set().

[ Omega Warrior @ 25.02.2008. 12:14 ] @
Probao sam na način na koji si mi rekao:

Citat:
Povratak iz EndSend ne znaci da su svi podatci iz socketa poslati, vec da poslat odredjeni broj bajtova (i taj broj ti se vraca kao rezultat EndSend()). To uporedis sa velicinom bufera i ako nisu svi bajtovi poslati mozes iz samog callback-a da napravis novi BeginSend koji koristi isti callback, u principu petlju dok god se buffer ne isprazni.



Ali ponasa se na isti način, mani sve ostalo radi besprijekorno... I to nakon mnogo sati testiranja... :D

Ali taj problem ostaje; Sad sam radi testiranja poslao 128 kB fajl. Uzeo sam velicinu tog fajla i usporedio sa EndSend rezultatom. Isti je iz prvog pokusaja, odnosno nakon 1,2 sekunde dode na endSend metodu...

Na stolu imam drugi komp koji se izvodi kao primatelj, i vidim da je file tek poceo da se skida a endSend je odmah vratila duzina fajla...

I opet se isti problem javlja...


Code:
Private Sub sentCallback(ByVal ar As IAsyncResult)
        Dim kolicina As Integer = cSocket.EndSend(ar) ' --Callback odmah vrati duzina fajla, ne znam mozda ako bih iskoristio veci fajl mozda bih tek onda vratio nesto manji razultat... 

        If Not kolicina >= duzinaPodatkaZaSlanje Then
            cSocket.BeginSend(cTransferData, 0, cTransferData.Length, SocketFlags.None, New AsyncCallback(AddressOf sentCallback), Nothing) '--Ovaj dio se skoro nikada ne poziva...
        End If
        
        processStatus()
    End Sub

Nikako nemogu da uspijem da saznam kada je fajl kompletno prenesen.

Da li imaš još kakvu ideju.

Hvala unaprijed!
[ mmix @ 25.02.2008. 13:25 ] @
A to tebe zapravo muci, pazi ovako:

Citat:
There is no guarantee that the data you send will appear on the network immediately. To increase network efficiency, the underlying system may delay transmission until a significant amount of outgoing data is collected. A successful completion of the BeginSend method means that the underlying system has had room to buffer your data for a network send.
The successful completion of a send does not indicate that the data was successfully delivered. If no buffer space is available within the transport system to hold the data to be transmitted, send will block unless the socket has been placed in nonblocking mode.

Sto ce reci, ti svoj deo posla obavio, sad je na TCP/IP steku da odradi svoj posao a kad ce on to odraditi i kojim tempom ne zavisi od tebe. Ako bas moras da znas kad je destination host primio sve moras da iskodiras tu logiku na obe strane, tj treba ti custom acknowledgment. Kad posaljes sve podatke iz send predji u receive mod i osluskuj povratnu unformaciju, destination host kad primi ceo fajl treba da ti posalje acknowledgment paket (npr 1 bajt sa rezultatom, 0-ok, 1-greska, itd), kad primis taj paket na CE-u znas da je ceo fajl stigao.





[ Omega Warrior @ 26.02.2008. 09:23 ] @
Tako to sada i imam rjeseno.

Ali tu onda postoje problemi sta ako dode do neke greske i ovaj primaoc ne uspije poslat potvrdu da je primio sve... Zato sam i trazio drugo rjesenje kako skuzit kad je sve poslano...
Ali cini se da nema drugog rjesenja, morat cu se zadovoljit sa ovime.

Hvala ti mmix na trudu i strpljenju, mnogo si mi pomogo!

[ vujkev @ 26.02.2008. 10:21 ] @
Pa ako ne dobiješ potvrdu posle npr 5 sec. ponovo pošalji ceo paket
[ mmix @ 26.02.2008. 11:23 ] @
Ako dodje do greske na remote host-u a ti si usao u receive sa svoje strane, imas dve opcije:


1. Koristis sinhroni receive() a pre toga postavis Socket.ReceiveTimeout na neki timeou period. Ako podaci ne stignu za timeout period, receive ce baciti SocketException i onda znas da nesto nije u redu.

2. Pre zapocinjanja asinhronog receiva-a digni timer sa nekim timeoutom, ako se timer handler desi pre nego sto worker thread stigne do EndReceive, opet imas problem i mozes da pozoves Socket.Close(); sto ce oboriti asinhrone pending operacije i vratiti execution worker thread-a na EndReceive (koji ce vratiti 0 bajtova ili baciti SocketException, ne secam se tacno).