Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Netzwerke (https://www.delphipraxis.net/14-netzwerke/)
-   -   IdTCPServer Threadproblem (https://www.delphipraxis.net/98282-idtcpserver-threadproblem.html)

Nakaron 24. Aug 2007 11:38


IdTCPServer Threadproblem
 
Hallo zusammen,

ich bin gerade dabei eine Client->Server->Client Anwendung zuschreiben. Mein großes Problem ist dabei, sobald ein Client sich einmal mit dem Server verbunden hat und dann wieder disconnected und ich den Server beenden möchte, dass diese Serveranwendung einfriert und ich diese nur noch über den Taskmanager beenden kann.

Meine Einzige Vermutung liegt nun darin, dass die ThreadConnections nicht ordentlich geschlossen werden.
Anbei mal die wichtigsten Codesnippets aus meinem Projekt:

Delphi-Quellcode:
type
  PClient  = ^TClient;
  TClient  = record                       // Objekt enthält Daten vom Client
                IP         : String[15];  { IP Adressse des Users }
                UserID     : String[10];  { Benutzer ID in der DB }
                UserName   : String[100]; { Benutzername }
                Connected  : TDateTime;   { Zeitstempel des Verbindungsaufbaus }
                Thread     : Pointer;     { Pointer zum IdContext-Thread }
              end;
.
.
.

var
  frmMain  : TfrmMain;
  Clients  : TThreadList;
.
.
.

procedure TfrmMain.TCPServerConnect(AContext: TIdContext);
var
  NewClient: PClient;
begin
  GetMem(NewClient, SizeOf(TClient));

  NewClient.IP         := AContext.Connection.Socket.Binding.PeerIP;
  NewClient.Connected  := Now;
  NewClient.Thread     := AContext;

  AContext.Data:=TObject(NewClient);

  try
    Clients.LockList.Add(NewClient);
  finally
    Clients.UnlockList;
  end;
end;

procedure TfrmMain.TCPServerDisconnect(AContext: TIdContext);
var
  ActClient: PClient;
  vI : Integer;
begin
  ActClient := PClient(AContext.Data);
  try
    Clients.LockList.Remove(ActClient);
    for vI:=0 to lvConnectedClients.Items.Count-1 do
      if(PClient(lvConnectedClients.Items[vI].Data)^.IP = ActClient.IP) then
      begin
        Dispose(lvConnectedClients.Items[vI].Data);
        lvConnectedClients.Items[vI].Delete;
      end;
    grpConnectedClients.Caption := 'Angemeldete Clients (' + IntToStr(lvConnectedClients.Items.Count) + ' Benutzer)';
  finally
    Clients.UnlockList;
  end;

  FreeMem(ActClient);
  AContext.Data := nil;
end;

procedure TfrmMain.TCPServerExecute(AContext: TIdContext);
var
  Data: String;
  vI: Integer;
  ActClient : PClient;
begin
  Data := AContext.Connection.Socket.ReadLn;
  Data := decodeStr(Data);

  ActClient := PClient(AContext.Data);

  if(Pos('[CMClient]AuthMe', Data) > 0) then
  begin
    addToLog('Authentifizierungsanfrage von: ' + AContext.Binding.PeerIP, 0, [], 8);
    doSELECT('SELECT usr_id, usr_loginName FROM cm_user WHERE usr_loginName = "' + GetTagData('Username', Data) + '" AND usr_loginPassword = "' + GetTagData('Password', Data) + '" LIMIT 1');

    if(Query.RecordCount <> 0) then
    begin
      addToLog('Authentifizierung erfolgreich.', 15, [], 8, clGreen);
      ActClient.UserID := Query.FieldByName('usr_id').AsString;
      ActClient.UserName:= Query.FieldByName('usr_loginName').AsString;
      lvConnectedClients.AddItem(Query.FieldByName('usr_loginName').AsString, Pointer(ActClient));
      lvConnectedClients.Items[lvConnectedClients.Items.Count-1].SubItems.Add('');

      AContext.Connection.Socket.WriteLn(encodeStr('[CMServer]Verify<Reason>Success</Reason>'));
    end else
    begin
      addToLog('Authentifizierung fehlgeschlagen.', 15, [], 8, clRed);
      AContext.Connection.Socket.WriteLn(encodeStr('[CMServer]Verify<Reason>Failed</Reason><ErrorMsg>Falsche Benutzer/Passwort Kombination!</ErrorMsg>'));
    end;
    exit;
  end;
end;
Ich bin bereits seit Tagen auf der Suche nach einer Lösung. Ich hoffe ihr könnt mir weiterhelfen.

Vielen Dank schon einmal im voraus.

Viele Grüße
Nakaron

DataCool 24. Aug 2007 11:53

Re: IdTCPServer Threadproblem
 
Hi Nakaron,

ich konnte leider beim ersten drüber schauen keinen Fehler finden.
Allerdings kann ich Dir sagen das ich bei Delphi 2007 + Indy10 NICHT
diese Probleme habe.

Bei mir sieht die Struktur zwar etwas anders aus, aber vom Prinzip genau der gleiche Ablauf,
nur das ich die Verification des Clients schon im OnConnect Ereignis behandle und nur
ein TClient Object erzeuge wenn die Anmeldung/verifizierung erfolgreich war.

Bei Deiner Lösung wird ja bei jedem Port-Scan ein Client Object erzeugt ...

Ich schaue nachher aber nochmal genauer über Deinen Code,

*Edit*

Denke habe die Ursache gefunden, Dein Client schickt nie ein "Disconnect/QUIT" Commando zum Server !
Somit kann der Server gar nicht feststellen, wann der Client sich disconnecten will.

Mache bitte mal folgendes :
- Dein ganzes Handling für die Client-Verifizierung bitte in das OnConnect !
- Wenn beim OnConnect kein gültiges Login gesendet wird, die Connection sofort wieder schliessen
- Erst nach erfolgreicher Anmeldung TClient erzeugen und in .Data ablegen

- Im OnExecute nut Commandos behandeln die nach dem Login erfolgen,
aber dort muss es auf jeden Fall ein Kommando wie z.B. "QUIT" geben,
wenn der SErver dieses Commando vom Client erhält muss er die Connection schliessen

- Im OnDisconnect musst Du prüfen, ob im .Data Daten vorhanden sind
If Assigned(....Data) then begin
- ansonsten .Data = nil war es nur ein Port-Scan

Greetz DataCool

Nakaron 24. Aug 2007 12:09

Re: IdTCPServer Threadproblem
 
Hi DataCool,

danke erstmal für deine schnelle Antwort.
Die Clientverifizierung kann ich leider nicht in das OnConnect packen, da der Client erst nach dem Connect die vom Clientbenutzer eingegebenen Logindaten an den Server sendet. Beim Server wird dann im OnExecute geprüft ob die Logindaten mit denen, welche sich in einer MySQL Datenbank befinden, übereinstimmen.

Viele Grüße
Nakaron

DataCool 24. Aug 2007 12:13

Re: IdTCPServer Threadproblem
 
Hi,

Zitat:

Die Clientverifizierung kann ich leider nicht in das OnConnect packen, da der Client erst nach dem Connect die vom Clientbenutzer eingegebenen Logindaten an den Server sendet. Beim Server wird dann im OnExecute geprüft ob die Logindaten mit denen, welche sich in einer MySQL Datenbank befinden, übereinstimmen.
Ich sehe bei Deinem Code absolut kein Problem den Verifizierungscode ins OnConnect zu packen.
Du kannst im OnConnect des Servers auch ein ReadLn machen ;-)

Greetz DAtaCool

Nakaron 24. Aug 2007 12:28

Re: IdTCPServer Threadproblem
 
Ok, daran hab ich nicht gedacht ;)

Hab nun alles eingebaut was du mir geraten hast. Eine Frage habe ich dennoch:

Was ist, wenn die Verbindung vom Client einfach abbricht? Dann habe ich doch wieder das selbe Problem ...


Viele Grüße
Nakaron

DataCool 24. Aug 2007 12:34

Re: IdTCPServer Threadproblem
 
Hi,

das löse ich immer so, indem ich dem Client Object eine Eigenschaft
"LastAction" TDateTime gebe.
Diese immer aktualisieren wenn was im OnExecute vom Client kommt.

Dann machst Du Dir einen seperaten Timer oder Thread(eleganter) der in einem bestimmten Intervall
alle Client objekte durchgeht schaut wann die letzte Action war und wenn diese zu lange her ist,
dann versuchst Du in the Connection des Clients ein z.B. "NOOP" zu schreiben.
Wenn der Client mittlerweile disconnected ist, dann merkt das jetzt auch der Server ;-)
Wenn der Client noch verbunden ist, passiert nichts ausser das beim Client ein "NOOP" ankommt,
das sollte natürlich gehandelt werden.

Greetz DAtaCool

Nakaron 24. Aug 2007 12:37

Re: IdTCPServer Threadproblem
 
Vielen Dank DataCool. Du hast mir sehr geholfen.

Das Problem ist nun endlich gefixt. Bin dir echt was Schuldig ;)

Viele Grüße
Nakaron

DataCool 24. Aug 2007 12:41

Re: IdTCPServer Threadproblem
 
Hi,

kein Problem, Indy ist mein Spezial Gebiet ;-)

Werd bestimmt mal die ein oder andere Frage in nem anderem Bereich haben ;-)

Bis dahin frohes Schaffen,

Greetz DataCool


Alle Zeitangaben in WEZ +1. Es ist jetzt 03:40 Uhr.

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