Entwickler-Ecke
Internet / Netzwerk - Problem mit Data-Snap
kalle345 - Sa 30.06.12 20:20
Titel: Problem mit Data-Snap
Ich bin gerade dabei ein LAN fähiges Mühle Spiel zu implementieren.
Für die Kommunikation von Server und Client verwende ich Data-Snap.
http://docwiki.embarcadero.com/RADStudio/de/Tutorial:_Einen_DataSnap-Server_mit_einer_Anwendung_verwenden
Jedoch erhalte ich beim Auffrufen der Funktion im Client:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12:
| function TClientVerbindungClient.verbunden_c: Boolean; begin if Fverbunden_cCommand = nil then begin Fverbunden_cCommand := FDBXConnection.CreateCommand; Fverbunden_cCommand.CommandType := TDBXCommandTypes.DSServerMethod; Fverbunden_cCommand.Text := 'TClientVerbindung.verbunden_c'; Fverbunden_cCommand.Prepare; end; Fverbunden_cCommand.ExecuteUpdate; Result := Fverbunden_cCommand.Parameters[0].Value.GetBoolean; end; |
die Fehlermeldung:
der Aufruf sieht folgendermaßen aus:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21:
| procedure TGUI.B_verbindenClick(Sender: TObject); var Temp: TClientVerbindungClient; begin SQLConnect_muehle_client.Params.Values['HostName'] := E_verbinden.Text; SQLConnect_muehle_client.Connected := true;
Temp:=TClientVerbindungClient.Create(SQLConnect_muehle_client.DBXConnection); if Temp.verbunden_c()=true then begin; ShowMessage('Verbunden...'); B_verbinden.Enabled := false; L_status.Caption:='Du bist an der Reihe'; derChat.schreibeIP(E_verbinden.Text); dieSteuerung.verbunden(); end; Temp.Free; end; |
die Prozedur im Server sieht folgendermaßen aus:
Delphi-Quelltext
1: 2: 3: 4: 5: 6:
| function TClientVerbindung.verbunden_c():boolean; begin AnzSteineImSpiel:=0; dieGUI.verbunden(); result := true; end; |
Wenn ich <dieGUI.verbunden();> auskommentiere funktioniert alles Problemlos..
Hoffe mir kann jemand weiterhelfen. Ich komme einfach nicht weiter.
Gruß
Moderiert von
Narses: Bild als Anhang hochgeladen.
jaenicke - Sa 30.06.12 20:51
Wo wird denn "dieGUI" initialisiert? Entweder ist diese Variable selbst nicht zugewiesen oder irgendetwas darin geht schief.
Hast du denn einmal einen Haltepunkt an die Stelle im Server gesetzt und geschaut was da passiert? Also was dieGUI ist usw.?
Zudem befindest du dich an der Stelle in einem Thread, d.h. Zugriffe auf gemeinsame oder visuelle Elemente müssen synchronisiert werden!
kalle345 - So 01.07.12 19:54
wenn ich im server im debugging durchsteppe kommt der Fehler direkt beim ersten Befehl in der Procedure <TGUI.verbunden();>.
dieGUI wird im Constructor von TClientVerbindung initialisiert:
Delphi-Quelltext
1: 2: 3: 4:
| constructor TClientVerbindung.erzeugeClientVerbindung(p_GUI:TGUI); begin dieGUI:=p_GUI; end; |
der Aufruf des Constructor erfolgt in FormCreate:
Delphi-Quelltext
1: 2: 3: 4: 5:
| procedure TGUI.FormCreate(Sender: TObject); begin dieSteuerung := TSteuerung.CreateSteuerung(self); TClientVerbindung.erzeugeClientVerbindung(self); end; |
Die Klasse TGUI kennt die Klasse TClientVerbindung nicht.
Aber die Klasse TClientVerbindung kennt die Klasse TGUI.
Und noch was: Ich habe TClientVerbindung nicht in der Forward-Deklaration (direkt nach type).
Ist das von Bedeutung?
Das mit dem synchronisieren hab ich noch gar nicht gelesen/gehört wie macht man das?
Gruß
jaenicke - So 01.07.12 20:34
Ach du Schande... ein Konstruktor, der nicht Create heißt. Das kann nur ins Auge gehen, wenn man nicht sehr genau weiß was man da tut... (und wer es weiß macht sowas nicht :zwinker:)
Ich rate dir
dringend den immer Create zu nennen. Tust du das nicht, wird dieser z.B. ausschließlich benutzt, wenn er direkt aufgerufen wird. Das kann gerade bei DataSnap ins Auge gehen, wenn auch vor allem bei übertragenen Objekten. Zudem fehlt dort der Aufruf des vererbten Konstruktors, es sei denn dein Objekt ist direkt von TObject abgeleitet.
Die Forward-Deklaration ist egal, wenn es kompiliert. Die braucht nur der Compiler.
Synchronisieren geht z.B. mit TThread.Synchronize.
Zum weiteren Debuggen:
kalle345 hat folgendes geschrieben : |
wenn ich im server im debugging durchsteppe kommt der Fehler direkt beim ersten Befehl in der Procedure <TGUI.verbunden();>. |
Und welchen Wert hat dann in verbunden (vor dem Ausführen des Befehls) Self? Wenn das nil ist, ist deine GUI nicht zugewiesen.
Wenn du möchtest, kannst du das Projekt ja mal anhängen oder mir per PN schicken, wenn du es nicht veröffentlichen möchtest.
jaenicke - So 01.07.12 21:34
Nachdem ich das Projekt nun per PN erhalten habe, habe ich beim Debuggen einmal die Maus drüber gehalten: ;-)
Ein Blick in den Quelltext hat mir auch gezeigt warum. Einmal zur Erklärung:
TDSServerClass verwaltet wie du denke ich weißt die Verbindungsklassen. Wenn nun ein Client anfragt, wird die Klasse mit OnGetClass ermittelt. Für jede neue Verbindung wird diese nun ggf. neu erzeugt.
Aber wenn "DataSnap" das tut, weiß das ja nichts von deinem Parameter. Stattdessen wird der Standardkonstruktor aufgerufen, den du aber nicht überschrieben hast.
Du hast also ein neues Verbindungsobjekt, das nun aber die GUI gar nicht bekommen hat.
Lösung:
Benutze auch OnCreateInstance der TDSServerClass und erzeuge das Verbindungsobjekt selbst:
Delphi-Quelltext
1: 2: 3: 4:
| procedure TGUI.DSMuehleClass_serverCreateInstance(DSCreateInstanceEventObject: TDSCreateInstanceEventObject); begin DSCreateInstanceEventObject.ServerClassInstance := TClientVerbindung.Create(Self); end; |
Dann zur Synchronisation:
Die fehlt in der Tat. Das sieht z.B. so aus, wenn es asynchron passieren soll (mit Synchronize würde der Aufruf warten bis der Aufruf ausgeführt ist):
Delphi-Quelltext
1: 2: 3: 4: 5:
| TThread.Queue(nil, procedure begin dieGUI.verbunden(); end); |
Dass es verschiedene Threads sind, siehst du in den beiden Bildern im Anhang. Der eine Screenshot ist beim Aufruf von TThread.Queue, der zweite bei der Ausführung darin. In der Threadliste siehst du, dass du jeweils in verschiedenen Threads bist wie ich ja schon geschrieben hatte. Aber ich wollte dir auch noch zeigen wie man das sehen kann.
(// EDIT: Wenn die Ausführung angehalten ist, ist man in dem Thread, bei dem Haltepunkt steht.)
Die GUI so fest mit der Steuerung zu verbandeln ist aber ohnehin nicht gut. Besser wären Events, die du mit Methoden in der GUI verbindest. Also im Prinzip sowas:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
| type TOnConnect = procedure(...) of object;
property OnConnect: TOnConnect read FOnConnect write FOnConnect;
if Assigned(FOnConnect) then TThread.Synchronize(nil, procedure begin FOnConnect(...); end; |
Das macht es auch einfacher alles in unterschiedliche Units zu packen. Denn das ist auch sehr sinnvoll. :zwinker:
kalle345 - So 01.07.12 23:11
Ich muss dir an dieser Stelle einfach mal ein großes Lob und Dankeschön aussprechen. Du gibst dir immer sehr viel Mühe beim Helfen.
jaenicke hat folgendes geschrieben : |
Die GUI so fest mit der Steuerung zu verbandeln ist aber ohnehin nicht gut. Besser wären Events, die du mit Methoden in der GUI verbindest. Also im Prinzip sowas: Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
| type TOnConnect = procedure(...) of object;
property OnConnect: TOnConnect read FOnConnect write FOnConnect;
if Assigned(FOnConnect) then TThread.Synchronize(nil, procedure begin FOnConnect(...); end; | Das macht es auch einfacher alles in unterschiedliche Units zu packen. Denn das ist auch sehr sinnvoll. :zwinker: |
Das glaube ich dir gerne.
Jedoch muss ich es nach diesem Muster bei meinem Lehrer abliefern...
Mit deinem letzten Beitrag habe ich es jetzt geschafft dass ich aus dem Client die Funktion <
TClientVerbindung.verbunden_c():boolean;> des Servers problemlos aufrufen kann.
Wenn ich jetzt jedoch versuche die <
TClientVerbindung.spielen_setzphase_c> mit dem Client aus dem Servers aufzurufen (sprich: nach dem verbinden im Client auf das Spielfeld klicken) spuckt er folgende Fehlermeldung aus:
---------------------------
muehle_p
---------------------------
Remote-Fehler: Methode TClientVerbindung.spielen_setzphase_c in der Servermethodenliste nicht gefunden.
---------------------------
OK
---------------------------
Gruß
jaenicke - So 01.07.12 23:41
kalle345 hat folgendes geschrieben : |
Ich muss dir an dieser Stelle einfach mal ein großes Lob und Dankeschön aussprechen. Du gibst dir immer sehr viel Mühe beim Helfen. |
Danke ;-)
kalle345 hat folgendes geschrieben : |
Das glaube ich dir gerne.
Jedoch muss ich es nach diesem Muster bei meinem Lehrer abliefern... |
Da könnt ich jetzt noch einiges zu schreiben, wenn er das genau so meinte, aber ich lass es besser. Dem Lehrer zuliebe. :zwinker:
Wobei ich es aber trotzdem schön finde, dass überhaupt so etwas wie DataSnap im Unterricht vorkommt. :zustimm:
kalle345 hat folgendes geschrieben : |
Remote-Fehler: Methode TClientVerbindung.spielen_setzphase_c in der Servermethodenliste nicht gefunden. |
Dort gibt es ja (in der Version hier jedenfalls) auch nur spielen_setzphase_
s.
Und dieser Kommentar ist schon einmal
sehr schlecht:
Diese Client-Klassen sollten
immer automatisch erzeugt werden. (SQLConnection verbinden, rechts draufklicken und auf Client-Klasse erzeugen klicken.)
Sonst machst du Änderungen, vergisst die auf der anderen Seite, ...
kalle345 - Mo 02.07.12 17:03
jaenicke hat folgendes geschrieben : |
Und dieser Kommentar ist schon einmal sehr schlecht:
Diese Client-Klassen sollten immer automatisch erzeugt werden. (SQLConnection verbinden, rechts draufklicken und auf Client-Klasse erzeugen klicken.)
Sonst machst du Änderungen, vergisst die auf der anderen Seite, ... |
Anfangs habe ich die Data-Snap Klasse selbst erweitert, weil die automatische Erzeugung nicht alle procedures/functions des Servers auch wirklich erzeugt hatte. Jedoch habe ich das Problem jetzt behoben indem ich anstatt cardinal‘s integer’s verwende. Anscheinen mag Data-Snap cardinal’s nicht...
---------------------------------------------------------------------------------------------
Nun stehe ich aber schon vor einem weiteren Problem:
Wenn ich beispielsweise im Konstruktor von TSteuerung folgendes deklariere:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19:
| constructor TSteuerung.Create(p_GUI:TGUI); var I: Integer; begin for I := 1 to 9 do begin derStein_weiss[I] := TStein.Create(); derStein_schwarz[I] := TStein.Create(); end;
for I := 1 to 8 do begin dasSpielfeld_innen[I] := TSpielfeld.Create(); dasSpielfeld_mitte[I] := TSpielfeld.Create(); dasSpielfeld_aussen[I] := TSpielfeld.Create();
dasSpielfeld_innen[I].aendereBesetzung(keine_f); dasSpielfeld_mitte[I].aendereBesetzung(keine_f); dasSpielfeld_aussen[I].aendereBesetzung(keine_f); end; end; |
und im im Konstruktor von TClientVerbindung auch:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19:
| constructor TClientVerbindung.Create(p_GUI:TGUI; p_Steuerung: TSteuerung); var I:cardinal; begin for I := 1 to 9 do begin derStein_weiss[I] := TStein.Create(); derStein_schwarz[I] := TStein.Create(); end;
for I := 1 to 8 do begin dasSpielfeld_innen[I] := TSpielfeld.Create(); dasSpielfeld_mitte[I] := TSpielfeld.Create(); dasSpielfeld_aussen[I] := TSpielfeld.Create();
dasSpielfeld_innen[I].aendereBesetzung(keine_f); dasSpielfeld_mitte[I].aendereBesetzung(keine_f); dasSpielfeld_aussen[I].aendereBesetzung(keine_f); end; end; |
Greife ich dann auf die gleichen Objekte (z.B. dasSpielfeld_mitte[2]) von den beiden Klassen TClientVerbindung+TSteuerung aus zu? Ich hoffe das versteht man :D..
Gruß
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2025 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!