Autor Beitrag
LokutusvB
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 74

WinXP
Delphi 5, Delphi XE
BeitragVerfasst: Fr 23.02.18 11:58 
Hallo,

ich habe dieses Thema bereits im Delphi-PRAXiS-Forum eröffnet. Leider jedoch bekomme ich keine Antwort. Vielleicht kann mir hier jemand helfen.

Ich habe eine variable Anzahl von Servern im Netz, mit denen ich mich verbinden muß. Deren IP-Adressen werden in eine INI-Datei gespeichert (diese kann sich aber jederzeit ändern). Umsetzen möchte ich das Projekt in Delphi XE6 unter Verwendung der Indy-Komponenten.

Für das Verwalten der Verbindungsinformationen und der Indy-Clients habe ich mir ein Rord angelegt:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
tcpDataClients = record
 recIndex: Integer;
 tsC: TidTCPClient;
 tIp: String;
 ...
end;


Daraus erstelle ich in variables Array, lese die IP aus der INI-Datei und erzeuge die Indy-Clients, mit denen ich mich anschließend verbinde.

Die Informationen der Server erhalte ich bei ReadLn() der Indy-Komponenten. Hierfür habe ich mir eine Thread-Klasse erstellt. Um jeder erzeugten Instanz dieser Klasse eine Indy-Komponente zuordnen zu können, übergebe ich im Constructor eine Variable:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
Constructor TT_Lesen.Create;
begin
  Inherited Create(False);
  tcpRecIndex := Variable;
end;


Mit Hilfe einer For-Schleife kann ich nun die Thread-Instanzen erzeugen...

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
procedure TF_Main.Start;
var
  ii: Integer;
begin
  for ii := 0 to Length(tcpCls) - 1 do begin
    tcpCls[ii].recIndex := ii;
    tcpCls[ii].Lesen_Thread := TT_Lesen.Create(tcpCls[ii].recIndex);
    tcpCls[ii].Lesen_Thread.Priority := tpNormal;
    tcpCls[ii].Lesen_Thread.Resume();
  end;
end;


und die Indy-Clients können im Htread bei den Servern die Informationen einholen.

ausblenden Delphi-Quelltext
1:
txt := Trim(F_Main.tcpCls[tcpRecIndex].tsC.IOHandler.ReadLn());					


Das ganze funktioniert bei 2 bis 3 Clients sehr gut. Allerdings habe ich oft gehört, daß Delphi Threads Probleme bereiten sollen, inbesondere wenn man auf Variablen einer Form zugreift. Deswegen habe ich für jeden Indy-Client im Record die recIndex angelegt. Beispiele im Netz findet man leider sehr wenig, und wenn dann arbeiten die Leute mit Pointern.

Deswegen wollte ich wissen, ob meine Lösung "sauber" und ausfallsicher ist oder ob dabei zu Zugriffsfehlern auf den Record-Index kommen kann. Oder aber ob es vielleicht besser wäre, dem Thread direkt das Record-Objekt zu übergeben.
Frühlingsrolle
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2103
Erhaltene Danke: 383

[Win NT] 5.1 x86 6.1 x64
[Delphi] 7 PE, 2006, 10.1 Starter, Lazarus - [C#] VS Exp 2012 - [Android API 15] VS Com 2015, Eclipse, AIDE - [C++] Builder 10.1
BeitragVerfasst: Fr 23.02.18 12:42 
Guten Tag LokutusvB,

du kannst hier ruhig eine Struktur (record) verwenden. Die andere Möglichkeit wäre eine Klasse, in der die Felder (recIndex, tsC, tIp), Eigenschaften wären. Es kommt darauf an, wie man es braucht.
Wäre dein tcpDataClients-record eine published-Eigenschaft, so würde sie im Objektinspektor garnicht angezeigt werden, wäre aber öffentlich durchaus erreichbar.
Wäre tcpDataClients stattdessen eine Klasse, dann würde es als Eigenschaft im Objektinspektor erscheinen.

_________________
„Politicians are put there to give you the idea that you have freedom of choice. You don’t. You have no choice. You have owners. They own you. They own everything." (George Denis Patrick Carlin)
LokutusvB Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 74

WinXP
Delphi 5, Delphi XE
BeitragVerfasst: Fr 02.03.18 11:12 
Moinsen,

die Lösung mit der Verwendung der Records klappt wunderbar, auch im "Dauerstresstest". Auch das Beenden des Programms (Threads terminieren, Verbindungen trennen...) klappt auf Programmebene wunderbar.

Ich habe das Programm als Windows-Dienst in Delphi neu erstellt, auch dieser funktioniert fehlerfrei bis auf das Stoppen des Dienstes. Auf Programmebene wartet das Terminate der Threads nicht, bis das Readln() der Indy-Komponenten ausgeführt wurde. Im Windows-Dienst wird der Dienst erst erfolgreich beendet, wenn jeder Thread, also jeder Indy-Client noch einmal ein ReadLn() durchgeführt hat. Wie kann ich das unterbinden und auch im Windows-Dienst die Threads beenden ohne das Warten auf ReadLn()?
Sinspin
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1124
Erhaltene Danke: 73

Win7
DXE2 Prof, Lazarus
BeitragVerfasst: Sa 03.03.18 15:41 
Ich habe Indy nicht wirklich im Kopf, bzw. wegen der Warterei als unpraktisch abgehakt.
Aber gibt es nicht mitllerweile auch einen Aufruf der nur den aktuellen Puffer liefert und dessen laenge, ohne auf ein Zeilenende zu warten?
Also, generell solltest Du versuchen ohne Readline zu arbeiten und den Aufruf der nur Zeilenweise liefert selber implementieren. Schon ist das Problem weg.

_________________
Solange keine Zeile Code geschrieben ist, läuft ein Programm immer fehlerfrei.
Ich teste nicht, weil ich Angst habe Fehler zu finden.
LokutusvB Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 74

WinXP
Delphi 5, Delphi XE
BeitragVerfasst: Di 06.03.18 10:05 
Das wäre ein Weg.

Ich habe mir mal die API und die Funktionen / Proceduren angeschaut, die der TidTCPClient zur Verfügung stellt. Und tatsächlich gibt es die Eigenschaft "ReadTimeOut". Zusammen mit ReadLn funktioniert auch nun das im Dienst wunderbar. :)

Trotzdem Danke dir für die Hilfe!