Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Netzwerke (https://www.delphipraxis.net/14-netzwerke/)
-   -   Delphi Indy 10 - Adresse wird bereits verwendet (https://www.delphipraxis.net/179457-indy-10-adresse-wird-bereits-verwendet.html)

stahli 8. Mär 2014 22:06

Indy 10 - Adresse wird bereits verwendet
 
Liste der Anhänge anzeigen (Anzahl: 1)
Ich habe einen Indy-Server und Client.
Derzeit tausche ich nur Now und einen Counter als Strings aus.
Zusätzlich wird Now und ein Counter in jedem Client (lokal) verwaltet und in Strings gespeichert.

Zwei Probleme und eine Frage...

1) Wenn zwei Clients auf dem selben Rechner laufen gibt es nach einiger Zeit immer wieder die Fehlermeldung "Adresse wird bereits verwendet"

- Ist in meinem Code direkt etwas falsch?

2) Wenn der Server auf meinem PC und der Client auf meinem Notebook läuft hängt die Client-GUI aller paar Sekunden für ca. 2 Sekunden.
Das betrifft alle laufenden Clients synchron. Es sollte also m.E. wohl am Netzwerk liegen.
Andersrum gibt es keine Hänger, auch nicht wenn der Server und Client auf dem Laptop laufen.

- Vielleicht liegst an der Firewall? Oder liegen dafür andere Gründe nahe?

3) Wenn ich sagen wir mal 100 mögliche Commands hätte, müssten diese 100 Fälle im Server ja hintereinander geprüft werden.
Die 100 Commands nacheinander im Client-Timer zu senden, wäre aber sicher suboptimal.
Wie macht man es korrekt? Alle Commends in einer Liste verwalten und im Timer jeweils einen Schritt weiter iterieren und 1 Command senden?


Delphi-Quellcode:
// Server

procedure TForm1Server.FormCreate(Sender: TObject);
var
  Binding: TIdSocketHandle;
begin
  IdIPWatch1.Active := True;
  Memo1.Text := IdIPWatch1.LocalIP;
  Binding := IdTCPServer1.Bindings.Add;
  Binding.IP := IdIPWatch1.LocalIP;
  Binding.Port := 40000;
  IdTCPServer1.Active := True;
  IdIPWatch1.Active := False;
  Button1Click(Sender);
end;

procedure TForm1Server.IdTCPServer1Execute(AContext: TIdContext);
var
  cmd: String;
begin
  try
    Inc(Counter);
    cmd := Trim(AContext.Connection.IOHandler.ReadLn);

    if (cmd = '@Now') then
    begin
      AContext.Connection.IOHandler.WriteLn(DateTimeToStr(Now));
    end;

    if (cmd = '@Counter') then
    begin
      AContext.Connection.IOHandler.WriteLn(IntToStr(Counter));
    end;

  finally
    AContext.Connection.Disconnect;
  end;
end;

// Client

procedure TForm1Client.Timer1Timer(Sender: TObject);
begin
  Gui.sGuiNow := DateTimeToStr(Now);
  Inc(Gui.GuiCounter);
  Gui.sGuiCounter := IntToStr(Gui.GuiCounter);

  IdTCPClient1.Connect;
  try
    IdTCPClient1.IOHandler.WriteLn('@Now');
    Gui.sNow := IdTCPClient1.IOHandler.ReadLn();
  finally
    IdTCPClient1.Disconnect;
  end;

  IdTCPClient1.Connect;
  try
    IdTCPClient1.IOHandler.WriteLn('@Counter');
    Gui.sCounter := IdTCPClient1.IOHandler.ReadLn();
  finally
    IdTCPClient1.Disconnect;
  end;

  ...
end;

Sir Rufo 8. Mär 2014 23:24

AW: Indy 10 - Adresse wird bereits verwendet
 
Zu 2 und 3 kann ich sagen:

Packe das in Threads (jetzt hau mich nicht gleich :mrgreen:) dann hat die GUI keine Hänger und die Commands kannst du weiterverarbeiten nachdem die Antwort vom Server gekommen ist (evtl. noch synchronisierte Rückmeldung an die GUI)

Harry Stahl 9. Mär 2014 00:31

AW: Indy 10 - Adresse wird bereits verwendet
 
Der Empfehlung von Sir Rufo schließe ich mich an.

Bei Deiner aktuellen Lösung läuft der Timer wahrscheinlich irgendwann über. D.h es kommt schon die nächste Timeranforderung, bevor der vorherige Timer- Event abgearbeitet war. So was kann man vermeiden, indem man am Anfang des Events den Timer ausschaltet und am Ende wieder einschaltet.

sx2008 9. Mär 2014 01:25

AW: Indy 10 - Adresse wird bereits verwendet
 
Haut mich nicht gleich wenn ich behaupte dass man keine Threads braucht.
Man benötigt das Event OnReadData das immer dann abgefeuert wird wenn Daten angekommen sind.
(bin mir nur nicht sicher ob Indy dieses Event überhaupt hat. Die alte Unit ScktComp hat das Event jedenfalls)

Das periodische Senden von @Now und @Counter kann man problemlos mit einem Timer vornehmen; nur der Empfang der Antwort wird von dem Event getriggert.

stahli 9. Mär 2014 09:47

AW: Indy 10 - Adresse wird bereits verwendet
 
Also erst mal sollte sich hier möglichst niemand prügeln weil sonst die nächsten Delphitage Maßnahmen bräuchten wie bisher Fußballspiele ... :stupid:

Also ich nutze keine VCL-Controls und eine Synchronisation sollte nicht notwendig sein. Wenn es später komplexer wird will ich gemeinsame Zugriffe aber über critcal Sections schützen.

Ich verstehe das auch so, dass die Indy-Antwort in einem Thread kommt und Indy das kapselt. So ganz habe ich das allerdings noch nicht nachvollziehen können.
Eigentlich müsste dann die Zeile bei *1 vom Compiler ja in einen Thread gekapselt werden, was ich auch wieder nicht glauben kann.
Dafür würde es wohl eine Anweisung brauchen wie IdTCPClient1.IOHandler.ReadLnInString(S); damit ein Thread erzeugt und die Variable übergeben werden und der Mainthread weiter laufen könnte. Insofern glaube ich eher, dass der Timer eine längere Behandlung braucht und auf die Übertragung warten muss.

Daher denke ich auch nicht, dass ein weiterer Thread hilft oder das temp. Ausschalten des Timers (werde ich natürlich testen, komme ich aber erst später dazu).
M.E. feuert ein Timer ja anderseits auch nicht wenn er gerade noch läuft. Maximal sollte es so sein, dass ein Timer nach Beendigung "gleich wieder" feuert, aber zwischendrin bekommt ja der Mainthread m.E. erst mal wieder Zeit.

Es scheint so zu sein, dass Problem 1) nur entsteht, wenn mehrere Clients auf dem gleichen PC laufen.


Ich könnte mir folgendes als zweckmäßig vorstellen:

Delphi-Quellcode:
procedure TForm1Client.Timer1Timer(Sender: TObject);
begin
   Gui.sGuiNow := DateTimeToStr(Now);
   Inc(Gui.GuiCounter);
   Gui.sGuiCounter := IntToStr(Gui.GuiCounter);

  if NichtSchonEineAnFrageFürXXXLäuftUndDieLetzteAnfrageÄlterIstAlsEineHalbeSekunde then
  begin
     IdTCPClient1.Connect;
     try
       IdTCPClient1.IOHandler.WriteLn('@Now');
       Gui.sNow := IdTCPClient1.IOHandler.ReadLn(); // *1
     finally
       IdTCPClient1.Disconnect;
     end;
  end;

  if NichtSchonEineAnFrageFürXXXLäuftUndDieLetzteAnfrageÄlterIstAlsEineHalbeSekunde then
  begin
     IdTCPClient1.Connect;
     try
       IdTCPClient1.IOHandler.WriteLn('@Counter');
       Gui.sCounter := IdTCPClient1.IOHandler.ReadLn(); // *1
     finally
       IdTCPClient1.Disconnect;
     end;
  end;

  ...
end;

Ich bräuchte mal eine :glaskugel: damit ich den rechten Weg finde...

Der schöne Günther 10. Mär 2014 08:24

AW: Indy 10 - Adresse wird bereits verwendet
 
Ich verstehe nicht- Warum disconnectest du dauernd von allen Seiten? Warum lässt du die Verbindung nicht einfach offen?
Der Socket Error 10048 bedeutet übrigens auch, dass man versucht, einen Socket an eine IP/Port-Kombo zu binden, die grade noch im Begriff ist, disconnected zu werden. Nicht dass Indy das irgendwie teilweise asynchron macht und das dann daher rührt.

PS: Ich weiß noch nicht ganz genau was du auf lange Sicht vor hast, aber ich bin mit den "Cromis IMC/IPC"-Komponenten wirklich glücklich geworden. Im Endeffekt noch eine Abstraktionsschicht auf den Indy-TCP-Komponenten obendrauf. Grade "Pures TCP" fände ich jetzt wirklich noch etwas zu grob. Das Cromis-Paket macht einem die Sache wirklich viel angenehmer.

Sir Rufo 10. Mär 2014 08:45

AW: Indy 10 - Adresse wird bereits verwendet
 
Zitat:

Zitat von stahli (Beitrag 1251274)
Ich verstehe das auch so, dass die Indy-Antwort in einem Thread kommt und Indy das kapselt.

Das ist richtig, die Implementierung (mit einem Thread auf der Indy-Seite) ist aber für deine Fragestellung hier völlig belanglos.
Denn
Delphi-Quellcode:
// Der Befehl kehrt zurück, wenn gesendet wurde
IdTCPClient1.IOHandler.WriteLn('@Now');
// Der Befehl kehrt zurück, wenn empfangen wurde
Gui.sNow := IdTCPClient1.IOHandler.ReadLn();
Wenn das Senden und Empfangen nun 5 Minuten dauert, dann ist dein UI auch 5 Minuten blockiert!

Um diese UI-Blockade zu vermeiden, musst du das zwangsläufig in einen eigenen Thread packen.

Auf der Indy-Seite ist das (im Groben) so implementiert
Delphi-Quellcode:
procedure WriteLn;
var
  LSendThread : TSendThread;
begin
  LSendThread.Create;
  try
    LSendThread.WaitFor; // warten, bis der Thread fertig ist
  finally
    LSendThread.Free;
  end;
end;

jaenicke 10. Mär 2014 09:22

AW: Indy 10 - Adresse wird bereits verwendet
 
Zitat:

Zitat von stahli (Beitrag 1251274)
Also ich nutze keine VCL-Controls und eine Synchronisation sollte nicht notwendig sein.

Solange du auch keine Windows Controls über deren Handles in mehreren Threads nutzt (dazu gehört auch die Nachrichtenwarteschlange des jeweiligen Fensters) ist das korrekt. Ansonsten musst du auch ohne VCL synchronisieren.

stahli 10. Mär 2014 09:26

AW: Indy 10 - Adresse wird bereits verwendet
 
Mal schnell der aktuelle Stand:

1)

Ich habe nochmal gesucht nach "Indy 10048" (auch in der DP) und das für mich folgendermaßen interpretiert:

Indy sammelt einige tausend Verbindungen und räumt die nach dem Schließen wieder auf. Das dauert 1-2 Minuten.
Ich hatte nun einfach aller paar Millisekunden neue Daten abgerufen. Dann war der Puffer (wenn mehrere Clients liefen) irgendwann voll und neue Verbindungen wurden benötigt ehe die alten aufgeräumt waren. Das führte zu den Fehlermeldungen.
Indy arbeitet aber weiter und der nächste Verbindungsversuch klappt werden wieder Daten übertragen. Wenn nicht gibt es laufende Fehlermeldungen.

Jetzt habe ich einen erneuten Abruf eines Wertes frühestens 250ms nach dem letzten Aufruf ausgeführt (so eine Unterbrechungsregelung will ich ja real ohnehin managen) und jetzt liefen dutzende Clients auf einem Rechner und im Netzwerk problemlos über lange Zeit.

Das Problem war also die zu häufige Datenübertragung (was verständlich ist, ich dachte nur das ginger wegen der kurzen Strings schon gut).

2)

Die Hakler bei Client auf NB und Server auf PC scheint Kaspersky auf dem PC zu verursachen. Jedenfalls hat das einige kritische Netzwerkzugriffe gemeldet. Ich werde das mal noch in Ruhe ansehen.
Alle anderen C/S-Konfigurationen laufen absolut flüssig.

3)

Die TCP-Übertragung will ich ohnehin einem Manager übertragen, der die Ergebnisse für die GUI dann puffert und verwaltet. Insofern kann ich ich die reale Datenübertragung danach optimieren, welche Daten wirklich neu benötigt werden.


--

Also insgesamt sieht das jetzt schon ganz gut aus.
Die GUI (nicht VCL) läuft sehr flüssig. Blockiert würde sie wenn Indy hängen würde.

Ich werde später die Übertragung wohl noch in einen Thread packen (insbesondere wie jaenicke schreibt, weil ich auf Maus- und Tastatur reagieren will), aktuell ist das in der derzeitigen Phase aber erst mal nicht erforderlich damit alles flüssig läuft.

Ich halte Euch auf dem Laufenden.

mjustin 10. Mär 2014 09:48

AW: Indy 10 - Adresse wird bereits verwendet
 
Hier wird der Hintergrund erläutert und für einen Beispielserver die maximale Anzahl gleichzeitig geöffneter Ports berechnet (470):

http://www.fromdual.com/huge-amount-...it-connections

Müssen die Verbindungen sofort geschlossen werden? In der Praxis hält zum Beispiel HTTP 1.1 die Verbindung längere Zeit aufrecht und sendet über sie mehrere Requests. (Das ist im o.g. Artikel die erste Lösung: put more workload in one connection)


Alle Zeitangaben in WEZ +1. Es ist jetzt 17:46 Uhr.
Seite 1 von 2  1 2      

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