Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Netzwerke (https://www.delphipraxis.net/14-netzwerke/)
-   -   Webserveranwendung: EXE ruft ISAPI (https://www.delphipraxis.net/181528-webserveranwendung-exe-ruft-isapi.html)

Delbor 22. Aug 2014 10:34

Webserveranwendung: EXE ruft ISAPI
 
Hi zusammen

Der Titel ist vielleicht etwas...äääh...gewöhnungsbedürftig, aber mir ist nichts besseres eingefallen.
Der Hintergrund:
In einer Projektgroup befinden sich 2 Projekte: Eine mit Webbroker erstellte ISAPI-DLL und eine EXE.
Aufgabe der EXE ist es, die Anfrage entgegenzunehmen und einer existierenden Session zuzuweisen, bzw. eine solche im Falle einner Erstanfrage neu zu erstellen. Anschliessend soll die Anfrage an die DLL, bzw. deren Webmodul weitergeleitet werden.

Anfänglich hab ich das mit IdHttpServer versucht. Das klappt insofern ganz 'gut', als dass ein in der IdHTTPServer1CommandGet-Methode gesetzter Haltepunkt angesprungen wird, wenn im Browser die URL eingegeben wird. Aber jetzt mal erst der Code:

Delphi-Quellcode:
{$R *.dfm}

procedure TDelborMainServer.FormCreate(Sender: TObject);
  var AClass: TComponentClass;
begin
//  FHTMLDir := ExtractFilePath(Application.ExeName) + 'HTML';
  FIdHTTPWebBrokerBridge := TIdHTTPWebBrokerBridge.Create;
  FIdHTTPWebBrokerBridge.AutoStartSession := True;
  FIdHTTPWebBrokerBridge.ServerSoftware := 'DelborMainServer';
  FIdHTTPWebBrokerBridge.OnConnect := OnIdHTTPWebBrokerBridgeConnect;
  FIdHTTPWebBrokerBridge.OnCommandOther := OnIdHTTPWebBrokerBridgeComandother;
  FIdHTTPWebBrokerBridge.DefaultPort := 8000;
  FIdHTTPWebBrokerBridge.Active := True;

end;

procedure TDelborMainServer.IdHTTPServer1CommandGet(AContext: TIdContext;
  ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
  var LFileName, LPathname: String; OK: Boolean; ECB: TEXTENSION_CONTROL_BLOCK;
begin
  OK := DelborFotoGalery(AContext,ARequestInfo, AResponseInfo);
end;

procedure TDelborMainServer.OnIdHTTPWebBrokerBridgeComandother(AThread: TIdContext;  //<===
   ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo);
begin
  beep;
end;

procedure TDelborMainServer.OnIdHTTPWebBrokerBridgeConnect(AContext: TIdContext);    //<===
begin
  beep;
end;

function DelborFotoGalery(AContext: TIdContext;
  ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo):Boolean;
  var Handle: THandle; DelborFotoGalery: TDelborFotoGalery;
begin
  Handle:=LoadLibrary(PChar(ExtractFilePath(ParamStr(0))+'DelborProject1.dll'));
  if Handle <> 0 then begin
    @DelborFotoGalery := GetProcAddress(Handle, 'HttpExtensionProc');                     // ): DWORD
    if @DelborFotoGalery <> nil then begin
      result := DelborFotoGalery(AContext,ARequestInfo, AResponseInfo);

    end;
  end;
end;
Nachdem die ersten Versuche sich nur auf die Verwendung des IdHTTPServers beschränkten, diese aber immer AVs lieferten(fehlende Servervariablen) habe ich TIdWebbrokerBridge eingebaut - eigentlich schon vom Klassennamen her einleuchtend.
Der Stand der Dinge ist allerdings: auch hier gibts eine AV. Von zwei gesetzten Haltepunkten (durch Pfeile markiert)wird vor Auslösen der AV gerademal der untere (OnIdHTTPWebBrokerBridgeConnect) ausgelöst.

Was mache ich falsch? Alles, was für mich schon fast sicher ist: Ich brauche die DLL gar nicht so aufzurufen, wie ich das in DelborFotoGalery tue...

Auch die DLL ist im Grunde noch nicht fertig entwickelt; das Webmodul dieser Webbroker-Anwendung wird noch durch ein normales Datenmodul mit Webdispatcher ersetzt. Mein Konzept sieht vor, dass die gesamte Anwendung letzten Endes auf einer(?) Datenbank basiert.


Und zum Schluss vielleicht noch die Info, die wohl an den Anfang gehört hätte: die Anwendung soll auf einem Windows-Server laufen. Vielen Dank für eure - hoffentlich zahlreichen - Antworten! Auch über Sinn und Unsinn einer EXE (die ja nicht zwingend eine CGI-Anwendung sein muss(??), lasse ich gerne mit mir reden.

Gruss
Delbor

mjustin 22. Aug 2014 11:08

AW: Webserveranwendung: EXE ruft ISAPI
 
Delphi-Quellcode:
    @DelborFotoGalery := GetProcAddress(Handle, 'HttpExtensionProc');                     // ): DWORD
    if @DelborFotoGalery <> nil then begin
      result := DelborFotoGalery(AContext,ARequestInfo, AResponseInfo);

    end;
Die Argumente der ISAPI HttpExtensionProc und die Indy Parameter (AContext,ARequestInfo, AResponseInfo) sind völlig inkompatibel. Das wird so nie funktionieren. Indy HTTP Request- und Responseobjekte lassen sich nicht einfach in eine ISAPI DLL "einpflanzen" ;)

Delbor 22. Aug 2014 11:28

AW: Webserveranwendung: EXE ruft ISAPI
 
Hi zusammmen

Die Grundfrage (wieso die verd.. AV's??:evil:) hat sich in Luft aufgelöst, die Seite wird mir im Browser angezeigt:
Delphi-Quellcode:
procedure TDelborMainServer.OnIdHTTPWebBrokerBridgeConnect(AContext: TIdContext);
begin
  FIdHTTPWebBrokerBridge.RegisterWebModuleClass(WebModuleClass);
end;
Laut Kommmentar existiert diese Prozedure nur noch wegen der Abwärtskompatibilität:
Zitat:

// FWebModuleClass, RegisterWebModuleClass supported for backward compatability
// Instead set WebModuleClass using: WebReq.WebRequestHandler.WebModuleClass := TWebModule1;
Ich hab allerdings keine Möglichkeit gefunden, den zuständigen WebRequestHandler anzusprechen. Zugegeben, ich hab auch nicht lange danach gesucht...

Zitat:

Vielen Dank für eure - hoffentlich zahlreichen - Antworten! Auch über Sinn und Unsinn einer EXE (die ja nicht zwingend eine CGI-Anwendung sein muss(??), lasse ich gerne mit mir reden.
Das gilt eigentlich immer noch. So, wie ich die verschiedenen Beispielprogramme verstanden habe, sollte eine Webbroker-Isapi-Anwendung als eigenständige DLL in den Internet InformationServices eingebunden werden können, was ich aber nie geschafft habe.
Das Einbinden als Iapi-Extension in den IIS alleine hätte allerdings nicht viel gebracht - es wären einfach nur die Anfrage bearbeitet worden.
Nach meinem bisherigen Verständnis können in den IIS allerdings auch Isapi-Filter eingebunden werden. Aber genau da hörte bisher mein Verständnis auf, da ich so zwar 2 Isapi-Dlls in den IIS eingebunden hätte - aber eben ohne, dass die eine von der anderen etwas weiss.
Die einzige 'Verbindung' wäre wohl eine gemeinsam genutzte DB gewesen.


Gruss
Delbor

Delbor 22. Aug 2014 11:46

AW: Webserveranwendung: EXE ruft ISAPI
 
Hi mjustin

Vielen Dank für deine Antwort!
Zitat:

Die Argumente der ISAPI HttpExtensionProc und die Indy Parameter (AContext,ARequestInfo, AResponseInfo) sind völlig inkompatibel. Das wird so nie funktionieren. Indy HTTP Request- und Responseobjekte lassen sich nicht einfach in eine ISAPI DLL "einpflanzen"
Das war auch der Grund, weshalb ich mich in den letzten Tagen dumm und dämlich gesucht habe. IdHTTPWebBrokerBridge.OnConnect hat ja nur einen Parameter(AContext). Wie sollte ich da die erwarteten Werte an die DLL weitergeben?

Ich gehe mal davon aus, dass eine Isapi-Dll, in den IIS eingebunden, von diesen die Werte für den ECB erhält. Aber schon das alein wirft chon wieder eine Menge Fragen auf....

Gruss
Delbor

mjustin 22. Aug 2014 12:15

AW: Webserveranwendung: EXE ruft ISAPI
 
Zitat:

Zitat von Delbor (Beitrag 1269552)
Ich gehe mal davon aus, dass eine Isapi-Dll, in den IIS eingebunden, von diesen die Werte für den ECB erhält. Aber schon das alein wirft chon wieder eine Menge Fragen auf....

Soll das Ergebnis ein einfacher HTTP Proxy werden, der in einer Delphi EXE mit Indy läuft und die Anfragen an den IIS durchleitet?

Indy enthält dann mit der Klasse TIdHTTPProxyServer vermutlich schon (fast) alles, was dafür benötigt wird.

Delbor 22. Aug 2014 12:39

AW: Webserveranwendung: EXE ruft ISAPI
 
H mjustin

Nein, das solll kein Proxi-Server werdsen, zumindest, soweit ich diesen Artikel verstanden habe. Das Ding soll von einem Hostserver schlicht meine Webseiten ausgeben. Dazu sollen jeweils Sessions benutzt werden. Abgeschlossene Sessions sollen in einer DB gespeichert werden. In einem weiteren Ausbau soll auch eine Cookieverwaltung dazukommen.

Die IIS habe ich auf meinem Kistchen nur, weil die Anwendung später auf dem Hostserver unter den IIS laufen soll. Die reine, mit dem Delphi-Experten erstellte Webbroker-Isapi (ohne Exe), habe ich auf meinem Computer unter den IIS nie zum laufen gebracht. Da nach meinen Infos unter den IIS aber auch Executables laufen, hab ich diese Variante gewählt wobei mir dies aber auch im Hinblick auf eine Session- und Cookieverwaltung wichtig schien.

Gruss
Delbor

Delbor 22. Aug 2014 18:27

AW: Webserveranwendung: EXE ruft ISAPI
 
Hi zusammen

Gerade eben hab ich mir selbst ein Bein gestellt - ich hab das getan, was Embarcadero hier empfiehlt und das Webmodul durch ein normales Datenmodul ausgetauscht. Seither geht gar nichts mehr...

In der Projekt-Unit habe ich folgendes geändert:
Delphi-Quellcode:
  if WebRequestHandler <> nil then
    WebRequestHandler.WebModuleClass := Webmodul1;
in
Delphi-Quellcode:
  if WebRequestHandler <> nil then
    WebRequestHandler.WebModuleClass := TDelborWebDataModul;
Dadurch startet zwar das Projekt. Aber wenn ich im Browser die URL eingebe, wird eine AV geworfen, wenn der WebRequestHandler das Webmodul aktivieren will, aber keins findet.
So, wie's aussieht, ist die Empfehlung Embarcaderos nur dann gültig, wenn die mit Webbroker erstellt ISAPI-Dll als separate Anwendung im IIS eingebunden wird.
Ich kann jetzt nur hoffen, dass das Webmodul DBExpress-Komponenten akzeptiert...

Oder weiss jemand, wie man ein Datenmodul endgültig zum Webmodul macht?

Gruss
Delbor

Olli73 23. Aug 2014 12:44

AW: Webserveranwendung: EXE ruft ISAPI
 
Zitat:

Zitat von Delbor (Beitrag 1269548)
Aber genau da hörte bisher mein Verständnis auf, da ich so zwar 2 Isapi-Dlls in den IIS eingebunden hätte - aber eben ohne, dass die eine von der anderen etwas weiss.
Die einzige 'Verbindung' wäre wohl eine gemeinsam genutzte DB gewesen.

Ja, in diesem Fall ist die "Verbindung" die Datenbank oder man nutzt HTTP-Requests untereinander.

ABER: Ich habe immer noch nicht verstanden, warum du 2 getrennte ISAPIs/EXEs benötigst und nicht gleich alles in eine ISAPI packst?!

Delbor 23. Aug 2014 18:27

AW: Webserveranwendung: EXE ruft ISAPI
 
Hi Olly73


Zitat:

Zitat von Olli73 (Beitrag 1269647)
Zitat:

Zitat von Delbor (Beitrag 1269548)
Aber genau da hörte bisher mein Verständnis auf, da ich so zwar 2 Isapi-Dlls in den IIS eingebunden hätte - aber eben ohne, dass die eine von der anderen etwas weiss.
Die einzige 'Verbindung' wäre wohl eine gemeinsam genutzte DB gewesen.

Ja, in diesem Fall ist die "Verbindung" die Datenbank oder man nutzt HTTP-Requests untereinander.

Ja, sowas habe ich mir auch gedacht, konnte mir die Sache aber nicht so richtig vorstellen. Vielleicht löst sich der Nebel aber langsam. Zum einen laufen Filter-Dlls ja nicht nur pro Anfrage, sondern permanent (so lange die IIS laufen), zum andern habe ich hier ein Beispiel, wie eine Isapi-Extension eine andere aufruft.
Diese beiden Beispiele hätte ich mir gerne per schrittweisem durchsteppen etwas näher angeschaut, doch wie geschrieben, konnte ich die IIS nie dazu übereden, mir die Seiten aus meinem eigenen Webbroker-Projekt auch anzuzeigen.
Zusammen mit der EXE klappt das aber, und gesetzte Haltepunkte werden auch angesprungen.

Zitat:

Zitat von Olli73 (Beitrag 1269647)
ABER: Ich habe immer noch nicht verstanden, warum du 2 getrennte ISAPIs/EXEs benötigst und nicht gleich alles in eine ISAPI packst?!

Na ja. Auslöser war das Eliza-Beispiel der Indys mit dem HTTP-Server, das mir auch angezeigt wurde, nachdem ich die IIS zwischenzeitlich rausgeworfen habe - wobei ich jetzt ja anstelle des IdHTTPServers die Webbrokerbridge verwende. Wie oben schon gesagt: mit der Exe bin ich unabhängig von den IIS.
Ein weiterer Grund war aber eben auch: Mit einer Filter-Isapi und einer Isapi-Extension hätte ich offensichtlich/scheinbar zwei unabhängige Projekte gehabt und habe mir zumindest vorerst absolut nicht vorstellen können, wie die beiden zusammenarbeiten,
Dazu kommt - eine EXE gibt mir die Möglichkeit, Programmlogik an einem Ort zu verarbeiten (Sessions, Cookies), wie ich das sonst auch mache.

Etwas anderes sollte vielleicht auch nicht ausser acht gelassen werden: Microsoft selbst beschreibt die ISAPI-Filter als veraltet; man solle an ihrer Stelle doch eher Module definieren. Und wenn ich richtig gelesen habe, können die auch executables sein.

Zitat:

...warum du 2 getrennte ISAPIs/EXEs benötigst
Es sind ja nicht zwei gtrennte(also je eine exe und eine Isapi) sondern je eine. Aber was du ansprichst, ist wohl eher die Projektgruppe mit den 2 separaten Projekten. Nun ja, die DL ist für sich ein eigenes Projekt, deshalb meine Überlegung mit der Projektgruppe.
Nachdem ich mir wie oben beschreiben, das ganze durch einfügen des Datemoduls zerschossen habe und eine neue DLL auch nicht viel änderte, werde ich wohl das ganze neu aufsetzen...


Das hier hätte ich beinahe überlesen:
Zitat:

...alles in eine ISAPI packst?!
Eine Webbrokeranwendung (Isapi) wird genau für jeweils einen Request ausgeführt, beantwortet diesen und wird dann beendet. Man kann das Webmodul zwar auch deaktivieren - es ist dann einfach 'geparkt' und kann bei Bedarf wieder aktiviert werden. Aber sie eignet sich nicht für ein Session-oder Cookie-Managment.
Und eine Session in die DB zu schreiben, solange sie nicht beendet wird, denke ich, sollte im Interesse der Performance wohl besser unterbleiben. Ich denke, das ergibt viel zu viele Datenbankzugriffe, vor allem, wenn ich auch anonyme Zugriffe über Sessions ablaufen.

Gruss
Delbor

Olli73 23. Aug 2014 19:35

AW: Webserveranwendung: EXE ruft ISAPI
 
Zitat:

Zitat von Delbor (Beitrag 1269662)
Das hier hätte ich beinahe überlesen:
Zitat:

...alles in eine ISAPI packst?!

Und dabei war das (zumindest für mich) das Wichtigste. ;)

Zitat:

Zitat von Delbor (Beitrag 1269662)
Eine Webbrokeranwendung (Isapi) wird genau für jeweils einen Request ausgeführt, beantwortet diesen und wird dann beendet. Man kann das Webmodul zwar auch deaktivieren - es ist dann einfach 'geparkt' und kann bei Bedarf wieder aktiviert werden. Aber sie eignet sich nicht für ein Session-oder Cookie-Managment.

Nö. Wenn die ISAPI einmal geladen ist, verbleibt sie normalerweise im Speicher; es sei denn du setzt absichtlich den Registryeintrag "CacheExtensions" auf 0:

http://msdn.microsoft.com/de-de/libr...(v=vs.71).aspx

Aber davon wird explizit abgeraten, außer zum Debuggen.

Mit UniGui (unigui.com) kann man auch eine ISAPI-DLL erstellen und die ist "statefull" (hält also Daten für jede aktive Session im Speicher)!

Zitat:

Zitat von Delbor (Beitrag 1269662)
Und eine Session in die DB zu schreiben, solange sie nicht beendet wird, denke ich, sollte im Interesse der Performance wohl besser unterbleiben. Ich denke, das ergibt viel zu viele Datenbankzugriffe, vor allem, wenn ich auch anonyme Zugriffe über Sessions ablaufen.

Du kannst die Daten auch im Speicher behalten, musst aber ein paar Dinge beachten:

Globale Variablen sind grundsätzlich Pfui, da multithreaded, und müssen durch critical sections abgesichert werden. Aber als globale Variable brauchst du eigentlich nur eine SessionList (Liste, Array, Hashmap, ...). Diese ist durch eine CriticalSection abzusichern.

In die einzelnen Sessions (Session-Objekte) kannst du dann deine Daten, (Daten-)Objekte, Datenmodule etc. packen. Dabei muss ein Objekt oder Datenmodul für jede Session neu erstellt werden und es dürfen auch dort keine globalen Variablen, sondern nur Felder/Properties verwendet werden.

Gruß
Olli

Delbor 23. Aug 2014 22:43

AW: Webserveranwendung: EXE ruft ISAPI
 
Hi Olli73

Zitat:

Nö. Wenn die ISAPI einmal geladen ist, verbleibt sie normalerweise im Speicher; es sei denn du setzt absichtlich den Registryeintrag "CacheExtensions" auf 0:
Ups! Ich habe da zwar - von Embaecadero - andere Infos:
Jede Anforderung wird in einem eigenen Thread behandelt.

Ich habe jetzt doch eine Weile gesucht und bislang nur oben verlinkte Seite gefunden, die meine Aussage unterstützt.
Aber vielleicht sollten ich doch mal ein Missverständnis ausräumen: Ich wollte nicht behaupten, dass die dll aus dem Speicher entfernt wird:
Zitat:

Eine Webbrokeranwendung (Isapi) wird genau für jeweils einen Request ausgeführt, beantwortet diesen und wird dann beendet
Das stimmt so wirklich nicht. Es geht darum, dass ISAPI für jeden Request einen Thread startet - und dieser wird beendet, wenn die Anfrage fertig bearbeitet ist.

Zitat:

Globale Variablen sind grundsätzlich Pfui, da multithreaded, und müssen durch critical sections abgesichert werden. Aber als globale Variable brauchst du eigentlich nur eine SessionList (Liste, Array, Hashmap, ...). Diese ist durch eine CriticalSection abzusichern.

In die einzelnen Sessions (Session-Objekte) kannst du dann deine Daten, (Daten-)Objekte, Datenmodule etc. packen. Dabei muss ein Objekt oder Datenmodul für jede Session neu erstellt werden und es dürfen auch dort keine globalen Variablen, sondern nur Felder/Properties verwendet werden.
Es ist jetzt schon eine Weile her, dass ich selbst mit Threads gearbeitet habe. Was mir davon so aus dem Stegreif noch geblieben ist: ein Thread eines (Desktop-)Programmes kann nicht auf ein VCL-Objekt zugreifen, ausser durch Synchronisierung. Das aber kann den Performancevorteil eines Threads zunichte machen. Ein (Session-)Objekt müsste also dem Thread vor/beim Start übergeben werden.

Ich hab jetzt gerade noch etwas in den Sourcen gestöbert. Bislang hatte ich geglaubt, durch die Verwendung der Isapi-Units, die Delphi mitliefert, würde ein Thread automatisch gestarte, konnte bislang aber keinen Code finden, der sowas macht...:?:
Solche Aussaggen veranlassten mich zu dieser Annahme:
Na ja, mir schwant, dass ein ausführliches Studium zuverlässiger als die Embarcadero-Help ist...

Andrerseits - was ist eigentlich genau die Aufgabe der IIS? Sorgen die für den ThreadStart? Da sind leider keine Sourcen dabei, wo man selbst nachsehen könnte, was genau abläuft.

Das mit den globalen Variablen war mir bekannt: Das war für mich einer, wenn nicht gar der Hauptgrund, für eine EXE.


Gruss
Delbor

Olli73 24. Aug 2014 01:51

AW: Webserveranwendung: EXE ruft ISAPI
 
Zitat:

Zitat von Delbor (Beitrag 1269671)
Zitat:

Nö. Wenn die ISAPI einmal geladen ist, verbleibt sie normalerweise im Speicher; es sei denn du setzt absichtlich den Registryeintrag "CacheExtensions" auf 0:
Ups! Ich habe da zwar - von Embaecadero - andere Infos:
Jede Anforderung wird in einem eigenen Thread behandelt.

Ich habe jetzt doch eine Weile gesucht und bislang nur oben verlinkte Seite gefunden, die meine Aussage unterstützt.

Da steht "eigner Thread". Und das ist korrekt. Aber es ist immer nur ein Prozess und daher ist es auch ein gemeinsamer Speicherbereich. Man muss lediglich mit den Speicherzugriffen aufpassen (kritische Abschnitte verwenden).

Zitat:

Aber vielleicht sollten ich doch mal ein Missverständnis ausräumen: Ich wollte nicht behaupten, dass die dll aus dem Speicher entfernt wird:
Zitat:

Eine Webbrokeranwendung (Isapi) wird genau für jeweils einen Request ausgeführt, beantwortet diesen und wird dann beendet
Das stimmt so wirklich nicht. Es geht darum, dass ISAPI für jeden Request einen Thread startet - und dieser wird beendet, wenn die Anfrage fertig bearbeitet ist.
Ich bin mir jetzt nicht sicher, wie es bei ISAPI ist, aber wenn du eine "Webbroker-EXE" erstellst, gibt es einen Thread-Pool, d.h. es gibt auch dort mehrere Threads, die werden aber nicht sofort beendet, sondern werden der nächsten Anfrage (eines beliebigen Clients!) erneut zugeteilt. Macht daher letztlich keinen Unterschied, da der Client/Browser bei jeder Anfrage in einem anderen Thread landen kann. Wenn du also z.B. im Webmodul ein Dataset offen lässt, greift später ggf. ein ganz anderer Client auf dieses zu.

Zitat:

Zitat:

Globale Variablen sind grundsätzlich Pfui, da multithreaded, und müssen durch critical sections abgesichert werden. Aber als globale Variable brauchst du eigentlich nur eine SessionList (Liste, Array, Hashmap, ...). Diese ist durch eine CriticalSection abzusichern.
In die einzelnen Sessions (Session-Objekte) kannst du dann deine Daten, (Daten-)Objekte, Datenmodule etc. packen. Dabei muss ein Objekt oder Datenmodul für jede Session neu erstellt werden und es dürfen auch dort keine globalen Variablen, sondern nur Felder/Properties verwendet werden.
Es ist jetzt schon eine Weile her, dass ich selbst mit Threads gearbeitet habe. Was mir davon so aus dem Stegreif noch geblieben ist: ein Thread eines (Desktop-)Programmes kann nicht auf ein VCL-Objekt zugreifen, ausser durch Synchronisierung. Das aber kann den Performancevorteil eines Threads zunichte machen. Ein (Session-)Objekt müsste also dem Thread vor/beim Start übergeben werden.
Da du hier eh nicht mit visuellen Komponenten arbeiten kannst, macht es das schon mal etwas einfacher. Das Synchronize wird meist benutzt, um die Oberfläche/Forms anzupassen, das entfällt ja hier. Ansonsten benötigst du eigentlich nur eine globale Variable (oder besser eine Function oder noch besser eine Klassenmethode) mit einer "SessionList" über die du an ein bestimmtes Datenobjekt oder eine bestimmte Instanz eines Datenmoduls herankommst. Die Sessionlist und ggf. das Datenobjekt/Datenmodul musst du mit einer CriticalSection absichern, damit eben sicher gestellt ist, dass sich die Threads nicht in die Quere kommen. Wenn du mit Datenbanken arbeitest, musst du auch pro Session eine Datenbankverbindung aufbauen, wenn deine Datenbank-Connection-Komponente nicht explizit als Threadsafe gekennzeichnet ist.

Wenn also eine neue Anfrage in deinem Webmodul landet, musst du zuerst über die sessionList (unter Beachtung der CriticalSection!) aufrufen und dir darüber deine Daten für diese Session besorgen, bzw. einen neuen Eintrag in der Liste anlegen und eine neue Instanz der Datenstruktur erstellen, wenn es eine neue Session ist (der Client benötigt dann auch diese Session-ID und muss die beim nächsten mal mitsenden).

Zitat:

Ich hab jetzt gerade noch etwas in den Sourcen gestöbert. Bislang hatte ich geglaubt, durch die Verwendung der Isapi-Units, die Delphi mitliefert, würde ein Thread automatisch gestarte, konnte bislang aber keinen Code finden, der sowas macht...:?:
Das passiert AFAIK irgendwo (tief) in den Indy-Komponenten.

Stimmt ja auch, ABER (s.o.): Thread != Prozess

Zitat:

Na ja, mir schwant, dass ein ausführliches Studium zuverlässiger als die Embarcadero-Help ist...
Waren scheinbar nur die Begrifflichkeiten. Aber sowas passiert mir in anderen Fällen auch ständig.

Zitat:

Das mit den globalen Variablen war mir bekannt: Das war für mich einer, wenn nicht gar der Hauptgrund, für eine EXE.
Da hilft dir eine "Webbroker-EXE" aber auch nicht weiter, da es dort das Gleiche mit den Threads ist (s.o.). Und ein CGI ist sogar ein eigener Prozess, also noch weniger für dich geeignet.


Gruß,
Olli

Delbor 24. Aug 2014 13:20

AW: Webserveranwendung: EXE ruft ISAPI
 
Hi Olli
Zitat:

Da hilft dir eine "Webbroker-EXE" aber auch nicht weiter, da es dort das Gleiche mit den Threads ist (s.o.). Und ein CGI ist sogar ein eigener Prozess, also noch weniger für dich geeignet.
Die Webbroker-Anwendung befindet sich nach wie vor in einer eigenen Datei - eben der DLL, wie sie von Delphi erstellt wird. Die exe ist eine normale Form, zur Zeit ohne jegliche visuellen Komponenten.
Die wohl wichtigste Klasse ist hier TIdWebbrokerBriodge, über die die Webbroker-Anwendung, bzw die erzeugte dll, aufgerufen wird - aber deswegen wohl die Apostrophe.

Zitat:

Da du hier eh nicht mit visuellen Komponenten arbeiten kannst, macht es das schon mal etwas einfacher.
Soweit ich dies in Erinnnerung habe, ist ein Synchronize immer dann nötig, wenn auf eine Klasse zugegriffen werden soll, die im Hauptthread abläuft, ob sie nun visuell ist oder nicht.

Mein vorheriges Projekt war eine Bilderdatenbank. Für die hatte ich eine Klasse erstellt, die die Daten aus einer SQL-Anfrage übernimmt (ein DS pro Instanz). Dasselbe gedachte ich eigentlich hier zu realisieren. Kurz skizziert:
  • eine Klasse wird von TThread abgeleitet(TCustomThrread?)
  • Die Klasse erhält(Stream-)Felder, für jedes DB-Feld eines. Ein 'Speicherzugriff' ist somit nach dem Start nur innerhalb des Threads nötig.

Aber bevor ich das realisiere, muss ich mir über den genauen Ablauf klar werden - wenn nämlich der Isapi-Tread entegegen meiner bisherigeen Erkenntnis trotzdem automatisch gestartet wird, wird es... na ja, sagen wir mal: interessant.

Gruss
Delbor

Sir Rufo 24. Aug 2014 13:31

AW: Webserveranwendung: EXE ruft ISAPI
 
@Delbor

Ein Synchronize ist eigentlich nur für die GUI notwendig. Alles andere kann man über die Synchro-Objekte so steuern, dass immer nur ein Thread-Kontext gleichzeitig zugreift (zugreifen kann)

Zudem wiegt man sich mit dem Synchronize auch in eine falsche Sicherheit.

Also erstelle die gemeinsam genutzten Klassen threadsafe und benutze die einfach (ohne Synchronize)

Delbor 24. Aug 2014 14:36

AW: Webserveranwendung: EXE ruft ISAPI
 
Hi Sir Rufo

Laut Help gilt das mit der falschen Sicherheit für CriticalSections ebenso wie für Synchronize. Ich denke mir, eine CriticalSection beim Create des Threads und bestücken seiner Felder sollte ausreichen, da der Thread zu seiner Laufzeit nur auf seinen eigenen Speicherbereich zugreift.

Gruss
Delbor

Sir Rufo 24. Aug 2014 14:55

AW: Webserveranwendung: EXE ruft ISAPI
 
Wenn du bei einem Thread von aussen die Felder bestücken kannst, dann hast du den Thread absolut falsch aufgebaut.

Von aussen sollte man mit dem Thread ausschliesslich über Methoden und Eigenschaften kommunizieren können. Diese kann man zuverlässig mit den SynchroObjekten threadsafe gestalten.

Eigentlich ist es ganz einfach: Man sorgt dafür, dass es nicht möglich ist auf einen Speicherbereich mit unterschiedlichen Threads gleichzeitig zuzugreifen.

Wenn man das mit Synchronize versucht (im MainThreadKontext), dann muss man höllisch aufpassen, dass jeder Zugriff darauf per Synchronize erfolgt (hört sich irgendwie schon komisch an).

Zitat:

Zitat von Delbor (Beitrag 1269709)
... eine CriticalSection beim Create des Threads und bestücken seiner Felder sollte ausreichen, da der Thread zu seiner Laufzeit nur auf seinen eigenen Speicherbereich zugreift.

hmmm, wenn du innerhalb des Konstruktors die Felder setzt und auf die von aussen (über Eigenschaften, Methoden) nicht zugegriffen wird, dann brauchst du gar keine CriticalSection.

Olli73 24. Aug 2014 15:11

AW: Webserveranwendung: EXE ruft ISAPI
 
Zitat:

Zitat von Delbor (Beitrag 1269709)
Hi Sir Rufo
Ich denke mir, eine CriticalSection beim Create des Threads und bestücken seiner Felder sollte ausreichen, da der Thread zu seiner Laufzeit nur auf seinen eigenen Speicherbereich zugreift.

2 "Denkfehler" in 1 Satz: ;)

(1) Der Thread hat keinen "eigenen Speicherbereich", es gibt nur 1 gemeinsamen Speicherbereich für den gesamten Prozess; das "getrennt" zu halten bzw. gegenseitige Zugriffe in sicherer Weise zu erlauben ist deine Aufgabe.

(2) Die CriticalSection musst du vor jedem Zugriff betreten und anschließend wieder verlassen und zwar aus dem Thread heraus, der auf die Daten zugreifen will; so wird der gleichzeitige Zugriff von 2 Threads aus verhindert. Das ist aber auch der Grund, warum die CriticalSection dir bei GUI-Komponenten nichts bringt: Da die VCL/WinApi deine CriticalSection nicht kennt, wird sie sie auch nicht beachten.

Sir Rufo 24. Aug 2014 15:21

AW: Webserveranwendung: EXE ruft ISAPI
 
Zitat:

Zitat von Olli73 (Beitrag 1269712)
Das ist aber auch der Grund, warum die CriticalSection dir bei GUI-Komponenten nichts bringt: Da die VCL/WinApi deine CriticalSection nicht kennt, wird sie sie auch nicht beachten.

Ähm, nee, das ist anders.

Die VCL ist nicht threadsafe ausgelegt und darum darf man auf die VCL-Teile eben nur synchronisiert zugreifen. Der andere Weg ist doch simpel über Getter und Setter zu erreichen, die dann die CriticalSection betreten und wieder verlassen.

Irgendwie beschleicht mich das Gefühl, dass hier von einer globalen CriticalSection gesprochen wird ... ist dem so, oder kommt nur meine Paranoia wieder hoch? :)

Olli73 24. Aug 2014 15:38

AW: Webserveranwendung: EXE ruft ISAPI
 
Zitat:

Zitat von Sir Rufo (Beitrag 1269713)
Zitat:

Zitat von Olli73 (Beitrag 1269712)
Das ist aber auch der Grund, warum die CriticalSection dir bei GUI-Komponenten nichts bringt: Da die VCL/WinApi deine CriticalSection nicht kennt, wird sie sie auch nicht beachten.

Ähm, nee, das ist anders.

Die VCL ist nicht threadsafe ausgelegt und darum darf man auf die VCL-Teile eben nur synchronisiert zugreifen. Der andere Weg ist doch simpel über Getter und Setter zu erreichen, die dann die CriticalSection betreten und wieder verlassen.

Ich habe mich da falsch ausgedrückt; ich meinte eigentlich, das es nicht ausreicht, wenn man jetzt auf den Gedanken kommt einfach selbst immer vor einem Zugriff auf ein VCL-Objekt eine CriticalSection zu betreten. Dann wären zwar die eigenen Zugriffe abgesichert, aber nicht die von der VCL/WinAPI...

Zitat:

Irgendwie beschleicht mich das Gefühl, dass hier von einer globalen CriticalSection gesprochen wird ... ist dem so, oder kommt nur meine Paranoia wieder hoch? :)
Also ich spreche die ganze Zeit nur von einer CriticalSection für eine "SessionList" und ggf. je einer CriticalSection pro "Datenobjekt" (welches in dieser Liste verwaltet wird).

Delbor 24. Aug 2014 16:32

AW: Webserveranwendung: EXE ruft ISAPI
 
Hi zusammen

Ich sehe das bisher so vor :
  1. Die Anfrage kommt rein
  2. Sie wird analysirtte
  3. In der Sessionlist wird geprüft, ob für den Anfrager bereits eine Session Existiert. Ein Datenbankzugriff ist hier nicht nötig, da die DB nur Infos über geschlossene Sessions enthält.
  4. Gibt es keine Session, wird eine neu erstellt - auchh für anonyme User.
  5. Gemäss der Anfrage wird die DB abgefragt. Die enthält u.a. Felder zum
    • URL
    • HTML
    • CSS
    • MenueCSS
  6. Das Resultat wird an Klassenfelder verteilt, die den Tabellenfeldern entsprechen. Diese Klasse Marke Eigenbau ist ein Proberty einer eigenen Threadklasse
  7. Der Thread wird erstellt und die Isapi gestartet

Zitat:

(2) Die CriticalSection musst du vor jedem Zugriff betreten und anschließend wieder verlassen und zwar aus dem Thread heraus, der auf die Daten zugreifen will;
Wenn eine CriticalSection 'nur' den Datenbereich sperrt, auf die der Thread, der sie einsetzt, zugreifen will, brauchts wirklich keine, immer vorausgesetzt, die Parameter Requestinfo, ResponseInfo und Co. haben keine Verbindung nach aussen und sind eigene Instanzen der jeweilgen Klassen.

Gruss
Delbor

Olli73 24. Aug 2014 17:51

AW: Webserveranwendung: EXE ruft ISAPI
 
Zitat:

Zitat von Delbor (Beitrag 1269718)
Wenn eine CriticalSection 'nur' den Datenbereich sperrt, auf die der Thread, der sie einsetzt, zugreifen will, brauchts wirklich keine, immer vorausgesetzt, die Parameter Requestinfo, ResponseInfo und Co. haben keine Verbindung nach aussen und sind eigene Instanzen der jeweilgen Klassen.

Wenn sichergestellt ist, dass es keine Überschneidungen bei den Zugriffen durch die Threads gibt, braucht man natürlich nix abzusichern.


Aber dass bei Verwendung von TIdHttpServer, TIdHttpWebBrokerBridge etc. bereits "automatisch" mehrere Threads existieren, ist dir bewusst?

Gruß,
Olli

Delbor 25. Aug 2014 12:07

AW: Webserveranwendung: EXE ruft ISAPI
 
Hi Olli

Heute morgen ist es mir wie Schuppen von den Augen gefallen: Der Parameter IdContext ist vom Typ AThread, und der wird bei den Sockets das erste mal deklariert und dann an alle Nachkommen weitergereicht, damit diese eine CriticalSection für den existierenden Thread einrichten können.
Pro Anfrage existiert also genau ein Thread, und zwar genau so lange, bis dieser die Antwort über die Sockets ausgegeben hat. Natürlich kann es trotzdem zu Überschneidungen kommen; nämlich dann, wenn kurz nacheinander mehrere Threads gestartet werden, aber einer (oder mehrere) schneller arbeitet als andere.
Wenn dann die Anfrage beim Webmodul angekommen ist, ist ein Speicherzugriff ohne Criticalsection nicht mehr möglich, bzw. ein Hazzardspiel. Und das bedeutet, dass mein Konzept, dem Thread alle Infos beim Start mitzugeben, hier nicht möglich ist.

Zitat:

Aber dass bei Verwendung von TIdHttpServer, TIdHttpWebBrokerBridge etc. bereits "automatisch" mehrere Threads existieren, ist dir bewusst?
Sorry, das suggeriert eigentlich, das alle diese Klassen einen Thread einführen. Dabei übernehmen sie nur den einen (pro Aufruf) von ihrem Vorfahren im Parameter IdContext.

Gruss
Delbor

Olli73 25. Aug 2014 14:45

AW: Webserveranwendung: EXE ruft ISAPI
 
Zitat:

Zitat von Delbor (Beitrag 1269772)
Und das bedeutet, dass mein Konzept, dem Thread alle Infos beim Start mitzugeben, hier nicht möglich ist.

Das ist es, was ich die ganze Zeit versuche zu erklären... ;)

Deshalb solltest du einfach eine DLL (oder EXE) verwenden und dir dort eine eigene Session-Verwaltung (mit CriticalSections !) einbauen.

Gru0,
Olli


Alle Zeitangaben in WEZ +1. Es ist jetzt 17:06 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