AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Netzwerke Delphi Spielwiese - SocketTest

Spielwiese - SocketTest

Ein Thema von stahli · begonnen am 7. Okt 2016 · letzter Beitrag vom 24. Mär 2017
Antwort Antwort
Seite 3 von 4     123 4   
Benutzerbild von stahli
stahli

Registriert seit: 26. Nov 2003
Ort: Halle/Saale
4.199 Beiträge
 
Delphi 10.4 Sydney
 
#21

AW: Spielwiese - SocketTest

  Alt 30. Okt 2016, 19:38
Ok, hier mal das Projekt (XE3 und Exen) und ein neues Video: https://youtu.be/6MBQD-HJLGw

Das Video dauert 40 min. Es ist also sicher nur interessant für diejenigen, die an asynchroner Kommunikation direktes Interesse haben.
(Dies ist übrigens mein 4. Aufnahmeversuch gewesen, da ich bei vorherigen Versuchen diverse Probleme und Abbrüche bis hin zu einem BlueScreen hatte. Also bitte seht mir nach, wenn es nicht so ganz strukturiert ist. Der Nerv-Faktor war schon etwas erhöht. Notfalls kann ich ja nochmal nachliefern.)

Ich denke, der Ansatz könnte ganz tauglich sein.
Aber sicher sind noch einige Bugs und Optimierungsmöglichkeiten vorhanden.

Auch das Problem mit dem gelegentlichen Fehler beim Abmelden eines Clients konnte ich noch nicht vollständig lösen.


Da ich das Konzept jetzt mal in einem anderen Projekt umsetzen will würde mich mal interessieren, was Ihr dazu meint und wer daran auch selbst Interesse hat.
Insbesondere ist natürlich interessant, wenn Ihr hier Probleme erkennt bzw. Lösungen für diese habt.


Natürlich ist das Projekt recht komplex und umfangreich, aber wenn wir eine stabile Version erreichen könnte das m.E. ein sehr nützliches Package für eine allgemeine und einfache asynchrone Kommunikation werden.


Es wäre also nett, wenn Ihr mal Rückmeldungen bezüglich Interesse und Verbesserungen gebt.


Gruß Stahli
Angehängte Dateien
Dateityp: zip soMessageTransfer.zip (265,2 KB, 17x aufgerufen)
Dateityp: zip soMessageTransferRun.zip (1,72 MB, 16x aufgerufen)
Stahli
http://www.StahliSoft.de
---
"Jetzt muss ich seh´n, dass ich kein Denkfehler mach...!?" Dittsche (2004)
  Mit Zitat antworten Zitat
Benutzerbild von stahli
stahli

Registriert seit: 26. Nov 2003
Ort: Halle/Saale
4.199 Beiträge
 
Delphi 10.4 Sydney
 
#22

AW: Spielwiese - SocketTest

  Alt 28. Nov 2016, 15:46
Ich nutze das Paket jetzt etwas erweitert in einem realen Programm. Das macht auf meinem Win10-Rechner und Win10-Laptop auch schon einen sehr guten Eindruck.

Auf meinem dienstlichen Rechner (Win7 Prof, 32bit) werden im Client aber nicht alle Daten dargestellt. Teilweise funktioniert es, aber nicht vollständig oder nur kurze Zeit.
Eine TCP-Verbindung besteht zumindest anfangs mal.

Welche einfachste Möglichkeit gibt es, den TCP-Traffic von außen zu loggen (ohne selbst etwas einzubauen)?
Wenn ich sehen würde, ob überhaupt noch Nachrichten ausgetauscht werden, könnte ich das Problem vielleicht schon mal etwas eingrenzen.

Ich habe hier keine Adminrechte und kann daher nichts installieren.
Gibt es dennoch eine Möglichkeit, den Traffic etwas einzusehen?
Stahli
http://www.StahliSoft.de
---
"Jetzt muss ich seh´n, dass ich kein Denkfehler mach...!?" Dittsche (2004)
  Mit Zitat antworten Zitat
Aviator

Registriert seit: 3. Jun 2010
1.584 Beiträge
 
Delphi 10.3 Rio
 
#23

AW: Spielwiese - SocketTest

  Alt 28. Nov 2016, 17:46
Welche einfachste Möglichkeit gibt es, den TCP-Traffic von außen zu loggen (ohne selbst etwas einzubauen)?
Wenn ich sehen würde, ob überhaupt noch Nachrichten ausgetauscht werden, könnte ich das Problem vielleicht schon mal etwas eingrenzen.
Schau dir mal WireShark an. Damit kannst du den gesamten Traffic auf deiner Netzwerkkarte beobachten.

EDIT:

Ich habe hier keine Adminrechte und kann daher nichts installieren.
Oh zu spät gelesen. Sorry.
Aber vielleicht gibt es ja eine Version die als Portable genutzt werden kann.

EDIT 2: Ja auf der DownloadSeite gibt es eine Portable Version als 32bit Version.

Geändert von Aviator (28. Nov 2016 um 17:50 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von stahli
stahli

Registriert seit: 26. Nov 2003
Ort: Halle/Saale
4.199 Beiträge
 
Delphi 10.4 Sydney
 
#24

AW: Spielwiese - SocketTest

  Alt 29. Nov 2016, 09:43
Vielen Dank.

Ich habe mir jetzt aber doch ein eigenes provisorisches Logging eingebaut (aus Zeitgründen noch nicht fertig).
Ich kann schon mal sehen, dass beim Client nicht alles ankommt.
Wird irgendwas beim Server klemmen.
Mal schauen...
Stahli
http://www.StahliSoft.de
---
"Jetzt muss ich seh´n, dass ich kein Denkfehler mach...!?" Dittsche (2004)
  Mit Zitat antworten Zitat
Benutzerbild von Zacherl
Zacherl

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

AW: Spielwiese - SocketTest

  Alt 29. Nov 2016, 19:43
Bei den non-blocking Sockets musst du beim Senden aufpassen:
Zitat von MSDN:
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.
Zitat von MSDN:
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.
Also müsstest du gegebenenfalls nach einem nicht vollständigen MSDN-Library durchsuchensend()-Versuch manuell den Rest in einem/mehreren subsequenten send() Calls hinterherschicken.

Was zusätzlich immer passieren kann (blocking und non-blocking), dass die Daten eines einzigen send()-Aufrufs in mehreren recv()-Aufrufen ankommen. Irgendeine Form von Pakettrennung muss man also zwingend selbst implementieren (Längenbasiert oder anhand von Trennzeichen). Aber das machst du soweit ich mich erinnere ja indirekt mit deinen String-Listen.
Projekte:
- GitHub (Profil, zyantific)
- zYan Disassembler Engine ( Zydis Online, Zydis GitHub)
  Mit Zitat antworten Zitat
Benutzerbild von stahli
stahli

Registriert seit: 26. Nov 2003
Ort: Halle/Saale
4.199 Beiträge
 
Delphi 10.4 Sydney
 
#26

AW: Spielwiese - SocketTest

  Alt 8. Dez 2016, 09:02
Ich habe den Übeltäter.

Längere Texte werden ggf. bei der Übertragung gesplittet.

Mein Handler hatte hier noch einen Bug und hat die unvollständigen Teile schon verarbeitet. Das Problem trat auf meinen 2 Rechnern nicht auf, aber bei allen anderen getesteten. Das lag wohl daran, wie die einzelnen Threads jeweils zeitlich zum arbeiten kommen.


Also falls das jemand nutzen will dann hier ein Bugfix:
Delphi-Quellcode:
function TsoConnectionStringList.GetNextSL: TStringList;
var
  C: Integer;
begin
  fCS.Enter;
  try
    if (fIndex < fItems.Count) then
    begin
      C := StrToInt(fItems[fIndex]);
      fItems[fIndex] := '';
      Inc(fIndex);
      Result := TStringList.Create;
      while (C > 0) do
      begin
        Result.Add(fItems[fIndex]);
        fItems[fIndex] := '';
        Inc(fIndex);
        Inc(fTransferCounter);
        Dec(C);
      end;
      if (fIndex >= fItems.Count) then
      begin
        fItems.Clear;
        fIndex := 0;
      end
      else
      begin
        if (fIndex > 1000) then
        begin
          while (fItems.Count > 0) and (fItems[0] = '') do
          begin
            fItems.Delete(0);
            Dec(fIndex);
          end;
        end;
      end;
    end
    else
      Result := nil;
  finally
    fCS.Leave;
  end;
end;
Stahli
http://www.StahliSoft.de
---
"Jetzt muss ich seh´n, dass ich kein Denkfehler mach...!?" Dittsche (2004)
  Mit Zitat antworten Zitat
Benutzerbild von stahli
stahli

Registriert seit: 26. Nov 2003
Ort: Halle/Saale
4.199 Beiträge
 
Delphi 10.4 Sydney
 
#27

AW: Spielwiese - SocketTest

  Alt 22. Dez 2016, 13:21
Die letzte Änderung reicht doch nicht aus.

Also mein Konzept ist gut einsetzbar für kurze Stringlisten-Messages.
Wem das ausreicht, der kann das gut einsetzen.

Längere Stringlisten werden aber von den Sockets in 2 oder mehr Teilen übertragen und auf Grund der Zeilenumbrüche nicht immer korrekt zusammengesetzt.
Wer also auch längere Stringlisten übertragen will braucht eine alternative Lösung.

Ich bin daher dabei, die Stringlisten in Streams zu speichern und die Streams zu übertragen. Das hat auch den Vorteil, dass man die Streams bei Bedarf leichter komprimieren bzw. verschlüsseln kann.

Orientiert habe ich mich an folgendem Beitrag: http://www.delphipraxis.net/687499-post27.html

Jetzt habe ich das Problem, dass Read(...) manchmal feuert, Socket.ReceiveLength aber 0 ist und entsprechend keine Daten geladen werden können. Read(...) feuert aber später nicht noch einmal, so dass Daten verloren gehen.

Wenn ein Stream vollständig angekommen ist wird er wieder in eine StringList geschrieben und in eine Queue gehängt zur weiteren Verarbeitung durch Threads.
Kann sein, dass ich hier etwas verbockt habe, aber in der Nacht konnte ich da nichts mehr finden.
Eigentlich sollten die Verarbeitungsthreads ja die Eriegnisbehandlung im Mainthread nicht stören. Solange ich Strings übertragen hatte war das auch nicht der Fall.
(Blöderweise spinnt mein USB-Stick schon wieder, so dass ich hier gerade keinen Code dabei habe und ihn gerade nicht posten kann.)

Hat jemand so pauschal eine Idee, wie das Problem Socket.ReceiveLength=0 zu lösen ist?
Stahli
http://www.StahliSoft.de
---
"Jetzt muss ich seh´n, dass ich kein Denkfehler mach...!?" Dittsche (2004)
  Mit Zitat antworten Zitat
Benutzerbild von Zacherl
Zacherl

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

AW: Spielwiese - SocketTest

  Alt 22. Dez 2016, 17:04
Hat jemand so pauschal eine Idee, wie das Problem Socket.ReceiveLength=0 zu lösen ist?
Der von dir verlinkte Code hat mehrere Schwächen:
  • Theoretisch ist es möglich, dass die Länge schon nicht in einem Stück ankommt
  • Wenn du zwei Streams hintereinander schickst, kann das letzte Paket von Stream 1 bereits die Länge und Daten von Stream 2 enthalten
Einfachste Vorgehensweise:
  1. Erstmal ALLE eingehenden Daten an einen Buffer (präziser: einer FIFO Datenstruktur) hinten anhängen
  2. Dann in einer Schleife alle vollständigen Pakete abarbeiten (Pseudo-Code):
    Delphi-Quellcode:
    while ((PacketSize = 0) and (BufferSize >= SizeOf(PacketSize)) or
      ((PacketSize > 0) and (BufferSize >= PacketSize)) do
    begin
      if (PacketSize = 0) then
      begin
        // Hier PacketSize vom Anfang des Buffers auslesen, dann alle Daten
        // im Buffer um SizeOf(PacketSize) nach vorne verschieben und schließlich
        // BufferSize um SizeOf(PacketSize) dekrementieren
      end;
      // Vollständiges Paket vorhanden
      if (BufferSize >= PacketSize) then
      begin
        // Hier Paket-Daten vom Anfang des Buffers auslesen, dann alle Daten
        // im Buffer um PacketSize nach vorne verschieben und schließlich
        // BufferSize um PacketSize dekrementieren
      end;
    end;
Durch diese Vorgehensweise solltest du alle Möglichen Fälle abdecken. Aus Performancegründen würde ich noch versuchen die Verschiebe- und besonders die Realloc Operationen des Buffers zu minimieren. Beispielsweise könntest du noch ein zustäzliches Feld BufferPos pflegen, mit dem du dir diese Aktionen sparst (müssten dann nur einmalig nach der Schleife ausgeführt werden, aber nicht bei jedem Durchlauf). Falls deine Pakete nicht in anderen Threads abgearbeitet werden, kannst du dir auch das Auslesen der Paketdaten sparen und deinem Paket-Handler einfach einen Zeiger auf @Buffer[BufferPos] zurückgeben, etc. Auch würde ich den Buffer nie komplett auf Null shrinken, sondern immer mindestens X-Byte alloziiert lassen, damit du bei kleineren Paketen im Optimalfall sogar komplett auf Realloc verzichten kannst.

Edit:
Achso und wie sieht deine Sende-Routine aus? Bei non-blocking Sockets kann MSDN-Library durchsuchenSend auch erfolgreich sein, aber trotzdem keine bzw. nicht alle Daten senden (wenn der Empfangsbuffer zu voll ist). Deshalb musst du hier in einer Schleife selbst dafür sorgen, dass alles verschickt wird. Bei den blocking Sockets kann das nicht passieren, da MSDN-Library durchsuchenSend dann einfach blockiert, bis im Empfangsbuffer wieder Platz ist und alle Daten gesendet werden konnten.
Projekte:
- GitHub (Profil, zyantific)
- zYan Disassembler Engine ( Zydis Online, Zydis GitHub)

Geändert von Zacherl (22. Dez 2016 um 17:08 Uhr)
  Mit Zitat antworten Zitat
Namenloser

Registriert seit: 7. Jun 2006
Ort: Karlsruhe
3.724 Beiträge
 
FreePascal / Lazarus
 
#29

AW: Spielwiese - SocketTest

  Alt 23. Dez 2016, 12:47
Sind es blocking oder non-blocking Sockets?

Theoretisch ist es möglich, dass die Länge schon nicht in einem Stück ankommt
Das sollte eigentlich bei blocking Sockets nicht passieren. Wenn ich 4 Bytes lese, denn bekomme ich auch 4 Bytes, und nicht 3, 2 oder 1. Allerdings ist das im verlinkten Code tatsächlich etwas komisch umgesetzt, weil da ja erst noch abgefragt wird, wieviele Bytes lesbar sind und dann nur so viel auch gelesen wird. Das läuft dem Konzept von blockierenden Sockets eigentlich zuwider.

Blockierender Ansatz wäre eigentlich so:

Server (wie im Original):
Delphi-Quellcode:
var
  lLen: Integer;
  lStream: TStream;
begin
  lStream := TFileStream.Create('c:\testbild.bmp', fmOpenRead);

  lLen := lStream.Size; // grösse Stream ermitteln
  Socket.SendBuf(lLen, SizeOf(lLen)); // Grösse senden

  Socket.SendStream(lStream); // dann das Bild hinten dran...
end;
Client:
Delphi-Quellcode:
var
  lLen: Integer;
  lStream: TStream;
  Buffer: Pointer;
begin
  Socket.RecvBuf(lLen, SizeOf(lLen));

  lStream := TFileStream.Create('c:\testbild.bmp', fmOpenWrite);

  // Im einfachsten Fall. Besser wäre es natürlich, einen Buffer fester Größe
  // zu erzeugen und mit einer Schleife zu arbeiten, damit der RAM nicht gesprengt wird.
  GetMem(Buffer, lLen);
  Socket.ReceiveBuf(Buffer^, lLen);
  lStream.Write(Buffer^, lLen);

  FreeMem(Buffer);
  lStream.free;
end;
Der nicht-blockierende Ansatz wäre so wie Zacherl beschrieben hat.

Wieso genau der Fehler bei dir kommt, weiß ich nicht, aber ich hatte ja schon mal geschrieben, dass blockierend und eventgetrieben sich nicht gut verträgt.
  Mit Zitat antworten Zitat
Benutzerbild von stahli
stahli

Registriert seit: 26. Nov 2003
Ort: Halle/Saale
4.199 Beiträge
 
Delphi 10.4 Sydney
 
#30

AW: Spielwiese - SocketTest

  Alt 23. Dez 2016, 13:18
Meine Anwendung ist nicht blockierend.

Ich habe gestern erst mal meinen bisherigen Ansatz in einer repeat-Schleife gepackt und den Ablauf etwas geloggt.

Im Eingang in die Methode hat ReceiveLength z.B. 8 ausgegeben.
Drei Zeilen weiter dann ggf. 100.

Also verändert sich der Wert auch schon mal dynamisch.

Until habe ich dann bei "ReceiveLength = 0" veranlasst.

Im Ergebnis holt sich die Behandlung zunächst einen Integerwert für die Streamgröße sobald 4 Bytes verfügbar sind und den Stream selbst sobald dieser komplett verfügbar ist.

So sollte das m.E. ja eigentlich funktionieren - hat es auch.

Aber sobald ich die Logs raus genommen habe lief es wieder nicht mehr.
Da muss wohl irgendwo anders noch ein (Thread-)Problem sein.

In der Nacht um 3 habe ich dann erst mal aufgegeben. :-/

Hier mal der Quelltext (mit dem Logging), der funktionierte.

PS: Ich schaue mir auch Zacherls Hinweis nochmal an, aber ich dachte, meine aktuelle Lösung würde quasi das Gleiche machen...

Delphi-Quellcode:
...Send
                  MS := TMemoryStream.Create;
                  lSL.SaveToStream(MS);
                  MS.Seek(0, soBeginning);
                  MSSize := MS.Size;
                  fServerSocket.Socket.Connections[I].SendBuf(MSSize, SizeOf(MSSize));
                  if fServerSocket.Socket.Connections[I].SendStream(MS) then
                    LogSL('==>', '', lSL)
                  else
                    LogSL('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx==>', '', lSL);


//==============================================

...Read
var
  iLen: Integer;
  Bfr: Pointer;
  SL: TStringList;
  lP: Int64;
  rl: Integer;
  iL: Integer;
begin
  if fff then
    Codesite.Send('ffffffffffffffffffffffffffff');

  fff := True;

  repeat
    rl := Socket.ReceiveLength;
    Codesite.Send('Read: ' + IntToStr(rl));

    Codesite.Send('a: ' + IntToStr(Socket.ReceiveLength));
    if not Assigned(fMS) then
    begin
      Codesite.Send('not Assigned(fMS)');
      if Socket.ReceiveLength >= SizeOf(fMSSize) then
      begin
        Codesite.Send('Socket.ReceiveLength >= SizeOf(fMSSize)');
        iL := Socket.ReceiveBuf(fMSSize, SizeOf(fMSSize));
        Codesite.Send('reading=' + IntToStr(iL));
        fMS := TMemoryStream.Create;
      end
      else
        Codesite.Send('NOT Socket.ReceiveLength >= SizeOf(fMSSize)');
    end
    else
      Codesite.Send('NOT not Assigned(fMS)');

    Codesite.Send('b: ' + IntToStr(Socket.ReceiveLength));
    if Assigned(fMS) then
    begin
      Codesite.Send('Assigned(fMS)');
      iLen := Socket.ReceiveLength;
      Codesite.Send('*** ' + IntToStr(iLen) + '/' + IntToStr(fMSSize));
      iLen := Min(fMSSize, iLen);
      GetMem(Bfr, iLen);
      try
        iL := Socket.ReceiveBuf(Bfr^, iLen);
        Codesite.Send('reading=' + IntToStr(iL));
        Dec(fMSSize, fMS.Write(Bfr^, iLen));
        if Socket.ReceiveLength > 0 then
        begin
          Codesite.Send('!!!REST');
        end;
        // lP := fMS.Position;
        // fMS.Position := 0;
        // SL := TStringList.Create;
        // SL.LoadFromStream(fMS);
        // SL.Insert(0, IntToStr(SL.Count));
        // LogSL('<--', '', SL);
        // FreeAndNil(SL);
        // fMS.Position := lP;
      finally
        FreeMem(Bfr);
      end;

      Codesite.Send('c: ' + IntToStr(Socket.ReceiveLength));
      if fMSSize = 0 then
      begin
        Codesite.Send('fMSSize = 0');
        fMS.Position := 0;
        SL := TStringList.Create;
        SL.LoadFromStream(fMS);
        SL.Insert(0, IntToStr(SL.Count));
        FreeAndNil(fMS);
        fMessageHandlerServer.RegisterInSL(SL, Socket);
        FreeAndNil(SL);
      end
      else
        Codesite.Send('NOT fMSSize = 0');
    end
    else
      Codesite.Send('NOT Assigned(fMS)');
    Codesite.Send('----: ' + IntToStr(Socket.ReceiveLength));
  until (Socket.ReceiveLength = 0);
  Codesite.Send('----------------------: ' + IntToStr(Socket.ReceiveLength));
  fff := false;
end;
Angehängte Dateien
Dateityp: txt log.txt (81,4 KB, 5x aufgerufen)
Stahli
http://www.StahliSoft.de
---
"Jetzt muss ich seh´n, dass ich kein Denkfehler mach...!?" Dittsche (2004)
  Mit Zitat antworten Zitat
Themen-Optionen Thema durchsuchen
Thema durchsuchen:

Erweiterte Suche
Ansicht

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 +2. Es ist jetzt 18:55 Uhr.
Powered by vBulletin® Copyright ©2000 - 2021, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2021 by Daniel R. Wolf