Delphi-PRAXiS
Seite 3 von 4     123 4      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Netzwerke (https://www.delphipraxis.net/14-netzwerke/)
-   -   Delphi Spielwiese - SocketTest (https://www.delphipraxis.net/190482-spielwiese-sockettest.html)

stahli 30. Okt 2016 18:38

AW: Spielwiese - SocketTest
 
Liste der Anhänge anzeigen (Anzahl: 2)
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

stahli 28. Nov 2016 14:46

AW: Spielwiese - SocketTest
 
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?

Aviator 28. Nov 2016 16:46

AW: Spielwiese - SocketTest
 
Zitat:

Zitat von stahli (Beitrag 1354848)
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:

Zitat:

Zitat von stahli (Beitrag 1354848)
Ich habe hier keine Adminrechte und kann daher nichts installieren.

Oh zu spät gelesen. Sorry. :oops:
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.

stahli 29. Nov 2016 08:43

AW: Spielwiese - SocketTest
 
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...

Zacherl 29. Nov 2016 18:43

AW: Spielwiese - SocketTest
 
Bei den non-blocking Sockets musst du beim Senden aufpassen:
Zitat:

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:

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.

stahli 8. Dez 2016 08:02

AW: Spielwiese - SocketTest
 
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 22. Dez 2016 12:21

AW: Spielwiese - SocketTest
 
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?

Zacherl 22. Dez 2016 16:04

AW: Spielwiese - SocketTest
 
Zitat:

Zitat von stahli (Beitrag 1356973)
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
Delphi-Quellcode:
Realloc
Operationen des Buffers zu minimieren. Beispielsweise könntest du noch ein zustäzliches Feld
Delphi-Quellcode:
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
Delphi-Quellcode:
@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
Delphi-Quellcode:
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.

Namenloser 23. Dez 2016 11:47

AW: Spielwiese - SocketTest
 
Sind es blocking oder non-blocking Sockets?

Zitat:

Zitat von Zacherl (Beitrag 1357031)
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.

stahli 23. Dez 2016 12:18

AW: Spielwiese - SocketTest
 
Liste der Anhänge anzeigen (Anzahl: 1)
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;


Alle Zeitangaben in WEZ +1. Es ist jetzt 00:58 Uhr.
Seite 3 von 4     123 4      

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