Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Netzwerke (https://www.delphipraxis.net/14-netzwerke/)
-   -   Multi Socket Transfer (https://www.delphipraxis.net/193338-multi-socket-transfer.html)

Zodi 19. Jul 2017 00:57

Multi Socket Transfer
 
Hallo Delphi Programmierer.

Ich habe 2 Anwendungen die eine läuft als Client und die andere als Server.
Dabei benutze ich die TClient/TServer Socket.
Um mehrere Dateien gleichzeitig zu senden erstelle ich zur laufzeit immer wieder einen Client der die entsprechende Datei dann sendet.
Wenn ich z.b 2 oder 3 Dateien gleichzeitig versende funktioniert alles gut.
Aber sobald es dann mehr als 4 Dateien sind die gleichzeitig gesendet werden, passiert es dann das die empfangenen Dateien manchmal fehlerhaft sind.
Zum Testen werden meistens Bilder verwendet und die haben dann fehler wenn man sie anschaut.
Sobald ein neuer Client erstellt wird sendet dieser die Datei so.....
Delphi-Quellcode:
    FileHandle := CreateFile(pchar(File_Pfad),GENERIC_READ,FILE_SHARE_READ,nil,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
    FileSize := GetFileSize(FileHandle,nil);
    SendData(Socket,'FileTransfer|OpenFile|' + inttostr(FileSize));

     while FileNotEnd do begin

        if FileSize - 4096 > 0 then begin
           SetLength(Data,4096);
           ReadFile(FileHandle,pointer(Data)^,4096,BytesRead,0);
           FileSize := FileSize - 4096;
           SendData(Socket,'FileTransfer|WriteFile|' + Data);
           Sleep(10);  
        end else begin
           SetLength(Data,FileSize);
           ReadFile(FileHandle,pointer(Data)^,FileSize,BytesRead,0);
           SendData(Socket,'FileTransfer|WriteFile|' + Data);
           Sleep(10);
           FileNotEnd := False;
           SendData(Socket,'FileTransfer|CloseFile');
           CloseHandle(FileHandle);
        end;
     end;
Delphi-Quellcode:
procedure SendData(Socket: TClientSocket; Data: string);
var
  Temp: ansistring;
begin
  while (Length(Data) > 0) and (Socket.Connected) do begin
    Temp := AnsiString(Copy(Data,1,4096));
    Delete(Data,1,4096);
    repeat
    Sleep(5);
    until Socket.SendBuffer(pointer(Temp)^,length(Temp)) <> -1;
    sleep(5);
  end;
end;
Sende ich zu schnell so das die Sockets nicht hinterher kommen?
Komisch ist das es ersst auftritt sobald ich mehr als 4 Bilder Gleichzeitig versende.

Danke schonmal im voraus.

SebastianZ 19. Jul 2017 10:54

AW: Multi Socket Transfer
 
Das Bild, das beim senden von 4 oder mehr Probleme macht funktioniert, wenn du es einzeln sendest? Merkwürdig.
Generell würde ich ein Bild oder eine andere Datei nicht in einen String laden und dann als AnsiString casten, dass kann dir Steuerzeichen in der Datei zerstören. Besser wäre hier die Datei zB als Base64 zu kodieren, oder direkt mit Bytes(oder Bytestream) zu arbeiten, ohne diese in einen String zu schreiben.

scrat1979 19. Jul 2017 12:00

AW: Multi Socket Transfer
 
Ich weiß ja nicht wie weit dein Projekt fortgeschritten ist. Habe mich selbst mit der Client-Server-Programmierung beschäftigt und quasi ALLE Socket-Componenten getestet. Hängengeblieben bin ich bei sgcWebSocket. Daraus habe ich mir innerhalb weniger Tage eine eigene Komponente abgeleitet welche nun alle meine Bedürfnisse perfekt abdeckt (ich benutze wahrscheinlich nur maximal 10% des Leistungsumfangs). Meine Programme laufen lokal und über das Internet schnell und stabil. Und das, obwohl ich mich nicht als Profi sondern höchstens als fortgeschritten in Sachen Programmierung bezeichnen würde. Für den privaten Gebrauch ohne Source kostenlos, die Vollversion inkl. Source kostet ca. 150€ ist aber jeden Cent wert.

Vielleicht wäre das einen Versuch - auch für die Zukunft - wert.

mensch72 19. Jul 2017 12:37

AW: Multi Socket Transfer
 
wie willst du wissen, ob du eventuell zuschnell zuviel sendest, wenn du das nirgends abfragst und/oder auswertest ?!

- Ich garantiere dir, über eine langsame WLan oder INet Verbindung zu einem Server im INet bekommst du "so" schon ab der ersten Dateiübertragung Probleme!
- auch wenn es mit AnsiString als Datenpuffer gerade (noch) funktioniert, man macht es nicht. Nimm "TBytes" und bei Casts nie typenlose "Pointer" sondern immer den passenden Typ, hier z-B. "PByteArray"
- über Sockets ist die Mischung aus StringCMDs und folgenden Binärdaten durchaus nix ungewöhnliches, nur macht man das besser in zwei Aufrufen wo der ersten im Klartext ankündigt, was dann binär kommt... besser ist es dort auch den Offset und die erwartete Länge vorab mit zu geben und erst dann die Binärdaten zu schicken
- noch besser wir es, wenn man sich selbst synchronisiert, in dem der Empfänger den Empfang der Daten durch rücksenden des empfangenen Blocks (hier also des StartOffsets) "quittiert" und man erst dann den nächsten Datenblock in den Socket zum Senden reinschreibt.
- sicher wird es, wenn der Empfänger bei seiner Quiitung noch eine Checksumme über die empfangenen Daten zurück schickt, welche der Sender vergleicht und bei Fehler den Datenblock z.B. nochmal wiederholt
- wichtig ist irgendein Sync!... man soll und darf den OS-TCPIP-Stack nicht als quasi unendlich großen Datenpuffer mißbrauchen!

Zacherl 19. Jul 2017 14:17

AW: Multi Socket Transfer
 
Die wichtigste Frage wäre, ob du die Sockets im "blocking" oder "non-blocking" Mode verwendest. Ich vermute, dass letztere Variante der Fall ist, denn nur dann hast du die von mensch72 beschriebenen Probleme. Im blocking Mode blockiert der MSDN-Library durchsuchensend bzw. MSDN-Library durchsuchenrecv Aufruf solange, bis die entsprechenden Buffer wieder Platz haben.

Zitat:

Zitat von mensch72 (Beitrag 1377018)
- noch besser wir es, wenn man sich selbst synchronisiert, in dem der Empfänger den Empfang der Daten durch rücksenden des empfangenen Blocks (hier also des StartOffsets) "quittiert" und man erst dann den nächsten Datenblock in den Socket zum Senden reinschreibt.
- sicher wird es, wenn der Empfänger bei seiner Quiitung noch eine Checksumme über die empfangenen Daten zurück schickt, welche der Sender vergleicht und bei Fehler den Datenblock z.B. nochmal wiederholt

Das ist beides bei Verwendung von TCP komplett überflüssig und verursacht nur unnötigen Overhead. TCP garantiert sowohl die komplette, als auch korrekte Zustellung sämtlicher Daten in unveränderter Reihenfolge.

Zitat:

Zitat von mensch72 (Beitrag 1377018)
- wichtig ist irgendein Sync!... man soll und darf den OS-TCPIP-Stack nicht als quasi unendlich großen Datenpuffer mißbrauchen!

Man KANN (unter Windows) sogar nichtmal. Bei blocking Sockets hat man dieses Problem auch nicht, da die MSDN-Library durchsuchensend Aufrufe solange blockieren, bis der Empfänger mit MSDN-Library durchsuchenrecv eine ausreichende Datenmenge empfangen hat (und somit wieder Platz im Sendebuffer frei ist). Dennoch sollte man die Datenmenge sowohl bei MSDN-Library durchsuchensend, als auch bei MSDN-Library durchsuchenrecv begrenzen (z.b. auf 64KiB Blöcke) und dann in einer Schleife senden/empfangen.

Etwas komplizierter ist es bei "non-blocking" Sockets. Hier muss die Rückgabe von MSDN-Library durchsuchensend überprüft werden, da nicht garantiert ist, dass die API sämtliche Daten in einem Rutsch verschickt.
Zitat:

Zitat von MSDN send
If no error occurs, send returns the total number of bytes sent, which can be less than the number requested to be sent in the len parameter. Otherwise, a value of SOCKET_ERROR is returned

Zitat:

Zitat von MSDN send
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. On nonblocking stream oriented sockets, the number of bytes written can be between 1 and the requested length, depending on buffer availability on both the client and server computers.

Zitat:

Zitat von MSDN recv
If no error occurs, recv returns the number of bytes received and the buffer pointed to by the buf parameter will contain this data received. If the connection has been gracefully closed, the return value is zero.
Otherwise, a value of SOCKET_ERROR is returned

Zitat:

Zitat von MSDN recv
For connection-oriented sockets (type SOCK_STREAM for example), calling recv will return as much data as is currently available—up to the size of the buffer specified. [..] If no incoming data is available at the socket, the recv call blocks and waits for data to arrive [..] unless the socket is nonblocking. In this case, a value of SOCKET_ERROR is returned


mensch72 19. Jul 2017 17:53

AW: Multi Socket Transfer
 
mir ist die vom Layer bei TCP hier schon garantierten vollständigen und gesicherten Übertragung (im gegensatz z.B. zu UDP) durchaus klar und auch der Unterschied zw. "blocking"(hoffentlich Thread basiert) oder "non-blocking"(hoffentlich eventbasiert) ist bekannt:)

Aber wenn ich den ersten Quelltext hier sehe, glaube ich das man ohne viel Nachdenken und Zeitaufwand es mit einer resultierend durch Übertragungszeit und Datenoverhead verlangsamenden eigenen logischen Quittung als Einsteiger leichter hin bekommt, als wenn man alles nur auf Basis der SocketStates(error, busy oder ready) verarbeiten und strukturieren soll.

-> Hier möchte jemand mehr Funktions&Datensicherheit, also kann er nach meiner Einschätzung&Erfahrung die so mit wenig (Lern)Aufwand weiter mit seinen aktuellen Mitteln reicht einfach erreichen.
-> Und ja, auch wenn es dem allgemeinem Ansatz der "eh schon per Layer gesicherten) Übertragung per TCP wiederspricht, ich finde logische Blockquittungen seitens des Empfängers sinnvoll, wenn dieser damit anzeigt das er die Daten erfolgreich empfangen UND VERARBEITET hat(in dem Fall Block erfolgreich auf HD gespeichert).
=> Möge der Fragende selbst entscheiden ob er sich weiter voll auf den Stack und dann nötige Auswertung von dessen States&Errors verlässt, oder sich weiter um nichts kümmert und es mit einer einfachen zusätzlichen eigenen Quittung selbst löst(und sein Ausgagsproblem so dann eben umgeht).

Da ich falsches Verhalten des Sockets ausschließe, wäre eine mögliche "non blocked" Erkläung für beschriebenen Effekt: der Socket hat beim Send die per Pointer übergebenen Daten nicht selbst dupliziert, sondern überträgt diese direkt ... heißt wenn dies in 10ms wie hier programmiert nicht fertig, würden durch den nächsten FileRead die Daten mit neuen Werten überschrieben.
Klar, wenn es ein "blocked Socked ist, kann&darf der garnicht zurückkommen, bevor nicht alle Daten "bestätigt" versendet sind... aber warum verwendet hier dann einer noch ein Sleep(10)???... ist es eventuell eben doch eine NonBlocked Übertragung???
=> Genau weil da wo ich bin sich NIEMALS jemand um sowas Gedanken machen will(schon garnicht beim Lesen fremder Quelltexte), gilt bei uns die goldene Regel: wir quittieren alles logisch und sei es über einen 2. Kanal (z.B. anderer Port,UDP,...)


OffTopic:
Nach Layerdefinition garantiert der Stack das TCP "100% sicher" ist... eventuell ist es unter Linux so aber Windows mag es reproduzierbar garnicht, wenn ich zuviel parallel mit einmal in/an den Stack gebe.
Wenn ich optimale Übertragung fast ohne Overhead will, bleibe ich mit meinem PayLoad knapp unter der aktuell verfügbaren FrameSize im Netzwerk.
Leider programmiere ich auch netzwerkfähige Embedded Microcontroler... da gibt es dann zwar auch einen TCP-Socket, nur der hat sehr oft seine physikalischen Grenzen(z.B. DMA-SpeicherpufferGröße)... daher quäle ich diesen nicht und übertrage nicht mehr als in ein Paket was in den internen Puffer passt und etwas unter der NetzwerkFrameSize liegt. Dann warte ich bis es die Gegenstelle "logisch" quittiert.
Nur so funktioniert es problemlos von 8..64Bit und im Speed von GPRS bis GigaBit, auch wenn man per Definition sich bei TCP-Sockets um sowas selbst garnicht mehr kümmern soll:)

mjustin 20. Jul 2017 09:36

AW: Multi Socket Transfer
 
Zitat:

Zitat von Zodi (Beitrag 1376997)
Um mehrere Dateien gleichzeitig zu senden erstelle ich zur laufzeit immer wieder einen Client der die entsprechende Datei dann sendet.

Woran erkennt der Server das Ende der Datei, wenn die Dateilänge nicht vorab übermittelt wird, und auch kein Ende-Byte gesendet werden kann (da es Binärdaten sind, daher dieses Endebyte enthalten sein kann)?

Die Lösungen wären also:

* Dateilänge vorab senden
* oder: Base64 Encoding verwenden und ein Null-Endebyte senden

Das es dennoch funktioniert, liegt daran dass der Server-Code beim Ausbleiben von Daten 'optimistisch' annimmt es sei das Ende der Datei erreicht ;)

mensch72 20. Jul 2017 10:16

AW: Multi Socket Transfer
 
..."Woran erkennt der Server das Ende der Datei"...?
=> steht doch oben im Quelltext... zum Schluss wird ein "CloseFile" als logische SteuerInformation an den Client geschickt... ist zwar für den Client nicht überprüfbar ob er alles ahat, aber es funktioniert wenn alles gut geht so durch aus:)

Eine "Base64" Übertragung gibt 25% Overhead... muss nicht sein wenn man sich wie aktuell auf den TCP Stream verlässt oder eben besser doch Offset&Länge noch als SteuerInfo mit überträgt.

mjustin 20. Jul 2017 10:24

AW: Multi Socket Transfer
 
Zitat:

Zitat von mensch72 (Beitrag 1377068)
..."Woran erkennt der Server das Ende der Datei"...?
=> steht doch oben im Quelltext... zum Schluss wird ein "CloseFile" als logische SteuerInformation an den Client geschickt... ist zwar für den Client nicht überprüfbar ob er alles ahat, aber es funktioniert wenn alles gut geht so durch aus:)

Prüft der Client auf "CloseFile"? Vermutlich ja, aber ohne den Code zu sehen kann man schwer sagen ob er noch Fehler enthält.

p.s. der Server sendet die Dateien 'gleichzeitig'. Werden mehrere Threads erzeugt? Wie sieht der Code dazu aus?

Zacherl 20. Jul 2017 14:25

AW: Multi Socket Transfer
 
Zitat:

Zitat von mensch72 (Beitrag 1377035)
Aber wenn ich den ersten Quelltext hier sehe, glaube ich das man ohne viel Nachdenken und Zeitaufwand es mit einer resultierend durch Übertragungszeit und Datenoverhead verlangsamenden eigenen logischen Quittung als Einsteiger leichter hin bekommt, als wenn man alles nur auf Basis der SocketStates(error, busy oder ready) verarbeiten und strukturieren soll.

Ist sicherlich Geschmackssache und kommt auf die Situation an :P Wenn man das Prinzip der blocking Sockets verstanden hat, ist es sicherlich kein Problem nach dem Versenden noch kurz auf ein schnelles "Ack"-Paket vom Server zu warten (wobei man dann auch hier noch Timeouts etc. implementieren muss, für den Fall, dass der Server aus irgendeinem Grund nicht zum Antworten kommt). Persönlich finde ich es aber auch nicht komplizierter, grade einen Rückgabewert zu prüfen (was man ja sowieso eigentlich in jedem Falle machen sollte).

Zitat:

Zitat von mensch72 (Beitrag 1377035)
ich finde logische Blockquittungen seitens des Empfängers sinnvoll, wenn dieser damit anzeigt das er die Daten erfolgreich empfangen UND VERARBEITET hat(in dem Fall Block erfolgreich auf HD gespeichert).

Das ist natürlich immer sinnvoll sein bei Operationen, die fehlschlagen könnten :thumb: Ich persönlich verzichte allerdings meistens auf die Quittierung sämtlicher Blöcke, sondern schicke nur im Fehlerfalle ein Paket.

Zitat:

Zitat von mensch72 (Beitrag 1377035)
Nach Layerdefinition garantiert der Stack das TCP "100% sicher" ist... eventuell ist es unter Linux so aber Windows mag es reproduzierbar garnicht, wenn ich zuviel parallel mit einmal in/an den Stack gebe.

Puh, das kann ich bei mir (zum Glück :stupid:) nicht reproduzieren. Hatte mal eine Anwendung, die auch sehr viele Dateien parallel sendet, indem mehrere Threads mit jeweils eigenem Client-Socket erstellt wurden (ist letztlich an der maximalen Anzahl von Threads per Anwendung gescheitert; unterhalb dieser Grenze lief das aber wunderbar). Nach diesem Versuch habe ich ein Protokoll entwickelt, welches beliebig viele Dateien parallel über ein einzelnes Socket streamen kann (wollte on-the-fly compression, Prioritäten, etc.). Auch hier hatte ich selbst bei sehr großer Blockgröße (teilweise über 100MiB auf Gigabit Servern) kein Problem.

Zitat:

Zitat von mjustin (Beitrag 1377061)
Dateilänge vorab senden

Zumindest mal die Länge würde ich bei Übertragung von Binärdaten wirklich immer vorherstellen! Alleine schon, um Teilpakete ggfls. wieder korrekt separieren bzw. zusammensetzen zu können. TCP garantiert nämlich NICHT, dass zwei Aufrufe von MSDN-Library durchsuchensend auch in zwei Aufrufe von MSDN-Library durchsuchenrecv resultieren. Ganz im Gegenteil werden mehrere kleine Pakete dann nämlich in einem Rutsch empfangen und müssen per Hand getrennt werden. Analog dazu führen extreme Blockgrößen bei MSDN-Library durchsuchensend dazu, dass ein einzelnes Paket in mehreren Schritten übertragen wird und dann erst reassembliert werden muss.


Alle Zeitangaben in WEZ +1. Es ist jetzt 06:12 Uhr.
Seite 1 von 2  1 2      

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz