Check alle échte Black Friday-deals Ook zo moe van nepaanbiedingen? Wij laten alleen échte deals zien

[C#] Threads en HttpWebResponse

Pagina: 1
Acties:

  • TRoZZ
  • Registratie: Oktober 2000
  • Laatst online: 13-05 11:46
Voor een leuk projectje maak ik een download applicatie zoals je vroeger GetRight had. Dus meerdere segmenten(threads) die 1 file downloaden en zodoende sneller je download binnen halen. Nu heb ik al veel gevonden en ook al wat werkend. Inweze heb ik ook al op de codeproject een werkende versie gevonden maar voor m'n eigen ego wil ik toch dit probleem oplossen.

Wat is er nl. aan de hand, de threads lopen niet asynchroon omdat de eerste thread alle prioriteit op eist. Zodoende worden alle segmenten achter elkaar gedownload en niet tegerlijkertijd. Ik heb al in het voorbeeld van de codeproject zitten zoeken wat deze anders doet maar ik doe nagenoeg het zelfde. Wat snippets uit mijn code:

C#:
1
2
3
4
5
6
7
8
           // Alle threads opstarten, bekend is dan hoe groot de segmenten zijn
           // hier is ook niet veel mis mee.
            foreach (SegmentDownloader ss in Segments)
            {
                   Thread t = new Thread(new ParameterizedThreadStart(ss.DownloadSegment));
                   t.Start(this);              
    
            }


C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// Het downloaden van een segment (wat gestript)
public void DownloadSegment(object  o)
        {
            StreamDownloader parent = o as StreamDownloader;
             
            long currentPosition = 0;
            try
            {           
                       // tot op hier loopt alles nog naast elkaar     
                using(Stream response = parent.DownloadProvider.CreateStream(RemoteFile, StartBytes, StartBytes+ReadBytes))
                {
                    
                    int bytesRead = 0;
                   
                    byte[] buffer = new byte[bufferSize];
                    currentPosition = StartBytes;
                    do
                    {
                        bytesRead = response.Read(buffer, 0, bufferSize); 
                            OutputStream.Position = currentPosition;
                            OutputStream.Write(buffer, 0, bytesRead);
                        currentPosition += bytesRead;
                        fd.CurrentBytes = TotalBytesRead;
                        OnBytesReveived(fd);
                        TotalBytesRead += bytesRead;

                    
                    }
                    while (bytesRead > 0 && TotalBytesRead < ReadBytes);
                }
                
               
      

            }
            catch (Exception exc)
            {
                throw exc;
            }
            finally
            {
                OutputStream.Close(); 
            }

        }


code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
        /// Het ophalen van de stream
        public virtual Stream CreateStream(RemoteFileInfo rfi, long initialPosition, long endPosition)
        {
            HttpWebRequest request = (HttpWebRequest)GetRequest(rfi.Url);


            if (initialPosition > 0)
            {
                request.AddRange((int)initialPosition);
            }

            WebResponse response = request.GetResponse();

            return response.GetResponseStream();
        }


Ik heb ook al eens geprobeerd de response niet te doen en gewoon een teller vol te laten lopen. Dit ging dus wel naast elkaar. Het zit dus volgens mij echt in het ophalen van de stream (dit gebeurt in CreateStream). Mocht iemand wat weten dan bedankt wat ik snap er niks van waarom het niet werkt.

  • Niemand_Anders
  • Registratie: Juli 2006
  • Laatst online: 09-07-2024

Niemand_Anders

Dat was ik niet..

Waarom gebruik je HttpWebResponse voor het downloaden. Waarom gebruik je niet gewoon de TcpClient of zelfs de Socket class om te downloaden. Het HTTP protocol is nou ook weer niet bijzonder moeilijk.

Jij doet gelijktijdige downloads, maar je schrijft de output synchroon naar de output stream. Probeer eens OutputStream.BeginWrite(). Controleer ook of je toevallig geen exclusive lock creert op het bestand. Daarnaast is Position *NIET* threadsafe! Dus zelfs als het downloaden werkt is er grote kans dat je uiteindelijke bestand niet correct is.

Je hebt best kans dat bijvoorbeeld FileStream wat locks heeft ingebouwd bij de synchrone aanroepen. Daardoor kan het best lijken alsof de delen achter elkaar worden gedownload.

Maar als je het schrijven naar de outputstream nu eens uitschakelt, hoe krijg je dan de responses terug?

(fixed some typos)

If it isn't broken, fix it until it is..


  • TRoZZ
  • Registratie: Oktober 2000
  • Laatst online: 13-05 11:46
Tja het implementeren van een protocol vind ik dan weer een uitdaging ansich. Misschien als dit werkt dat ik er eens aanbegin. Voor nu moet het ook via de implementatie van .NET werken. Immers werkt het codeproject er ook mee.

De Asynchrone versie van de stream read of write is wel een goeie sugestie. Echter kan op de filestream (output) niet asynchroon worden geschreven. Het uitlezen van de http stream kan wel asynchroon. De outputstream staat trouwens nu in een 'lock' dus dat moet echt problemen tegen gaan (behalve dan als een thread het lock blijft houden).

Maar goed alle tips hielpen wel tot het maken van een kleine verbetering. Ik kwam er nl. achter dat ik de request/response die ik in het begin deed niet afsloot. Nu sluit in dat wel netjes af en kan ik opeens met 2 streams tegerlijkertijd downloaden. Als ik heel de Request/Response code weghaal en vervang voor een dummy oplossing dan werken wel alle stream asynchroon. Het lijkt er dus op dat ik niet meer dan 2 webresponse streams tegerlijkertijd mag hebben.

  • Niemand_Anders
  • Registratie: Juli 2006
  • Laatst online: 09-07-2024

Niemand_Anders

Dat was ik niet..

Euh OutputStream mag natuurlijk niet in een lock staan, want dan kan de andere thread toch nooit zijn data wegschrijven naar diezelfde stream. En FileStream heeft net als NetworkStream standaard support voor voor asynchroon lezen en schrijven.

Quick & dirty fix: Bestandelen wegschrijven naar bestanden als XXX-part1.dat, XXX-part2.dat en als alle delen zijn gedownloaden dan de delen samenvoegen tot 1 bestand.

Een lange termijn oplossing zou zijn middels via the OnBytesReceived event methode juist een structure mee te geven waarin bytes, start positie en aantal bytes bepaald is. De listener plaatst de structure instance in een queue en je controller processed doorloopt simpel de queue en schrijft de verschillende data sequences naar het bestand. Deze techniek wordt voornamelijk in torrent clients toegepast.

De beperking van twee streams verbaasd mij niet helemaal. Ik weet niet precies welke implementatie voor HttpWebRequest wordt gebruikt, maar internet explorer heeft standaard ook de limiet van twee concurrent connecties naar een IP. Ik denk achter dat als je even zoekt op google dat je vrij eenvoudig 3 a 4 implementaties van het HTTP protocol terug moet kunt vinden. Ik kwam vrij snel bij deze implementatie uit. Met niet al te veel moeite is daar bij eenvoudig de content range header aan toe te voegen.

If it isn't broken, fix it until it is..