Entwickler-Ecke

Internet / Netzwerk - TCP-Verbindung zu mehreren Endgeräten


LokutusvB - Fr 23.02.18 12:58
Titel: TCP-Verbindung zu mehreren Endgeräten
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:


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:


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


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.


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.


Delete - Fr 23.02.18 13:42

- Nachträglich durch die Entwickler-Ecke gelöscht -


LokutusvB - Fr 02.03.18 12: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 - Sa 03.03.18 16: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.


LokutusvB - Di 06.03.18 11: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!