Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Netzwerke (https://www.delphipraxis.net/14-netzwerke/)
-   -   Delphi TCP Client/Server: stabile Verbindung??? (https://www.delphipraxis.net/92277-tcp-client-server-stabile-verbindung.html)

romber 17. Mai 2007 11:50


TCP Client/Server: stabile Verbindung???
 
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo!

Das Programm führt verschiedene Anylisen durch und schickt die Ergebnisse sofort an alle verbundene Clients. Es ist sehr wichtig, dass die Verbindung sofort wieder aufgebaut wird, falls diese aus irgendeinem Grund unterbrochen wurde. Die Code, die ich mir dafür gebastelt habe, funktioniert, es ist aber irgendwo Hacken drin. Irgendwann springt die CPU-Auslastung auf 100%, die Anzeige der verbundenen Clienten spinnt und als Folge wird das Client-Programm stumm terminiert. Zack - einfach weg, als on man das Programm geschlossen hat.

Server:

Delphi-Quellcode:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ExtCtrls, IdBaseComponent, IdComponent, IdTCPServer, StdCtrls;

type
  TForm1 = class(TForm)
    IdTCPServer1: TIdTCPServer;
    Timer1: TTimer;
    Label1: TLabel;
    procedure IdTCPServer1Connect(AThread: TIdPeerThread);
    procedure FormCreate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure IdTCPServer1Disconnect(AThread: TIdPeerThread);
    procedure IdTCPServer1Execute(AThread: TIdPeerThread);
    procedure Timer1Timer(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

type
  TClientClass = class
  private
    PeerThread: TIdPeerThread;
end;

var
  Form1: TForm1;
  fClientList: TList;      //Liste für verbundene Client
  ListData: TStringList;   //In dieser Liste landen die Daten, die dann an die Clienten verschickt werden

implementation

{$R *.dfm}

procedure TForm1.IdTCPServer1Connect(AThread: TIdPeerThread);
begin
  AThread.Data := TClientClass.Create();
  fClientList.Add(AThread.Data);
  TClientClass(AThread.Data).PeerThread := AThread;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  fClientList := TList.Create;
  ListData := TStringList.Create;
end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  fClientList.Free;
  ListData.Free;
end;

procedure TForm1.IdTCPServer1Disconnect(AThread: TIdPeerThread);
begin
  fClientList.Delete(fClientList.IndexOf(AThread.Data));
end;

procedure TForm1.IdTCPServer1Execute(AThread: TIdPeerThread);
var
  i: integer;
  LastIndex: integer;
  NoData: integer;
  lCount: integer;
begin
  LastIndex := ListData.Count;
  while AThread.Connection.Connected do
  begin
     Sleep(1);
     if ListData.Count > LastIndex then
     begin
       NoData := 0;
       lCount := ListData.Count;
       for i := lCount - 1 - LastIndex downto 0 do
       AThread.Connection.WriteLn(ListData.Strings[i] + '|~|');
       LastIndex := lCount;
     end
     else                                      // Wenn die Verbidunung zu einem Client unerwartet
     begin                                     // abgebrochen wird, merkt das der Server erst, wenn
       NoData := NoData + 1;                   // er versucht, irgendwas zu schicken. Dafür ist dieses
       if NoData >= 100 then                   // Teil da.
       begin                                
         AThread.Connection.WriteLn();
         NoData := 0;
       end;
     end;
  end;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  Label1.Caption := IntToStr(fClientList.Count) + ' aktive Verbindungen';
end;

end.
Client:

Delphi-Quellcode:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient,
  StdCtrls;

type
  TForm1 = class(TForm)
    cbConnect: TCheckBox;
    procedure cbConnectClick(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

type
  TCheckConnection = class(TThread)
  protected
    procedure Execute; override;
end;

type
  TClientTCP = class(TThread)
  constructor Create (CreateSuspended: Boolean);
  destructor Destroy; override;
 private
   TempData: string;
   CheckConnection: TCheckConnection;
 protected
   procedure Execute; override;
   procedure TCPClientStatus(ASender: TObject; const AStatus: TIdStatus; const AStatusText: String);
 public
   TCPClient: TIdTCPClient;
end;

var
  Form1: TForm1;
  TCPConnection: TClientTCP;
  CheckConnectionCounter: integer;

implementation

{$R *.dfm}

procedure TCheckConnection.Execute;
begin
  FreeOnTerminate := true;
  while not Terminated do
  begin
     CheckConnectionCounter := CheckConnectionCounter + 1;
     if CheckConnectionCounter = 5 then
     begin
        TCPConnection.TCPClient.Disconnect;
        Terminate;
     end;
     Sleep(1000);
  end;
end;

constructor TClientTCP.Create(CreateSuspended: Boolean);
begin
  inherited Create(CreateSuspended);
  FreeOnTerminate := true;
end;

destructor TClientTCP.Destroy;
begin
  Sleep(1);
  TCPConnection := nil;
  if Form1.cbConnect.Checked then
  begin
    if not Assigned(TCPConnection) then
    TCPConnection := TClientTCP.Create(false);
  end;
  if Assigned(CheckConnection) then
  CheckConnection.Terminate;
end;

procedure TClientTCP.Execute;
var
  x, Data: string;
begin
  CheckConnectionCounter := 0;
  TCPClient := TIdTCPClient.Create(nil);
  TCPClient.OnStatus := TCPClientStatus;
  TCPClient.Host := 'localhost';
  TCPClient.Port := 55595;
  try
    TCPClient.Connect;
    CheckConnection := TCheckConnection.Create(false);
    while not Terminated and TCPClient.Connected do
    begin
       CheckConnectionCounter := 0;
       x := TCPClient.ReadLn;
       Data := TempData + x;
       TempData := '';
       while pos('|~|', Data) > 0 do
       begin
         //TAnalyseData.Create(false, Copy(Data, 1, Pos('|~|', Data) - 1));
         Delete(Data, 1, Pos('|~|', Data) + 3);
       end;
       if Length(Data) > 0 then TempData := Data;
    end;
  except
  end;
  if TCPClient.Connected then
  TCPClient.Disconnect;
  TCPClient.Free;
end;

procedure TClientTCP.TCPClientStatus(ASender: TObject; const AStatus: TIdStatus; const AStatusText: String);
begin
  if AStatus = hsConnected then
  begin
    Form1.cbConnect.Caption := 'verbunden';
    exit;
  end;

  if (AStatus = hsDisconnected) or (AStatus = hsDisconnecting) then
  begin
    if Form1.cbConnect.Checked then
    Form1.cbConnect.Caption := 'Verbindung wird hergestellt...'
    else
    Form1.cbConnect.Caption := 'nicht verbunden';
    exit;
  end;
end;

procedure TForm1.cbConnectClick(Sender: TObject);
begin
  if cbConnect.Checked = true then
  begin
     cbConnect.Caption := 'Verbindung wird hergestellt...';
     if not Assigned(TCPConnection) then
     TCPConnection := TClientTCP.Create(false);
  end
  else
  begin
    if Assigned(TCPConnection) then
    begin
       TCPConnection.Terminate;
       cbConnect.Caption := 'nicht verbunden';
    end;
  end;
end;

end.
Sicherlich mache ich etwas falsch. Wer kann mir helfen? :roll:

romber 17. Mai 2007 19:27

Re: TCP Client/Server: stabile Verbindung???
 
Kann mir keiner helfen? :(

Udontknow 18. Mai 2007 11:19

Re: TCP Client/Server: stabile Verbindung???
 
Hallo!

Ich habe mal grob drübergeblickt, und die ServerExecute-Routine sieht doch sehr merkwürdig aus: Du sendest einfach in einer Schleife wild drauf los. Was soll das? Wäre es nicht besser, die Daten nur einmal bzw. bei Anfrage vom Client zu senden?

Wieso muss man überhaupt sofort erfahren, daß der Client unerwartet verschwunden ist?

Cu,
Udontknow

romber 18. Mai 2007 17:06

Re: TCP Client/Server: stabile Verbindung???
 
Wie schwer das auch zu vostellen ist, es gibt Martktbereiche, wo jede Sekunde zählt. Für so ein Marktbereich ist mein Programm gedacht.
Was mache ich falsch, dass oben beschriebene Fehler passieren?

Luckie 18. Mai 2007 17:16

Re: TCP Client/Server: stabile Verbindung???
 
1. Windows ist kein EChtzeit Betriebssystem und somit nicht für zeitkritische Anwnedungen geeignet und
2. kann man sich nicht auf die Geschwindigkeit einer Netzwerkverbindung verlassen.

Du solltest also dein Konzept noch mal überdenken.

romber 18. Mai 2007 17:19

Re: TCP Client/Server: stabile Verbindung???
 
Auch wenn ich den Sleep(1) auf mehrere Sekunden erhöhe, ändert das nichts. Die Fehler tauchen immer wieder auf.

Udontknow 18. Mai 2007 17:40

Re: TCP Client/Server: stabile Verbindung???
 
Du solltest das trotzdem umbauen. Ein Server, der dauernd pollt, ist nicht so toll.

Vorschlag: Verwende doch ein ReadInteger, den der Server standardmäßig aufruft. Sowohl bei Verbindungsabbrüchen als auch bei normalen Beendigungen des Clients wirst du eine Indy-Exception erhalten. Hier der Code:

Delphi-Quellcode:
procedure TForm67.TCPServerExecute(AContext: TIdContext);
var Stream:TMemoryStream;
var Command:Integer;
begin
  try
    Command:=AContext.Connection.IOHandler.ReadInteger();
  except
    HandleClientLost; //<- Routine, die bei IdExceptions dann getriggert wird
    raise;
  end;
  //Hier Code für die Interpretierung des Commands hinterlegen
  if Command=0 then
    ...
end;
Gleichzeitig kannst du das ReadInteger nutzen, um eben bestimmte Anforderungen entgegen zu nehmen (die TCP-Verbindung wird ja normalerweise für Kommunikation genutzt).

Bis dann,

Andreas

romber 18. Mai 2007 18:55

Re: TCP Client/Server: stabile Verbindung???
 
@Udontknow

Danke! Sieht gut aus, werde ich gleich probieren.
Aber kann jemand in meinem Server-Code ein dummes Fehler erkennen (außer dem, was Udontknow schon bemerkt hat), das zu einem plötzlichen Anstieg der CPU-Auslastung auf 100% führen könnte? Oder soll ich die Ursache in dem übrigen Code suchen?

Und wo liegt das Problem an dem Client? Das Client-Programm verschwindet von selbst. Wo liegt dort das Fehler?

romber 19. Mai 2007 15:03

Re: TCP Client/Server: stabile Verbindung???
 
Ich habe die Code modernisiert, so wie Udontknow geposetet hat. Dir Probleme sind aber immer noch da.

DataCool 21. Mai 2007 23:15

Re: TCP Client/Server: stabile Verbindung???
 
Hi,

bin leider nicht dazu gekommen, in die Tiefe des Code zu schauen.
Aber ich kann Dir mit Sicherheit sagen, das Du mit Indy9 oder auch mit Indy10 das ganze ohne Probleme
realisieren kannst.

Ich mache ähnliches mit 50-100 Clients an einem Server ohne Probleme,
wobei ich die Disconnects der Clients nur jede Minute überprüfe.
Aber bei normalen Disconnects melden sich die Clients auch regulär ab.

Was ich Dir damit sagen will, ist das gleihe was meine Vorgänger auch schon gesagt haben :
Bei einer Server/Client Anwendung muss die Struktur und das Protokoll genau bedacht/entworfen seien.

Wie schnell musst Du wirklich wissen, ob ein Client nicht mehr verbunden ist ?

Wäre es nicht ausreichend, wenn der Client merkt wenn die Verbindung zum Server unterbrochen ist und sich umgehend neu verbindet ?
In diesem Fall müßtest Du nur die Daten, die "ausgeliefert" werden sollen, zwischen speichern, und dazu speichern welcher Client welche Daten bekommen hat.

Erzähl mal näheres über die Art der Daten die Du austauschen willst, die Rahmenbediengungen unter denen das ganze ablaufen soll und den "Sinn und Zweck" des ganzen ;-)

Gruß DataCool

romber 5. Jun 2007 22:36

Re: TCP Client/Server: stabile Verbindung???
 
Ich habe immer noch keine Lösung. :(
Kann jemand mein Code genau anscheuen und sagen, was ich da falsch mache?
Wäre super!

DataCool 5. Jun 2007 22:41

Re: TCP Client/Server: stabile Verbindung???
 
Hi,

beantworte mir erstmal meine Fragen und sag was zu meinen Denkansätzen.
Du musst etwas umdenken(vom Konzept her).

Aber sag uns doch einfach was Du genau machen willst, dann können wir Dir eher helfen,

Greetz DataCool

alzaimar 5. Jun 2007 22:48

Re: TCP Client/Server: stabile Verbindung???
 
Einen Thread nur mit 'Terminate' zu beenden und dann zu erwarten, das er auch wirklich beendet wurde ist ... mutig ...

'Terminate' sagt dem Thread nur, das er sich beenden *soll*. Du musst dann noch warten, bis er beendet *ist*. Das machst Du mit 'WaitFor', also so etwa:

Delphi-Quellcode:
With MyThread do begin
  Terminate;
  WaitFor;
  // Free
End;
Im Disconnect des Servers prüfst Du nicht, ob 'IndexOf' einen gültigen Wert zurückliefert. Wozu dient denn fClientList? Was macht der Server sonst noch? Liegt es wirklich an diesem Code-Teil?

romber 20. Jun 2007 14:57

Re: TCP Client/Server: stabile Verbindung???
 
Tut mir Leid, dass ich so spät antworte. Musste geschäftlich abreisen. Das Problem besteht aber nach wie vor.

Zitat:

Zitat von DataCool
...Wie schnell musst Du wirklich wissen, ob ein Client nicht mehr verbunden ist ?

Eigentlich ist es nicht so extrem wichtig, wie schnell der Server merkt, dass der Client nicht mehr da ist. Genauer gesagt ist das überhaupt nicht wichtig.

Zitat:

Zitat von DataCool
...Wäre es nicht ausreichend, wenn der Client merkt wenn die Verbindung zum Server unterbrochen ist und sich umgehend neu verbindet ?

Natürlich wäre das ausreichend!!! Das wäre sogar wunderbar!!! :thumb: Genau das habe ich auch mit der oben geposteten Client-Code erreicht. Nur dort ist irgendwo ein [Riesen]Fehler drin. Denn wie schon oben beschrieben, sturzt das Client-Programm nach einiger Zeit ohne jede Meldung ab - einfach weg! Und das liegt genau an dem Code oben, denn wenn ich die Cleint-Funktion weglasse läuft das Programm dauernd ohne Absturze. Deswegen frage ich mich und auch: was ist an dem Code oben falsch? Dort ist definitiv etwas falsch!

Zitat:

Zitat von DataCool
...Erzähl mal näheres über die Art der Daten die Du austauschen willst, die Rahmenbediengungen unter denen das ganze ablaufen soll und den "Sinn und Zweck" des ganzen.

Es sind Strings, manchmal auch ziemlich lange, es ist aber nicht das Problem. Mit dem oben geposteten Code kommen die Daten jedesmal komplett und in richtiger Reihenfolge an. Das funktioniert also. Die Rahmenbeningungen kann mann auch als "klassisch" bezeichnen. Ich habe ein dedicated Windows-Server von "1&1" auf einer 100 Mbit Leitung. Auf dem Server läuft das Server-Teil. Die Klienten sind einfache Benutzer auf Windows XP oder Vista. Zu dem "Sinn und Zweck" der Sache muss ich sagen, dass es sehr-sehr-sehr wichtig ist, dass die Daten möglicht schnell die Clients erreichen, wenn der Server die Daten bekommen hat.

Was ist an der Code oben falsch? Experten! HILFE!!! :roll:


Alle Zeitangaben in WEZ +1. Es ist jetzt 21:57 Uhr.

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 by Thomas Breitkreuz