C# WebClient extrem langsam?
Moin!
Ich möchte einen WebClient nutzen, um eine ca. 1 GB große Datei mit C# herunterzuladen. Wenn ich sie im Webbrowser (Chrome) runterlade, dauert dass etwa 5 Minuten. Bei meinem Code verliert sich meine Geduld aber schon bevor 1% erreicht ist (ca 5 Minuten, für 6 Prozent werden über 40 Minuten benötigt).
Ich habe die ganzen Proxy-Sachen recht weit am Anfang gemacht, weil es auf Stackoverflow stand, dass das helfen könnte (scheint es aber nicht zu tun).
Meinen Code findet ihr hier: https://paste.helpch.at/ezixopufot.cs
Debug.Log sowie alles um "progress" und "ProgressBar" sind eigene Klassen von mir. Die scheinen an der Geschwindigkeit nichts zu ändern.
Hier einmal der Screenshot aus dem Task-Manager. Es scheint gar nicht alles zur Verfühung stehende an Bandbreite etc genutzt zu werden. Das Bestätigt sich auch wenn ich mir die heruntergeladenen Bytes gegen die verbleibenden Bytes anzeigen lasse.
.net 4.6 Target
Visual Studio 2017
Windows 10 Pro
Debug-Build
Hat jemand eine Idee? Vielen Dank!
(Achtung, Randinfo: Meine Tests erfolgen in der Windows Sandbox. Ich weiß aber dass zumindest Edge kein solches Limit hat (auch aus der Sandbox), siehe dieses Speedtest-Ergebnis: Speedtest by Ookla - The Global Broadband Speed Test)
Übrigens, nach zusätzlichen ca. 10 Minuten warten (noch etwas mehr), mit exakt dem oben stehenden Code, sieht meine Fortschrittsleiste so aus:
Ich hätte nur gerne diese Leiste und das scheint nur per WebClient zu gehen (ein event zu haben immer wenn es Fortschritt gibt). Gäbe es schnellere Alternativen würde ich sofort wechseln.
Die Datei die ich laden will ist übrigens: http://addresses.loyce.club/Bitcoin_addresses_LATEST.txt.gz
1 Antwort
...wird Deine Anzeige vielleicht nur nicht aktualisiert?
Vielleicht solltest Du Dir die Zieldatei einfach anschauen, die Dürfte normal geladen werden.
In Zeile 33 hast Du ein "nettes" Console.ReadKey();
In einer Callbackfunktion ein totales NoGo.... . selbst ein simples WriteLine kann unter Umständen so langsam sein, das sich Events gegenseitig auf die Füße treten.
wie Zeitkritisch Callbackst sind zeigt folgende Demo
using System;
using System.Net;
using System.ComponentModel;
class Prog{
public static void DownloadCompleted(object sender, AsyncCompletedEventArgs e)
{
Console.WriteLine("completed");
}
public static void DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
//ist so langsam, das das Event ca. 3 mal gefeuert hat noch bevor der Corsor wieder auf die angegebene Position springen kann
Console.SetCursorPosition(0,10);
Console.WriteLine(e.ProgressPercentage);
//Console.ReadKey(); //<--sowas ist total Kacke in einer Callbackfunktion
}
static void Main(string[] args){
WebClient wc = new WebClient();
wc.DownloadFileCompleted += Prog.DownloadCompleted;
wc.DownloadProgressChanged += DownloadProgressChanged;
wc.DownloadFileAsync(new Uri("http://addresses.loyce.club/Bitcoin_addresses_LATEST.txt.gz"), "test.bin");
Console.WriteLine("warte auf das Ende");
Console.ReadKey();
}
}
Also, ich habe jetzt vermutet dass einfach nicht die richtigen Werte ankommen. Die Ausgabe wiederlegt das aber. Hier habe ich einmal mit den Werten die mein Programm mir sagt gearbeitet und nachgerechnet: https://ibb.co/41TBvbM
Die Problematik bei Leuten wie Dir ist, das Ihr euch ein Thema herausgreift, welches über Eure Grundkenntnisse hinaus geht. Da werden irgendwelche Codeschnippsel zusammenkopiert, ohne wirklich verstanden zu haben wie die Sache funktioniert.
Es ist nahezu unmöglich hier eine Art Privatunterricht zu zelebrieren.
zum Thema Ereignisverarbeitung schau Dir folgendes an:
- https://docs.microsoft.com/de-de/dotnet/standard/events/
- https://docs.microsoft.com/de-de/dotnet/api/system.componentmodel.backgroundworker?view=net-6.0
Sowie das übrige Texte zum ComponentModel:
Naja. Da muss man nicht gleich so kommen.
Zudem geht die Frage an einen sehr spezifischen Punkt, nämlich das Fehlschlagen einer Division. Sollte doch einfach sein, oder? Hat auch nichts mit Events zu tun, hätte man meinen Text gelesen.
Zudem weiß ich sehr wohl wie das funktioniert, danke.
nämlich das Fehlschlagen einer Division.
Ich dachte es ging um :
e.ProgressPercentage gibt nur Müll zurück
einfach nicht die richtigen Werte ankommen...
Es scheint doch schlimmer um die Basics zu stehen...😖
Schulbildung
Prozentrechnung : BytesReceived *100/TotalBytesToReceive
C# basics:
- IntergerVariable +-*/ Integervariable = IntegerErgebnis
- FloatVariable +-*/ Integervariable = Ergebnis
Ich bin nicht böse oder frech, wenn ich moniere, das Anfänger mit Forms/WEP herumzaubern und nicht wissen was unter der Haube passiert...
ganz einfach mal in der console...:
using System;
class Prog{
static void Main(string[] args){
int a=7;
int b=3;
Console.WriteLine(a/b); // int / int = int
Console.WriteLine((double)a/b); //caste einen Teil der Formel zu Double und das Ergebnis ist Double
Console.WriteLine(a*1.0/b); // ...ein Teil der Formel ist Double
Console.ReadKey();
}
}
oder mein obiges Script verfeinern:
using System;
using System.Net;
using System.ComponentModel;
class Prog{
public static void DownloadCompleted(object sender, AsyncCompletedEventArgs e){
Console.WriteLine("completed");
}
public static void DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e){
Console.WriteLine("downloaded {0} of {1} bytes. {2} % complete...FixpointPercentage:{3,7:f3} %",
e.BytesReceived,
e.TotalBytesToReceive,
e.ProgressPercentage,
(e.BytesReceived*100.0/e.TotalBytesToReceive)
);
}
static void Main(string[] args){
WebClient wc = new WebClient();
wc.DownloadFileCompleted += DownloadCompleted;
wc.DownloadProgressChanged += new DownloadProgressChangedEventHandler(DownloadProgressChanged);
wc.DownloadFileAsync(new Uri("http://addresses.loyce.club/Bitcoin_addresses_LATEST.txt.gz"), "test.bin");
Console.ReadKey();
}
}
Ich mache die Beispiele für die Console ja nicht weil ich zu dumm für Forms bin, Sondern um alles überflüssige wegzulassen, damit der FS kapiert was unter der "Motorhaube passiert...
Nutze selbst auch die Konsole.
Es ging mir auch nicht darum, dich blöd darstehen zu lassen oder kacke zu dir zu sein.
Hier ging es mir mehr darum, zu fragen, was ProgressChangedEventArgs.ProgressPercentage anderes macht als die Division. Ein Int wird zurückgegeben, also kann es 0 für läuft und 1für fertig oder 1-100 für den Fortschritt sein. Es ist letzteres; "A percentage value indicating the asynchronous task progress.". Also der Fortschritt in Prozent - wieso dann immer 0?
Die bytesReceived und TotalBytesReceived Variablen können ausgegeben werden und sind dann richtig, aber nicht in anderen Operationen genutzt werden - mindestens eine ist immer 0. Das war meine eigentliche Frage hier.
Und wieso sollte man die erhaltenen Bytes * 100 rechnen? Sollten nicht die erwarteten und erhaltenen Bytes beide in der Einheit "Byte" und damit direkt zum rechnen nutzbar sein?
Ich kann Dir nicht sagen weshalb bei der Eventhandler von DownloadFileAsync bei Dir nur 0 übergibt bei mir läuft der Download (meist) perfekt durch. ("Meist", weil es bei mir gelegentlich passierte, dass bei zu häufigem probieren der Datei, der Server den Request ablehnte?)
wieso sollte man die erhaltenen Bytes * 100 rechnen?
Prozentrechnen ... Dreisatz ? (7.Klasse):
Prozentsatz zu Prozentwert wie 100 zu Grundwert
...also...
Prozentsatz/Prozentwert = 100/Grundwert
...Prozentwert auf der linken Seite ausmultiplizieren...
Prozentsatz/Prozentwert * Prozentwert = 100/Grundwert * Prozentwert
...Auf der linken Seite kürzt sich der Prozentwert weg. Es bleibt...
Prozentsatz = 100/Grundwert * Prozentwert
Da es bei einer Multiplikation pupsegal ist auf welcher Seite ein Multiplikator steht, geht eben auch:
Prozentsatz = Prozentwert * 100/Grundwert
Dem Mathelehrer ist es egal, genauso wie dem Compiler...
Sollten nicht die erwarteten und erhaltenen Bytes beide in der Einheit "Byte" und damit direkt zum rechnen nutzbar sein?
Prozentrechnen hat ersmal nix mit Einheiten zu tun. Ebenso könnte ich auch mit Bananen rechnen
Der Datentyp von e.BytesReceived und e.TotalBytesToReceive ist übrigens long (heute sind Dateien auch größer als ein uint (4.294.967.295)) .
Um den Prozentsatz mit Kommastellen zu bekommen zwinge ich durch die Double-Kostante 100.0 (statt 100) den Compiler zu einen impliziten Typecast aller Komponenten der gesamten Formel.
War bei dem * 100 nur verwirrt. Ich hätte bytesIn / bytesTotal * 100 gemacht, so wie ich es in der Schule gelernt habe.
Prozentrechnen hat ersmal nix mit Einheiten zu tun. Ebenso könnte ich auch mit Bananen rechnen
Jup, das war mir durchaus klar. War aber der Grund für meine Verwirrung von weiter oben, weil eben beide Werte die gleiche Einheit (eben Byte) haben und ich nicht erst noch was umrechnen müsste (Mb in Byte oder so wat)
Habe jetzt das hier:
double bytesIn = Convert.ToDouble((double)e.BytesReceived);
double totalBytes = Convert.ToDouble((double)e.TotalBytesToReceive);
double percentage = e.BytesReceived / e.TotalBytesToReceive;
Ergebnis ist trotzdem 0. Wobei da tatsächlich 0 und nicht 0.0 steht. (Randnotiz: Habe es einmal mit und einmal ohne den zusätzlichen cast zu double probiert, bringt beides nix.)
Ein Test mit Dummy-Longs:
percentage = Convert.ToDouble((long)1) / Convert.ToDouble((long)5);
Raus kommt 0.2 => 20%, also das richtige Ergebnis.
e.BytesReceived / e.TotalBytesToReceive;
...
Raus kommt 0.2 => 20%, also das richtige Ergebnis.
???😖 0.2 sind 2 Zehntel von 1. 20 % sind 20 von 100 oder 0.2 *100
...spätestens jetzt sollte Dir einleuchten, das dir der Multiplikator 100 fehlt... 0.2*100 =20... machts klick?
Ich erlöse dich einfach aus Deinem Leid...
demo.cs
using System;
using System.Net;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
public class Form1 : Form {
private Button DLButton;
private ProgressBar ProgressBar1;
private Label ProgressDisplay1,
ProgressDisplay2,
StateDisplay,
UriLabel;
private String DLUri = "http://addresses.loyce.club/Bitcoin_addresses_LATEST.txt.gz";
public Form1() {
this.Size = new Size(600,150);
DLButton = new Button();
DLButton.Text = "&Download";
DLButton.Location = new Point(10, 10);
DLButton.Click += new System.EventHandler(RunDownload);
this.Controls.Add(DLButton);
UriLabel = new Label();
UriLabel.Text = DLUri;
UriLabel.Location = new Point(100, 10);
UriLabel.AutoSize = true;
this.Controls.Add(UriLabel);
ProgressDisplay1 = new Label();
ProgressDisplay1.Text = String.Format("downloaded {0,12} of {1,12} bytes. {2,3} % complete...",0,0,0); //e.BytesReceived,e.TotalBytesToReceive,e.ProgressPercentage
ProgressDisplay1.Location = new Point(10, 40);
ProgressDisplay1.AutoSize = true;
this.Controls.Add(ProgressDisplay1);
ProgressDisplay2 = new Label();
ProgressDisplay2.Text = String.Format("FixpointPercentage:{0,7:f3} %",0); // (e.BytesReceived*100.0/e.TotalBytesToReceive)
ProgressDisplay2.Location = new Point(10, 60);
ProgressDisplay2.AutoSize = true;
this.Controls.Add(ProgressDisplay2);
StateDisplay = new Label();
StateDisplay.Location = new Point(200, 60);
StateDisplay.Text = "Waiting for Start download...";
StateDisplay.AutoSize = true;
this.Controls.Add(StateDisplay);
ProgressBar1 = new ProgressBar();
ProgressBar1.Value = 0;
ProgressBar1.Location = new Point(0, 80);
ProgressBar1.Size = new Size(ClientSize.Width,20);
this.Controls.Add(ProgressBar1);
//ProgressBar1.ForeColor = ColorTranslator.FromHtml("#400040"); //only posible if VisualStyles not Enabled
//ProgressBar1.Style = ProgressBarStyle.Continuous; //only posible if VisualStyles not Enabled
}
private void DownloadCompleted(object sender, AsyncCompletedEventArgs e){
StateDisplay.Text = "File Complete...";
}
private void DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e){
ProgressBar1.Value = e.ProgressPercentage;
ProgressDisplay1.Text = String.Format("downloaded {0,12} of {1,12} bytes. {2,3} % complete...",
e.BytesReceived,
e.TotalBytesToReceive,
e.ProgressPercentage
);
ProgressDisplay2.Text = String.Format("FixpointPercentage:{0,7:f3} %",(e.BytesReceived*100.0/e.TotalBytesToReceive)); //hier Deine Prozentrechnung ,ganz billig (auf 3 Nachkommastellen wird erst durch die Formatanweisung {0,7:f3} gerundet! einsetzen Parameter 0 Platzhalter, 7 Stellen rechtsbündig, Fixpoint 3 Nachkommastellen )
}
private void RunDownload(object sender, EventArgs e) {
DLButton.Enabled = false;
StateDisplay.Text = "Downloading...";
WebClient wc = new WebClient();
wc.DownloadFileCompleted += DownloadCompleted;
wc.DownloadProgressChanged += new DownloadProgressChangedEventHandler(DownloadProgressChanged);
wc.DownloadFileAsync(new Uri(DLUri), "test.bin");
}
static void Main() {
Application.EnableVisualStyles();
Application.Run(new Form1());
}
}
TL;DR: Fortschritt wird jetzt öfter aufgerufen aber berechneter Fortschritt in Prozent ist 0, obwohl es normal und schnell runter lädt.
Du hast komplett recht.
Das entfernen vom Console.ReadLine() macht es um einiges schneller (bemerkbar daran, dass die kleine Animation neben dem Ladebalken schneller abgespielt wird, da die am aktualisieren des Fortschrittes hängt).
Leider komme ich trotzdem nicht über einen Fortschritt von einem Prozent. Habe mir die Menge an Bytes und den Download-Fortschritt jetzt mal so ausgeben lassen, dass ich den berechneten Fortschritt (bytesGeladen / gesamtBytes * 100) und die zahl der geladenen Bytes angezeigt bekomme.
Die Zahl an Bytes erhöht sich unglaublich schnell und erreicht auch recht fix die erwarteten Mengen. Das bedeutet dass der Download an sich zu funktionieren scheint. Das Reflektiert sich auch dann wenn man sich die Dateigröße des Downloads über die Dateiinformationen ausgeben lässt.
Erreicht der Download nun sein Ende, dann geht der Fortschritt ganz am Ende auf 100 (mit der Berechnung so: double percentage = bytesIn / totalBytes). Dann wird mein Download-Fortschritts-Event nicht mehr aufgerufen. Mein End-Event aber schon.