![]() |
TIdHTTPServer: Programmabsturz bei langwieriger ResponseInfo-Berechnung
Hallo,
ich schreibe gerade eine Anwendung zur Steuerung einer Startampel für Motorsport-Veranstaltungen mittels eines Raspberry Pi und compiliere diese mit FreePascal 2.6.0. Dieser steuert über die GPIO-Ports die eigentliche Ampel-Schaltung bzw. liest deren Zustand aus. Dazu enthält die Anwendung auch einen Webserver auf Indy-Basis (TIdHTTPServer). Gleichzeitig ist an das Kamera-Interface des Pi auch noch eine passende Kamera angeschlossen, die einen Videostream liefert, aus dem dann interrupt-gesteuert bei Auslösen einer Lichtschranke das aktuelle Frame des Videostreams zwischengespeichert wird. Dies zur Überwachung des Startbereiches, für den die Startampel zuständig ist. Eben dieses zwischengespeicherte Videoframe möchte ich als JPEG-Bild über den HTTP-Server ausgeben. Geht auch alles wunderbar, solange nur 1 Client auf das Webinterface zugreift. Sobald mehrere auf das Videobild zugreifen, stürzt die Software ab. Ich muss folgendes dazu sagen: die Umwandlung des Videoframes in das JPEG-Format einschließlich der Ausgabe in einen MemoryStream dauert ca. 2-3 Sekunden. Solange müssen alle Webclients warten, da die Erstellung mittels einer Critical Section geschützt ist. Nochmal eine kurze Funktionsübersicht: - Steuerung bzw. Auslesen der Startampel mittels GPIOs und Interrupts - Abfangen einzelner Video-Frames der CSI-Kamera über einen GPIO-Interrupt, der durch eine angeschlossene Lichtschranke ausgelöst wird - Ausgabe des Startampel-Zustands (Zählerstand, welche roten bzw. grünen LEDs leuchten etc.) + Steuerung über Webinterface mittels von TIdHTTPServer abgeleiteter Klasse Nach langem Testen habe ich herausgefunden, dass die zeitliche Dauer der JPEG-Erstellung das Problem ist, da ich den Absturz der Anwendung auch dadurch herbeiführen kann, dass ich in das DoCommandGet-Event ein Sleep(2000) einfügen kann. Dies führt dann selbst beim Aufruf einfacher Text-Pages zum Absturz, unabhängig davon, ob ich "ContentText" oder "ContentStream" für die Ausgabe verwende. Dann auch schon, wenn nur 1 Webclient auf das Webinterface zugreift. Wenn ich in der "TIdHTTPResponseInfo.WriteContent"-Methode (TIdCustomHTTPServer.pas)die "FConnection.IOHandler.Write"-Aufrufe auskommentiere, geht naturgemäß zwar keine Webausgabe, aber dann stürzt auch mein Programm nicht ab. Bei kurzen Antwortzeiten (kleine Seiten, Bilder etc.), die nur wenige Millisekunden benötigen, funktioniert alles wunderbar. Ein kurzes Code-Beispiel:
Delphi-Quellcode:
Es wird auch keine Fehlermeldung angezeigt. Die Anwendung ist eine Konsolenanwendung (ohne LCL) und ist dann einfach weg.
procedure THTTPServer.DoCommandGet(AContext: TIdContext;
ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo); var fs : TFileStream; fileExt, document, content, filePath, fileName : string; commandNumber : Integer; begin document := 'HTTP' + StringReplace(ARequestInfo.Document, '..', '', [rfReplaceAll]); if document = 'HTTP/' then document := document + 'index.html'; filePath := SysUtils.ExtractFilePath(document); fileName := SysUtils.ExtractFileName(document); try // ... // Hier werden ContentText bzw. ContentStream befüllt // ... finally if Assigned(AResponseInfo.ContentStream) then begin try Sleep(2000); // <--- führt zum kommentarlosen Crash AResponseInfo.WriteContent; except on e: exception do Writeln(e.Message); end; end else if AResponseInfo.ContentText <> '' then begin Sleep(2000); // <--- führt ebenfalls zum kommentarlosen Crash AResponseInfo.WriteContent; end; end; end; Ich hoffe, dass von euch jemand Erfahrungen und evtl. auch Lösungsansätze in der Richtung hat. Vielen Dank für eure Mühen! :thumb: |
AW: TIdHTTPServer: Programmabsturz bei langwieriger ResponseInfo-Berechnung
Wenn du das Signal der Lichtschranke bekommst, dann wandle den Frame in ein JPEG um (eigener Thread) und liefere das einfach an die Clients aus.
Solange da noch nichts vorliegt liefere einfach ein Dummy-Bild aus. |
AW: TIdHTTPServer: Programmabsturz bei langwieriger ResponseInfo-Berechnung
Danke für die Antwort. Aber das Blockieren (bis das Bild fertig berechnet ist) macht schon Sinn.
Und generell möchte ich ungern daraufhin programmieren, irgendwelche zeitlichen Grenzen zu unterbieten, um einen Programmabsturz zu verhindern. Es könnte ja auch aus anderen Gründen dazu kommen, dass die Bearbeitung einer Anfrage mal länger dauert. Und dann wäre der Programmabsturz da. Von daher wäre es gut, wenn man das Problem irgendwie lösen könnte anstatt es für den Einzelfall zu umgehen. |
AW: TIdHTTPServer: Programmabsturz bei langwieriger ResponseInfo-Berechnung
LCL? Delphi oder doch Lazarus?
Also der ganze Server ist also eine Konsolenanwendung und du hast vermutlich vergessen die eine Exception abzufangen. :angel: Und hier wird vermutlich das Transfer-Timeout der Klientanwendung zuschlagen. Es ist grundsätzlich so, daß wenn Exceptions, welche bis zur Basis zum Windows gelangen das Programm abschießen. Bei der VCL ist es so, daß die Message-Behandlung über ein Try-Except geschützt ist und Exceptions so praktisch nie bis zur obersten Ebene durchgelangen. Windows zeigt keine Exceptions an. Dieses macht das Programm selber, indem es sie abfängt und "verarbeitet". Ist eine Exception durchgerauscht, dann gibt das Programm maximal noch einen Fehlercode zurück (System.ExitCode > in DOS das berühmte ERRORLEVEL) und eventuell wird noch eine Meldung in die Ereignisanzeige vom Windows geschrieben. Aus diesem Grund hatte Embarcadero vor einer ganzen Weile auch die Vorlage für Konsolenanwendungen erweitert.
Delphi-Quellcode:
program Project10;
{$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils; begin try { TODO -oUser -cConsole Main : Code hier einfügen } except on E: Exception do Writeln(E.ClassName, ': ', E.Message); end; end. |
AW: TIdHTTPServer: Programmabsturz bei langwieriger ResponseInfo-Berechnung
Zitat:
Zitat:
Wie man an dem Beispiel oben sieht, habe ich zumindest für den Fall des ContentStreams (bei dem der Fehler ursprünglich auftrat) einen try...except-Block um den "WriteContent"-Aufruf herum platziert. Ich werde aber wohl mal zum Test einen try...except-Block um das ganze Hauptprogramm herum machen und schauen, ob mir dann eine Fehlermeldung angezeigt wird. Evtl. tritt die Exception ja an einer anderen Stelle im Programm auf. Und was Windows oder DOS machen, ist ja für einen Raspberry Pi nicht ganz so interessant ;) |
AW: TIdHTTPServer: Programmabsturz bei langwieriger ResponseInfo-Berechnung
Das mit dem
Delphi-Quellcode:
in deinem Code habe ich aber auch nicht verstanden.
try .. finally
Du möchtest also, auch wenn vorher eine Exception geworfen wurde, immer einen Response abliefern, ohne zu wissen, ob der nun sinnvoll oder sinnentleert ist. Wenn in dem Code eine Exception auftritt, dann kann man die behandeln, weil man das erwartet hat und dafür eine Möglichkeit hat, das noch zu retten oder man lässt diese Exception passieren (und fängt diese an anderer Stelle zum Loggen). Der
Delphi-Quellcode:
Block ist hervorragend zum Aufräumen gedacht um keine Speicherlecks zu hinterlassen. Oder etwas zu schließen, was vorher geöffnet wurde.
finally
Insgesamt sehe ich hier auch eine fehlerhaftes Management der Exceptions. Und warum der gleiche Frame immer wieder in ein JPEG umgerechnet werden soll erschließt sich mir auch nicht. |
AW: TIdHTTPServer: Programmabsturz bei langwieriger ResponseInfo-Berechnung
Das try...finally ist weniger der Exceptions wegen (die erwarte ich im try-Block nicht), sondern dazu, dass ich im try-Block ein exit ausführen kann, wenn ich weiter innen mit dem Aufstellen der Antwort fertig bin. Kann man auch anders lösen, muss man aber nicht.
Im übrigen ist das ein Beispiel-Code, um zu zeigen, wo das Sleep platziert ist, und nicht etwa der Code im Endzustand. Da müsste auch noch ein try...except, um den Block, der den ContentText behandelt herum, um konsistent zu sein. Aber der beschriebene Fehler tritt in erster Linie im ContentStream-Block auf. Und ich habe nicht gesagt, dass ich das gleiche Frame jedesmal in JPEG umwandle. Aber wenn alle Clients während der 2-3 Sekunden, in denen die Umwandlung zum ersten und einzigen Mal stattfindet, anfragen, dann müssen sie halt auch alle warten, bis die erste Erstellung vorbei ist. Und dieses Warten ist, wie gesagt, so gewollt. Danach wird bis zum Zwischenspeichern eines anderen Frames aus einem Cache, der die JPEG-Variante enthält, gelesen. Und nochmal: ich habe hier das recht _allgemeine_ Problem, dass bei einer langen Antwortberechnung (egal wie die jetzt im Detail aussieht, ob ContentText oder ContentStream benutzt wird etc.) das Programm _kommentarlos_ abstürzt. Ich bin dankbar für eure Mühen. :thumb: Aber ich bin kein Anfänger, dem man die Grundlagen des Exception-Handlings erklären muss, NUR weil in dem unfertigen Beispiel-Stück vielleicht das eine oder andere Detail noch nicht ganz perfekt aussieht. Das Problem liegt hier an einer anderen Stelle, soweit bin ich schon gekommen. Mein Problem ist, dass ich den detaillierten Aufbau des Indy HTTP-Servers nicht kenne und mir das Programm mangels Fehlermeldung auch keinerlei Hinweis darauf gibt, wo das Problem sein könnte (trotz try...except-Block um die betreffende Stelle). Und sollte es da irgendwo eine Zugriffsverletzung geben, dann weiß ich nicht, wie das Programm auf derartige Exceptions reagiert. Da habe ich schon unterschiedlichste Erfahrungen gemacht, je nach dem, ob solche Exceptions in eingebundenen Laufzeitbibliotheken (statisch oder dynamisch eingebunden) oder im Hauptprogramm, im Main-Thread oder einem Hintergrund-Thread auftreten. Die Bandbreite reicht von Exception mit Fehlermeldung, über Fehlermeldungen mit Programmabsturz, Einfrieren des Programms bis hin zum kommentarlosen verschwinden. Und das alles auch, wenn der entsprechende Teil mit einem try...except-Block geschützt war. Evtl. räumt ja der Indy HTTP-Server irgendwo nach einem Timeout was auf, was noch nicht aufgeräumt werden sollte. Ich werde jetzt mal einen try...except-Block um das ganze Programm machen, in der Hoffnung, irgendwas zu finden. Da es sich allerdings um einen Server handelt und der Fehler beim Senden der Antwort liegt, weiß ich nicht, ob der try...except-Block dann den richtigen Thread "umfasst". |
AW: TIdHTTPServer: Programmabsturz bei langwieriger ResponseInfo-Berechnung
Update: auch ein try...except-Konstrukt, in dem der Exception-Block die Exception-Nachricht ausgibt, um das Haupt-Programm ändert nichts am kommentarlosen Absturz.
|
AW: TIdHTTPServer: Programmabsturz bei langwieriger ResponseInfo-Berechnung
Zitat:
Kann es sein, dass dir irgend etwas durch den Speicher rauscht? Mavarik |
AW: TIdHTTPServer: Programmabsturz bei langwieriger ResponseInfo-Berechnung
Den ExitCode auslesen wäre mal ein Versuch. (vielleicht wird ja irgendwo ein hartes
![]() Oder gibt es sowas unter Linux nicht? (hätte vermutet, daß die sowas auch haben) Raspberry Pi ... hmmmmm Hat der Raspberry Pi auch ein Eventlog im System? Und mit dem Debuggen wird es dort wohl nicht so einfach? Mit etwas "Glück" hast du einen Bug im FPC oder im Raspberry Pi entdeckt und kannst da nicht viel machen. (außer diese Pausen zu verhindern) |
AW: TIdHTTPServer: Programmabsturz bei langwieriger ResponseInfo-Berechnung
@Mavarik:
Ich weiß nicht, was mir durch den Speicher rauschen sollte. Ich erstelle einen MemoryStream in der Variable AResponseInfo.ContentStream, befülle ihn mit Daten (JPEG-Bild) und erwarte dann, dass sich der Indy HTTP-Server um den Rest kümmert. Was ja auch, wenn es schnell genug geht, immer klappt. Viel mehr mache ich nicht. @himitsu: Stimmt, den exit-Code könnte ich mir mal noch anschauen. Hast du eine Idee, wie das möglichst einfach geht? Mit denen habe ich mich bisher noch nicht befasst. Ich schaue mir gerade mal noch die TerminateWaitTime-Eigenschaft der TIdCustomTCPServer-Klasse an. Eventuell hat die Einfluss auf das Verhalten. |
AW: TIdHTTPServer: Programmabsturz bei langwieriger ResponseInfo-Berechnung
Ich habe gerade mal die DoCommandGet-Methode des HTTP-Servers "vereinfacht":
Delphi-Quellcode:
Das reicht, um bei ein paar Client-Verbindungen das Programm zum Absturz zu bringen... leider....:(
procedure THTTPServer.DoCommandGet(AContext: TIdContext;
ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo); begin AResponseInfo.ContentText := 'Test'; Sleep(4000); end; |
AW: TIdHTTPServer: Programmabsturz bei langwieriger ResponseInfo-Berechnung
Was passiert eigentlich, wenn Du die Anwendung im Debugger auf einem PC laufen lässt? Wenn irgendwo eine Timeout-Exception während der 2 Sekunden geworfen wird, wird man das doch wohl merken. Indy ist ja bekannt dafür, Exceptions als Flußkontrolle zu verwenden.
Dein Handler wird in einem Thread aufgerufen, hier einen Try-Except-Block zu bauen, ist sicherlich sinnvoll, wird aber das Verhalten des Hauptthreads nicht beeinflussen. Wäre ja denkbar, das der den Thread abschießt, weil er 2 Sekunden lang ackert (was albern wäre, aber denkbar). Wie weit ist dein Projekt? Kommen andere Komponenten in Betracht? Ich denke an ICS, die sind eventgesteuert, längst nicht so CPU-lastig. Nachteil: Man muss doch einiges an Logik über Events und endliche Automaten selbst lösen.... Hups, ICS wird für FreePascal nicht erhältlich sein (aber vielleicht gehts trotzdem, Source ist ja vorhanden). |
AW: TIdHTTPServer: Programmabsturz bei langwieriger ResponseInfo-Berechnung
Ich werde das mal mit Debugger auf dem PC testen. Und dort sowohl in einem Konsolen- als auch in einem GUI-Programm.
Aber das erst im nächsten Jahr. Für heute ist's genug. Allen einen guten Rutsch! :) |
AW: TIdHTTPServer: Programmabsturz bei langwieriger ResponseInfo-Berechnung
Zitat:
Zitat:
|
AW: TIdHTTPServer: Programmabsturz bei langwieriger ResponseInfo-Berechnung
Habe ebenfalls genau das selbe problem. Unter debian sorgt ein zu lange andauernder response für einen direkten crash ohne Informationen, wobei unter windows ein socketerror #10053 erzeugt wird und das programm normal weiter läuft.
Beides wurde mit Lazarus auf Windows und auch auf Debian getestet. MfG Nachtrag: hab die .exe per wine laufen lassen und diese variante läuft ohne crash auf debian... |
AW: TIdHTTPServer: Programmabsturz bei langwieriger ResponseInfo-Berechnung
Das spricht dann wohl doch für einen Bug im Linux oder Lazarus/FPC/Indy.
Wobei ich jetzt mal den Bug eher in Lazarus/FPC/Indy suchen würde, als im Linux, denn ein Timeout sollte ja nicht so selten sein und in den letzten paar Jahren sollte das schon aufgefallen sein, also vorallem in den anderen Programmiersprachen, die vermutlich häufiger verwendet wurden. |
AW: TIdHTTPServer: Programmabsturz bei langwieriger ResponseInfo-Berechnung
Schon mal beruhigend, dass ich nicht der einzige bin, der mit dem Problem zu kämpfen hat.
Ich versuch's jetzt mal damit: ![]() |
AW: TIdHTTPServer: Programmabsturz bei langwieriger ResponseInfo-Berechnung
evtl wäre fcl-web TFPHttpServer eine Alternative
![]() unter FPC packages/fcl-web/examples/ solltest du auch fündig werden |
AW: TIdHTTPServer: Programmabsturz bei langwieriger ResponseInfo-Berechnung
Ist es möglich ein kleines Beispielprojekt - Client und Server - hier zu posten (Client und Server), um das Problem zu reproduzieren? (meine Umgebung: Lazarus oder Delphi 2009 und neuer, Indy 10.6, Windows 8, oder Ubuntu 12.04).
|
AW: TIdHTTPServer: Programmabsturz bei langwieriger ResponseInfo-Berechnung
Das müsste so reichen:
Delphi-Quellcode:
Als Client habe ich Google Chrome verwendet (mit ein paar Tabs, die den Webserver anfragen). Aber auch jeder andere Browser sollte es tun. Spätestens, wenn du in den Tabs schnell ein paar mal F5 drückst, sollte das Server-Programm abstürzen.
unit http;
{$mode objfpc}{$H+} interface uses Classes, SysUtils, IdHTTPServer, IdCustomHTTPServer, IdContext, IdSocketHandle, IdGlobal; type THTTPServer = class(TIdHTTPServer) private _newPort : Word; public constructor Create(port : Word); procedure InitComponent; override; procedure DoCommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo); override; end; implementation constructor THTTPServer.Create(port: Word); begin _newPort := port; inherited Create; end; procedure THTTPServer.InitComponent; var Binding: TIdSocketHandle; begin inherited; Bindings.Clear; Binding := Bindings.Add; Binding.IP := '0.0.0.0'; Binding.Port := _newPort; Binding.IPVersion := Id_IPv4; end; procedure THTTPServer.DoCommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo); begin AResponseInfo.ContentText := 'Test'; Sleep(4000); end; end. Ich bin mittlerweile bei Synapse. Damit scheint das ganze zu funktionieren (erste Einschätzung). |
Alle Zeitangaben in WEZ +1. Es ist jetzt 11:43 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