Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Netzwerke (https://www.delphipraxis.net/14-netzwerke/)
-   -   Socket Error 10054 Connection reset by peer - wie behandeln? (https://www.delphipraxis.net/187187-socket-error-10054-connection-reset-peer-wie-behandeln.html)

Medium 5. Nov 2015 10:57

Socket Error 10054 Connection reset by peer - wie behandeln?
 
Huhu Leute,

ich habe ein Programm, mit dem ich via TCP (Indy) Anfragen an ein paar Geräte sende (keine PCs, sondern Modbus->TCP Gateways), um Daten von diesen anzufordern. Das klappt auch alles recht gut, bis auf bei einem der Gateways. Dieses kommt, teils nach wenigen Minuten, teils erst nach einem Tag, auf die Idee die Connection zu resetten, was zu o.g. Meldung bei mir führt.
Ab dieser Meldung kann ich mit dem Gateway nicht mehr kommunizieren. Als Lösung dafür habe ich es so programmiert, dass ich bei Auftreten des Fehler die komplette TIdTCPClient Instanz lösche und neu instanziiere, um einen ganz frischen neuen Socket zu bekommen. Leider gibt es mit der neuen Instanz genau dasselbe Problem: Es geht sofort mit Error 10054 weiter.

Aber! Starte ich mein Programm neu, geht es völlig problemlos!

Was macht ein Programmneustart mehr, was ein
Delphi-Quellcode:
IdTCPClient.Free;
nicht zu leisten vermag? An den Gateways kann ich leider nichts machen. Ich muss das Problem in meinem Programm behandeln, was ja scheinbar möglich sein muss, wenn ein Neustart (nur des Programms, nicht des ganzen PCs) funktioniert.

Ein verknüpftes Problem: Ich fange den Fehler in einem try..except ab. Aber das Fehlerfenster wird trotzdem generiert, auch außerhalb der IDE. Wie kann ich das unterdrücken? Mein Programm soll nachher 24/7 unbeaufsichtigt laufen, und da wäre es extrem unschön wenn es diese Meldungen ansammelt.


Besten Dank im Voraus!

TiGü 5. Nov 2015 11:25

AW: Socket Error 10054 Connection reset by peer - wie behandeln?
 
Detailfrage:
Die TIdTCPClient-Instanz läuft in einen eigenen Thread?

Medium 5. Nov 2015 15:47

AW: Socket Error 10054 Connection reset by peer - wie behandeln?
 
Ich bin leider erst jetzt wieder im Büro, sorry. Die Instanzen laufen im Kontext des Mainthreads, da es für das Programm recht egal war, dass es zwischendurch für ein paar ms nicht reagiert. 2 von den 3 Verbindungen laufen auch ohne Probleme.

Edit: Jetzt ist die VM auch hochgefahren, so dass ich Code posten kann:
Delphi-Quellcode:
// TCP Komponenten Erstellen
procedure TValueGroup.CreateTCP;
begin
  if Assigned(FTCP) then
  begin
    if FTCP.Connected then
      FTCP.Disconnect(false);
    FreeAndNil(FTCP);
  end;
  FTCP := TIdTCPClient.Create;
  FTCP.IOHandler := TIdIOHandlerStack.Create;
  FTCP.IOHandler.ReadTimeout := 1000;
  FTCP.Host := FIP;
  FTCP.Port := FPort;
  FTCP.OnStatus := OnTCPStatus;
end;
Delphi-Quellcode:
// Verbindungsherstellung
procedure TValueGroup.ConnectToGateway;
begin
  try
    FTCP.ConnectTimeout := 100;
    if not FTCP.Connected then FTCP.Connect();
  except
    on e: Exception do
    begin
      frmTCPLog.Add(FTCP.Host+': Connect failed. Recreating.');
      try
        CreateTCP;
      except
      end;
    end;
  end;
end;
Delphi-Quellcode:
// Meine Daten-Refresh Prozedur
procedure TValueGroup.RefreshData;
var
  remainingBytes: Integer;
  sAdr: Integer;
begin
  remainingBytes := FMaxAddress+4 + 2;
  SetLength(FBuffer, remainingBytes);
  try
    if not FTCP.Connected then
      ConnectToGateway;

    sAdr := 0;
    while remainingBytes > 255 do
    begin
      FRequestBuffer[1] := Byte(sAdr shr 8);
      FRequestBuffer[2] := Byte(sAdr and $FF);
      FRequestBuffer[4] := 255;
      SendBuffer(TCP, RequestBuffer);
      ReceiveBuffer(TCP, FBuffer, sAdr, 255);
      dec(remainingBytes, 255);
      inc(sAdr, 255);
    end;

    FRequestBuffer[1] := Byte(sAdr shr 8);
    FRequestBuffer[2] := Byte(sAdr and $FF);
    FRequestBuffer[4] := Byte(remainingBytes and $FF);
    SendBuffer(TCP, RequestBuffer);
    ReceiveBuffer(TCP, FBuffer, sAdr, remainingBytes);

    BufferToValues;
  except
    on e: Exception do
    begin
      frmTCPLog.Add(FTCP.Host+' Recovering from "'+e.Message+'"');
      CreateTCP;
    end;
  end;
end;
Der Fehler der auftritt, ist der in diesem except-Block der zuletzt gezeigten Prozedur. Das heisst das bloße Connect geht, aber die Kommunikation führt zum Fehler. Aber halt nur bei einem Partner, und nach unbestimmter Zeit tadelloser Funktion.

Der Vollständigkeit halbar noch die Send- und RecieveBuffer Funktionen:
Delphi-Quellcode:
function TValueGroup.ReceiveBuffer(AClient: TIdTCPClient; var ABuffer: TBytes; aOffset, aLength: Integer): Boolean;
var
  b: TBytes;
begin
  Result := True;
  try
    SetLength(b, aLength);
    FillChar(b[0], aLength, 0);
    AClient.IOHandler.ReadBytes(b, -1, false);
    Move(b[0], ABuffer[aOffset], aLength);
  except
    Result := False;
  end;
end;

function TValueGroup.SendBuffer(AClient: TIdTCPClient; ABuffer: TBytes): Boolean;
begin
  try
    Result := True;
    try
      AClient.IOHandler.WriteBufferOpen;
      AClient.IOHandler.Write(ABuffer);
      AClient.IOHandler.WriteBufferFlush;
    finally
      AClient.IOHandler.WriteBufferClose;
    end;
  except
    Result := False;
  end;
end;
Der OnStatus-Handler loggt nur die Statusänderungen und unternimmt sonst nichts.

Namenloser 5. Nov 2015 20:58

AW: Socket Error 10054 Connection reset by peer - wie behandeln?
 
Zitat:

Zitat von Medium (Beitrag 1320634)
Als Lösung dafür habe ich es so programmiert, dass ich bei Auftreten des Fehler die komplette TIdTCPClient Instanz lösche und neu instanziiere, um einen ganz frischen neuen Socket zu bekommen. Leider gibt es mit der neuen Instanz genau dasselbe Problem: Es geht sofort mit Error 10054 weiter.

Aber! Starte ich mein Programm neu, geht es völlig problemlos!

Was macht ein Programmneustart mehr, was ein
Delphi-Quellcode:
IdTCPClient.Free;
nicht zu leisten vermag?

Das ist in der Tat seltsam. Ich kann es mir nur damit erklären, dass es etwas mit der Zeit zu tun hat, die zwischen dem Free und dem Neuerzeugen vergeht. Es gibt das manchmal bei Servern, dass eine Verbindung, nachdem sie beendet (besser gesagt abgebrochen) wurde, erst mal eine Weile weiter bestehen bleibt (TIME-WAIT). Während der alte Socket im TIME-WAIT-Zustand ist, kann man keinen neuen Socket auf dem Port erstellen.

Eigentlich sollte das nach meinem begrenzten Wissen nur bei Servern ein Problem sein und nicht bei Clients. Aber ich könnte mir vorstellen, dass es dennoch irgendwas damit zu tun hat.¹ Du kannst ja mal zum Testen nach dem Free ein Sleep o.ä. einbauen, das ungefähr so lange dauert wie ein Programmneustart dauern würde.

Oder kann es sein, dass irgendeine Firewall oder Virenscanner auf dem Client verrückt spielt?

Edit ¹: Jetzt wo ich noch mal darüber nachdenke: Vermutlich ist der Server oder die Verbindung zum Server irgendwie abgeschmiert (wurde nicht korrekt beendet), und der Socket bleibt deshalb im TIME-WAIT-Zustand. Der Server versucht einen neuen Listener-Socket zu erstellen, kann er aber nicht, weil der alte Socket noch im TIME-WAIT-Zustand ist. Natürlich lässt das Betriebssystem des Servers keine neuen Verbindungen zu dem alten Socket zu, weil der ja tot ist, weshalb die Verbindungsversuche deines Clients mit "Connection reset by peer" zurückgewiesen werden. Wenn du eine Weile wartest, ist der alte Socket weg, der Server kann einen neuen erstellen, und dann klappen die Verbindungen wieder.

Medium 5. Nov 2015 21:12

AW: Socket Error 10054 Connection reset by peer - wie behandeln?
 
FW oder Virenscanner kann ich ausschließen, aber deine erste Idee klingt gar nicht mal so schlecht! Mein Programm braucht in der Tat ein paar Sekunden zum Start, da erst noch ein größeres TreeView aus der DB befüllt wird nebst anderem Init-Krams. Ich werde morgen testweise eine Pause einbauen, ich hoffe dass sich das Problem jetzt nicht zu rar zum brauchbar testen macht.

Ich kenne das auch noch von Siemens SPSen, die nach (auch regulärem) Trennen der Socket-Verbindung zu projektierten Fetch/Write Ports rund 20s brauchten bis sie wieder eiine Verbindung annahmen. Aber seit wir auf das S7Online Protokoll via libnodave umgestiegen sind, kein Problem mehr. Von daher absolut plausibel. Bin gespannt.
Das einzige was mich ein wenig pessimistisch stimmt ist, dass ich heute morgen im Log Verbindungsversuche durchgehend von 4:30 bis 9:00 Uhr hatte, und ich das Prgoramm dann beendet habe. Ich hoffe, dass das Gateway dann aktiv geblockt hat wegen zu früher Anfragen oder so. Was aber auch dämlich wäre. Egal, probieren! Danke!

Edit wegen deinem Edit: Klingt durchaus auch plausibel. Ich weiss jetzt nicht wie genau die Gateways intern funktionieren - Mini-PCs sind es definitiv nicht - aber vielleicht haben die ein vergleichbares Problem. Das könnte auch ggf. erklären warum schnell wiederholte Anfragen den Zustand einfrieren: Vielleicht hat der "tote" Socket noch eine Karenzzeit nach dem er das Reset geschickt hat, welche ich so immer neu aufziehen würde. Probieren!

Sir Rufo 5. Nov 2015 21:52

AW: Socket Error 10054 Connection reset by peer - wie behandeln?
 
Hast du auch schon versucht den InputBuffer vom IOHandler zu löschen, so wie hier beschrieben
http://stackoverflow.com/a/10523444/1744164

Also so in etwa
Delphi-Quellcode:
// TCP Komponenten Erstellen
procedure TValueGroup.CreateTCP;
var
  tmp: TIdTCPClient;
begin
  if Assigned( FTCP )
  then
    begin
      if FTCP.Connected
      then
        try
          FTCP.Disconnect( false ); // Hier kommt u.U. der Socket Error 10054
        except
          on e: Exception do
            frmTCPLog.Add( FTCP.Host + ': Error on Disconnect "' + e.ClassName + ': ' + e.Message + '". Do not care, we are recreating the connection' );
        end;
      if Assigned( FTCP.IOHandler )
      then
        FTCP.IOHandler.InputBuffer.Clear;
      FreeAndNil( FTCP );
    end;
  // Hier sollte eigentlich kein Fehler passieren,
  // aber falls doch, dann so absichern
  tmp := TIdTCPClient.Create;
  try
    tmp.IOHandler            := TIdIOHandlerStack.Create;
    tmp.IOHandler.ReadTimeout := 1000;
    tmp.Host                 := FIP;
    tmp.Port                 := FPort;
    tmp.OnStatus             := OnTCPStatus;

    FTCP := tmp;
    tmp := nil;
  finally
    tmp.Free;
  end;
end;

// Verbindungsherstellung
procedure TValueGroup.ConnectToGateway;
begin
  try
    FTCP.ConnectTimeout := 100;
    if not FTCP.Connected
    then
      FTCP.Connect( );
  except
    on e: Exception do
      begin
        // Exception komplett mitloggen
        frmTCPLog.Add( FTCP.Host + ': Connect failed with "' e.ClassName + ': ' + e.Message + '" Recreating.' );
        // try
        CreateTCP;
        // except
        // end;
      end;
  end;
end;

// Meine Daten-Refresh Prozedur
procedure TValueGroup.RefreshData;

var
  remainingBytes: Integer;
  sAdr         : Integer;
begin
  remainingBytes := FMaxAddress + 4 + 2;
  SetLength( FBuffer, remainingBytes );
  try
    if not FTCP.Connected
    then
      ConnectToGateway;

    sAdr := 0;
    while remainingBytes > 255 do
      begin
        FRequestBuffer[ 1 ] := Byte( sAdr shr 8 );
        FRequestBuffer[ 2 ] := Byte( sAdr and $FF );
        FRequestBuffer[ 4 ] := 255;
        SendBuffer( TCP, RequestBuffer );
        ReceiveBuffer( TCP, FBuffer, sAdr, 255 );
        dec( remainingBytes, 255 );
        inc( sAdr, 255 );
      end;

    FRequestBuffer[ 1 ] := Byte( sAdr shr 8 );
    FRequestBuffer[ 2 ] := Byte( sAdr and $FF );
    FRequestBuffer[ 4 ] := Byte( remainingBytes and $FF );
    SendBuffer( TCP, RequestBuffer );
    ReceiveBuffer( TCP, FBuffer, sAdr, remainingBytes );

    BufferToValues;
  except
    on e: Exception do
      begin
        // Exception komplett mitloggen
        frmTCPLog.Add( FTCP.Host + ': Recovering from "' + e.ClassName + ': ' + e.Message + '"' );
        CreateTCP;
      end;
  end;
end;

Medium 5. Nov 2015 22:07

AW: Socket Error 10054 Connection reset by peer - wie behandeln?
 
Über den SO bin ich bei meiner Suche auch gestolpert, ja :). Das Neuerstellen der TIdTCPClient Komponente (inkl. ihres IOHandlers) sollte das durchaus erledigen. Aber ich habe davor tatsächlich das Stück Code aus der Antwort dort probiert. Leider ohne Erfolg. (Durch den Post bin ich erst auf die Idee gekommen, Disconnect() mit dem Parameter aufzurufen.) Trotzdem danke für das Interesse!!

Dejan Vu 6. Nov 2015 06:35

AW: Socket Error 10054 Connection reset by peer - wie behandeln?
 
ich hatte vor Jahren mit Delphi im SPS-Umfeld zu tun. Ich kann mich erinnern, das gleiche Problem gehabt zu haben, woraufhin ich bei jeder TCP-Zicke das Programm gnadenlos beendet habe (Halt). Durch einen Neustart und anschließendem reconnect war das Problem dauerhaft gelöst. Der Neustart war einfach eine Endlosschleife in einer Batchdatei
Code:
:Loop
MeinProgramm.EXE
Goto Loop
Mir war und ist klar, das diese Art der Lösung ziemlich unprofessionell ist. Aber seit dieser Maßnahme ist Ruhe im Karton-

Medium 7. Nov 2015 00:17

AW: Socket Error 10054 Connection reset by peer - wie behandeln?
 
Ich bin vorhin letztlich wieder auf UDP Kommunikation gegangen hier, wo das kein Problem ist. Ursprünglich bin ich von UDP zu TCP gewechselt, weil ich den Eindruck hatte, dass ich u.U. die Antworten in mehreren Paketen bekomme, die ich leider dank fehlender ID im Protokoll nicht einer spezifischen Anfrage zuordnen kann. TCP ist was solches Handshaking angeht ja etwas verlässlicher.
Aber ich habe meinen Fehler gefunden, und ja: Er war dämlich. In der Doku von den Gateways steht, dass ich von deren internem Array immer je 1000 Werte am Stück abfragen kann. Also habe ich das getan. Allerdings hieß "Wert" bei denen "Byte", bei mir aber "Single"... ich habe einfach 4x zu große Anforderungen geschickt, was meine Lücken in den Daten erklärt. Autsch!

Ergo: Kleinere Anfragen, auf deren Antworten ich brav warte und sie jeweils zusammen stückel, und schon ist alles paletti via UDP. Ich vermute schon fast, dass mich das Gateway per TCP ab und zu "rausgeworfen" haz, weil es irgendwann gemerkt hat, dass die Request-Buffer nach zu viel gefragt haben. (Bei den anderen 2 Gateways erfrage ich weniger, aber dennoch >1000 am Stück. Weiss der Geier warum dieses eine empfindlicher gewesen sein könnte.) Aber das werde ich nicht weiter erforschen, die ganze Nummer hat jetzt schon WEIT mehr Zeit und Nerven gekostet als man jemals zugeben dürfte.

Saß der Fehler doch mal wieder vor dem Bildschirm. Verdammt. (Auch wenn die Frage, wie man mit einem vom Peer gesendeten Reset am besten umgeht prinzipiell nicht ohne Relevanz ist.)

Vielen Dank nochmals an alle hier!! :dp:

Dejan Vu 7. Nov 2015 08:28

AW: Socket Error 10054 Connection reset by peer - wie behandeln?
 
Du musst dich aber nicht wundern, wenn von deinen UDP Paketen nicht immer alle ankommen.


Alle Zeitangaben in WEZ +1. Es ist jetzt 03:35 Uhr.
Seite 1 von 2  1 2      

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