AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

Multi Socket Transfer

Ein Thema von Zodi · begonnen am 19. Jul 2017 · letzter Beitrag vom 17. Sep 2017
Antwort Antwort
Seite 1 von 2  1 2      
Benutzerbild von Zodi
Zodi

Registriert seit: 18. Jul 2017
Ort: Berlin
45 Beiträge
 
Delphi XE7 Ultimate
 
#1

Multi Socket Transfer

  Alt 19. Jul 2017, 00:57
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.
Pascal

Geändert von Zodi (19. Jul 2017 um 00:59 Uhr)
  Mit Zitat antworten Zitat
SebastianZ

Registriert seit: 23. Jul 2009
89 Beiträge
 
Delphi 11 Alexandria
 
#2

AW: Multi Socket Transfer

  Alt 19. Jul 2017, 10:54
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.

Geändert von SebastianZ (19. Jul 2017 um 10:56 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von scrat1979
scrat1979

Registriert seit: 12. Jan 2007
Ort: Sulzbach a.d. Murr
1.028 Beiträge
 
Delphi 10.4 Sydney
 
#3

AW: Multi Socket Transfer

  Alt 19. Jul 2017, 12:00
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.
Michael Kübler
  Mit Zitat antworten Zitat
mensch72

Registriert seit: 6. Feb 2008
838 Beiträge
 
#4

AW: Multi Socket Transfer

  Alt 19. Jul 2017, 12:37
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!
  Mit Zitat antworten Zitat
Benutzerbild von Zacherl
Zacherl

Registriert seit: 3. Sep 2004
4.629 Beiträge
 
Delphi 10.2 Tokyo Starter
 
#5

AW: Multi Socket Transfer

  Alt 19. Jul 2017, 14:17
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.

- 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.

- 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 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 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 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 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
Projekte:
- GitHub (Profil, zyantific)
- zYan Disassembler Engine ( Zydis Online, Zydis GitHub)

Geändert von Zacherl (19. Jul 2017 um 14:33 Uhr)
  Mit Zitat antworten Zitat
mensch72

Registriert seit: 6. Feb 2008
838 Beiträge
 
#6

AW: Multi Socket Transfer

  Alt 19. Jul 2017, 17:53
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
  Mit Zitat antworten Zitat
mjustin

Registriert seit: 14. Apr 2008
3.004 Beiträge
 
Delphi 2009 Professional
 
#7

AW: Multi Socket Transfer

  Alt 20. Jul 2017, 09:36
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
Michael Justin
  Mit Zitat antworten Zitat
mensch72

Registriert seit: 6. Feb 2008
838 Beiträge
 
#8

AW: Multi Socket Transfer

  Alt 20. Jul 2017, 10:16
..."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.
  Mit Zitat antworten Zitat
mjustin

Registriert seit: 14. Apr 2008
3.004 Beiträge
 
Delphi 2009 Professional
 
#9

AW: Multi Socket Transfer

  Alt 20. Jul 2017, 10:24
..."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?
Michael Justin

Geändert von mjustin (20. Jul 2017 um 10:29 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von Zacherl
Zacherl

Registriert seit: 3. Sep 2004
4.629 Beiträge
 
Delphi 10.2 Tokyo Starter
 
#10

AW: Multi Socket Transfer

  Alt 20. Jul 2017, 14:25
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 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).

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 Ich persönlich verzichte allerdings meistens auf die Quittierung sämtlicher Blöcke, sondern schicke nur im Fehlerfalle ein Paket.

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 ) 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.

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.
Projekte:
- GitHub (Profil, zyantific)
- zYan Disassembler Engine ( Zydis Online, Zydis GitHub)
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 2  1 2      


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 17:22 Uhr.
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