[de] Große Dateien übertragen

Topics: Technical Support
Jan 21, 2013 at 11:35 AM

Hallo,

unsere Client-Server Anwendung benutzt Zyan als Kommunikationsschicht. Ich würde gerne auch Dateien übertragen lassen (zunächst erstmal nur vom Server zum Client) um Updates zu verteilen.

In einem früheren Post zu dieser Frage, wurde davon abgeraten, zyan dafür zu verwenden.

Ist das in der jetzigen Version immer noch der Fall? Wie könnte ich stattdessen Dateien sinnvoll übertragen?

Coordinator
Jan 21, 2013 at 8:17 PM

Hallo MyKey0815,

es kommt darauf an, was Du genau machen willst.
Wenn es darum geht einen reinen Dateiserver zu programmieren, gibt es vermutlich performantere Kommunikationssysteme dafür, als Zyan.

Falls das Übertragen von Dateien aber eher eine Zusatzfunktion (z.B. Updates) ist, spricht grundsätzlich nichts dagegen, das auch mit Zyan zu tun.

Generell werden Dateien in Form von Byte-Arrays, also byte[] übertragen. Wenn es große Dateien sind, sollte man die Dateien nicht am Stück übertragen, sondern in kleinen Stückchen (engl. chunks) übers Netzwerk schicken. So kannst Du eine Fortschrittsmessung einbauen und musst bei einem Fehler nicht die ganze große Datei von vorn übertragen. Allerdings erhöht sich dadurch auch der Netzwerk-Overhead, da jeder Chunk über in Form eines Methodenaufrufs auf dem Server übertragen wird. Bei jedem Methodenaufruf muss Zyan Korrelationsdaten übertragen (Name der aufzurufenden Methode, Typeninformationen, Kryptografieschlüssel usw.). Das sind zwar immer nur wenige Byte, pro Aufruf, aber Kleinvieh macht bekanntlich auch Mist.

Für den Dateitransfer könntest Du Dir zwei Komponenten schreiben. Eine Sender-Komponente und eine Empfänger-Komponente.

Die Sender-Komponente liest die zu versendende Datei über eine FileStream-Objekt. Du definierst vorher, wie groß die Chunks sein sollen (z.B. 32768 Byte = 32 KB). Zu klein sollten sie nicht sein, damit der Overhead im Rahmen bleibt. Über das FileStream-Objekt wird immer nur ein Chunk eingelesen. Dieser wird dann übers Netzwerk an die Empfängerkomponente versendet. Dann wird der nächste Chunk eingelesen und versendet. Das geht innerhalb einer Schleife so weiter, bis das Ende der Datei ereicht ist. Die Sender-Komponente sollte am Anfang der entfernten Empfänger-Komponente mitteilen, wie lang die Datei insgesamt ist und natürlich wie die Datei heißt. 

Die Empfänger-Komponente verwendet ebenfalls ein FileStream-Objekt, um die empfangene Datei zu schreiben. Dabei werden die empfangenen Chunks in den Datenstrom geschrieben. Die Empfänger-Komponente muss die vielen einzelnen Aufrufe zum Übertragen der Chunks korrelieren. Es könnten ja verschiedene Clients gleichzeitig verschiedene Dateien hochladen. Dann darf die Empfänger-Komponente nicht durcheinander kommen. Deshalb sollte sie beim Start eines Dateiuploads eine eindeutige Kennung für die Upload-Sitzung vergeben. Diese Kennung wird an die Sender-Komponente übermittelt. Diese sendet die Kennung mit jedem übertragenen Chunk mit (z.B. als zusätzlichen Parameter), damit die Empfänger-Komponente zuordnen kann, zu welchem laufenden Upload der empfangene Chunk gehört. Außerdem sollte zu jedem Chunk eine Reihenfolgennummer übermittelt werden. Dadurch wird sicher gestellt, dass die Datei auch dann korrekt geschrieben wird, wenn z.B. aufgrund von Netzwerkproblemen die Pakete nicht in der korrekten Reihenfolge eintreffen. 

Wenn man diese zwei Komponenten einmal geschrieben hat, kann man sie für beliebige Dateitransfers einsetzen.

Gruß

Rainbird

Jan 22, 2013 at 6:57 AM
Edited Jan 22, 2013 at 7:01 AM

Hallo Rainbird,

vielen Dank für deine ausführliche Antwort. Ich hab mich dann auch gleich dran gemacht, deinen Vorschlag umzusetzen. Aber leide musste ich dann nach einiger Zeit aufgeben. Irgentwie bekam ich Zyan nicht in das Gesamtbild.

Es ist vorgesehen, das nur Updates oder so übertragen werden. Also kein Ersatz für einen Fileserver oder so. Aktuell sind es nur die Programmupdates, die sich so max. bei 10 MB Gesamtvolumen (wenn die Anwendung mal fertig ist) sein wird. Vielleicht werde ich zukünftig auch noch die Berichte (als PDF) lokal laden können - aber auch das sind nur Dateien von max. ein paar 100 KB.

Frage: Du sprichst hier von "Komponenten". Sind das zwei unterschiedliche Klassen oder wie ist das gemeint?

Wie kommt die Kommunikation zustande? Du schreibst, das ich das in Chunks einteilen soll, aber wie sieht dann das beim Empfänger aus? Dort läuft doch auch ein FileStream-Objekt, was auf Daten wartet. Oder bin ich da völlig falsch.

@alle: Vielleicht hat ja jemand schon mal so was in der Art implementiert und könnte hier das "Gerüst" posten bzw. einen Link, der mir da auf die sprünge helfen kann. Wenn ich nach WCF oder .NET Remoting und Datentransfer suche, kommen halt immer sehr spezielle Lösungen für die Verwendung dieser Techniken.

Vielen Dank im Vorraus

Michael

Coordinator
Jan 22, 2013 at 2:18 PM
Edited Jan 22, 2013 at 2:35 PM
MyKey0815 wrote:

@alle

Hi MyKey0815,
sorry I can't answer in German.

Please have a look at stream handling in Zyan. Here is an example (unit tests for streams):
http://zyan.codeplex.com/SourceControl/changeset/view/29230#428499 

The basic idea is simple: return a Stream instead of byte[]. .NET Remoting supports streams out-of-the-box, so Zyan supports them automatically. If you're not going to build a highly loaded file server, then possible negative impact on scalability caused by streams will most likely be unnoticeable.

Pros:

  • The solution is very simple: just return a Stream instance when you need to pass a file to a client.
  • You don't have to create you own utility classes to keep track of the opened files, etc.
  • You can track the progress if you wish (i.e. display a progress bar while downloading a file) or simply use Stream.CopyTo() if you don't need to.

Contras: 

  • Returning a stream from a server actually means registering a client-activated object on the server. Stateful objects reduce application scalability (i.e., downloading thousands files at once may temporary render your server out of resources).
  • You are obliged to close your streams to free server's resources as quickly as possible.
  • The server will keep your streams open for a while if your connection is broken (although this might probably be worked around).

The choice between streams and byte arrays depends on the usage scenario.
If I had to transfer a few large files (say, larger than 5-10 megabytes), I'd use streams.
But for series of small files (less than 10-100 kilobytes), I'd rather use byte arrays.

Greetings, yallie.