![]() |
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:
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.
IdTCPClient.Free;
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! |
AW: Socket Error 10054 Connection reset by peer - wie behandeln?
Detailfrage:
Die TIdTCPClient-Instanz läuft in einen eigenen Thread? |
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:
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.
// 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 Vollständigkeit halbar noch die Send- und RecieveBuffer Funktionen:
Delphi-Quellcode:
Der OnStatus-Handler loggt nur die Statusänderungen und unternimmt sonst nichts.
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; |
AW: Socket Error 10054 Connection reset by peer - wie behandeln?
Zitat:
![]() 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. |
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! |
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
![]() 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; |
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!!
|
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:
Mir war und ist klar, das diese Art der Lösung ziemlich unprofessionell ist. Aber seit dieser Maßnahme ist Ruhe im Karton-
:Loop
MeinProgramm.EXE Goto Loop |
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: |
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. |
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