Delphi-PRAXiS
Seite 2 von 2     12   

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Netzwerke (https://www.delphipraxis.net/14-netzwerke/)
-   -   TCPClient+SSL, Blockierendes Read -> Disconnect -> AV (https://www.delphipraxis.net/179503-tcpclient-ssl-blockierendes-read-disconnect-av.html)

himitsu 11. Mär 2014 15:09

AW: TCPClient+SSL, Blockierendes Read -> Disconnect -> AV
 
Zitat:

Da das Lesen schon in einem eigenen Thread läuft, wird der Hauptthread (sofern keine "Synchronize"-Fehler gemacht werden) nicht blockiert.
Ja, aber wenn man die Zugriffe absichern würde, dann würde die CriticalSection den anderen Thread blockieren.

Und wenn das Lesen nicht mehr blockierend ist, dann könnte man es auch in den anderen Thread verschieben, wo die TCP-Komponente erstell/verwaltet wird, und könnte sich so dann die Synchronisierung sparen.


Man kann auch in beide Richtungen einen Server-Client aufmachen, dann brauchst du nicht zu pollen, da der "Server" im Client dann via Empfangsereignis reagiert.

Oder man verwendet etwas "ausgewachsenere" Transportkomponenten, welche z.B. eine Callbackfunktion zum Client bieten, so daß der Server direkt senden kann und im Client dann ein Empfangsereignis auslöst, ohne daß der Client ständig abfragen muß.

idontknow 11. Mär 2014 15:15

AW: TCPClient+SSL, Blockierendes Read -> Disconnect -> AV
 
Zitat:

Theoretisch kann man mit einer Instanz der TidTCPClient Komponente zwar gleichzeitig senden und empfangen (lt. Indy Entwickler Remy Lebau kann man über einen Thread IOHandler.Read und aus einem anderen IOHandler.Write aufrufen).
Hm. Genau das mache ich ja. Wenn das laut Remy geht, dann sollte doch mein Testprogramm kein Problem darstellen. Solange ich das (reale) Programm nicht beende, kann ich stundenlang mit dem Server Daten austauschen, das Problem entsteht wirklich erst dann, wenn ich disconnecte, und eben auch nur, wenn SSL im Spiel ist. Ich meine mich zu erinnern, daß ich ohne SSL den Fehler auch reproduzieren kann, wenn ich nach dem Disconnect und dem erfolgreichen Abbruch des Read ein weiteres Mal TCPClient.Read.. aufrufe.

Zitat:

Für den Anfang würde ich zwei Threads verwenden, die jeweils ihre eigene Instanz der Komponente enthalten. Dann ist es nicht mehr möglich, dass sie sich gegenseitig stören.
Das Grundproblem bliebe aber: Das das blockierende Read abschmiert, wenn die zugrundeliegende Verbindung (zwangsläufig aus einem anderen Thread heraus) disconnected wird. Was für mich bedeuten würde, das ich entweder das Programm nicht beenden kann, solange der Thread auf Daten vom Server wartet oder das ich pollen muss, was ich nicht sehr elegant finde und was die Prozessorlast in die Höhe treibt... Hmm. In der Realität wird es so sein, daß ich mehrere (1 ~ vielleicht 20) Services auf einer Maschine habe, die auf Daten warten...

himitsu 11. Mär 2014 15:33

AW: TCPClient+SSL, Blockierendes Read -> Disconnect -> AV
 
Das SSL schaltet sich nun in den transfer mit ein und ich vermute, daß der die Verschlüsselung nicht je Datenrichtung "einzeln" behandelt, womit du dann diese Komponente in beiden Threads verwendest, was bei einer nicht threadsicheren Komponente wieder Probleme bereitet.

mjustin 11. Mär 2014 15:49

AW: TCPClient+SSL, Blockierendes Read -> Disconnect -> AV
 
Zitat:

Zitat von himitsu (Beitrag 1251556)
Zitat:

Da das Lesen schon in einem eigenen Thread läuft, wird der Hauptthread (sofern keine "Synchronize"-Fehler gemacht werden) nicht blockiert.
Ja, aber wenn man die Zugriffe absichern würde, dann würde die CriticalSection den anderen Thread blockieren.

Daher mein Vorschlag je Richtung eine threadlokale Komponente zu verwenden.

Zitat:

Zitat von himitsu (Beitrag 1251556)
Man kann auch in beide Richtungen einen Server-Client aufmachen, dann brauchst du nicht zu pollen, da der "Server" im Client dann via Empfangsereignis reagiert.

Indy hat im Gegensatz zu anderen Komponenten kein Epfangsereignis. Wenn der Server etwas an den Indy Client sendet, liest man diese Daten aus dem Socket (über IOHandler.Read). Dies kann je nach verwendetem Protokoll in einer Schleife geschehen. Eine andere Möglichkeit gibt es mit TCP generell nicht - man muss immer eine Leseoperation auf den Socket machen, um Daten von der Gegenseite zu erhalten. (Das ist kein "Pollen", da man dabei keine Pakete an den Server sendet.)

idontknow 11. Mär 2014 15:53

AW: TCPClient+SSL, Blockierendes Read -> Disconnect -> AV
 
@himitsu:

Ein Client und ein Server pro Richtung ginge natürlich, im Server.onExecute dann wahrscheinlich so:
Delphi-Quellcode:
if AContext.Connection.IOHandler.Readable then begin
    AllesWasDaIst := AContext.Connection.IOHandler.AllData(TEncoding.Ansi);
    Parsen...
end;
um auch hier das blockierende Lesen zu Vermeiden, sonst habe ich wahrscheinlich dasselbe Problem, wenn der Server auf Daten wartet und ich den beenden möchte. Der Call von onExecute heißt ja nur, das Daten eingetroffen sind, aber nicht, das die gewünschte Menge an Daten ankam...

Wenn es nicht schöner geht, wäre das natürlich eine Lösung...

samso 11. Mär 2014 16:00

AW: TCPClient+SSL, Blockierendes Read -> Disconnect -> AV
 
Der Schlüssel zum Erfolg ist, dass man sicherstellt, dass mit dem ReadThread tatsächlich nur gelesen wird und mit dem WriteThread nur geschrieben wird. Das wiederum ist mit Indy nicht so trivial, weil die diversen Methoden verdecken, was tatsächlich geschieht. Während des Verbindungsaufbau/Abbau darf nur einer der Threads auf den Client zugreifen. Das ist hier nicht der Fall, denn Du unterbrichst die Verbindung, während der ReadThread versucht zu lesen.

Mein Vorschlag: Du baust beim ReadThread einen Timeout von z.B. 20ms [IOHandler.Readable(20)] ein. Du wartest also nicht unendlich lange auf Daten, sondern halt nur kurze Zeit. Nach Ablaufen des Timeouts, prüfst Du ob der Thread terminiert wurde. Wenn er nicht terminiert wurde, darfst Du nochmal versuchen zu lesen usw.
Das regelmäßige Aufwachen des Threads wegen des Timeouts führt zu keiner nennenswerten CPU-Belastung. Für den Verbindungsabbau terminierst Du den ReadThread. Dann wartest Du bis er tatsächlich beendet wurde. Erst jetzt löst Du Disconnect aus.

mjustin 12. Mär 2014 08:21

AW: TCPClient+SSL, Blockierendes Read -> Disconnect -> AV
 
Zitat:

Zitat von idontknow (Beitrag 1251569)
Der Call von onExecute heißt ja nur, das Daten eingetroffen sind, aber nicht, das die gewünschte Menge an Daten ankam...

Das ist nicht korrekt. Die OnExecute Methode des TIdTCPServers wird in einer Schleife aufgerufen, unabhängig davon ob Daten angekommen sind oder nicht. Ob Daten angekommen sind, muss man selber ermitteln.
Am Anfang der OnExecute kann man dazu die Funktionen InputBufferIsEmpty und CheckForDataOnSource aufrufen.
Im Beispiel wird, wenn CheckForDataOnSource innerhalb der angegebenen Zeit keine neuen Daten lesen konnte, der OnExecute Durchlauf verlassen:
Delphi-Quellcode:
var
  IO: TIdIOHandler;
begin
  IO := AContext.Connection.IOHandler;

  if IO.InputBufferIsEmpty then
  begin
    IO.CheckForDataOnSource(10);
    if IO.InputBufferIsEmpty then Exit;
  end;

  // Do stuff with IOHandler
  IO. ...

end;

Und anstatt einfach alles einzulesen was im InputBuffer steht, kann man genau die benötigte Anzahl Bytes einlesen oder bis zu einem Terminator, je nachdem wie das Protokoll es vorsieht. Wenn Indy nicht alle Daten einlesen kann weil sie noch nicht komplett angekommen sind, blockiert die Read... Methode (bis zum Timeout, wodurch der Server einen Verbindungsverlust erkennt und die Connection seinerseits schliesst).

idontknow 12. Mär 2014 09:00

AW: TCPClient+SSL, Blockierendes Read -> Disconnect -> AV
 
Vielen Dank an alle, die geholfen haben. Habe das Problem wie vorgeschlagen mit einem Timeout gelöst, funktioniert prima.


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

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