Remoting und Events im Server-Objekt

jgeske

Grünschnabel
Hi zusammen,

ich spiele gerade mit dem Remoting-Tutorial von Crazy Weasel herum. Ich habe das Client-GUI um eine Listbox erweitert, die mir eine Auflistung aller am Server angemeldeten Clients zeigt. Als Datasource der Listbox dient ein DataTable, welches ich über ein Member des Server-Objektes abrufe. Soweit funktioniert auch alles gut.

Nun möchte ich die Listbox immer dann aktualisieren, wenn sich ein Client am Server anmeldet oder abmeldet. Dazu habe ich das Interface-Assembly um ein Delegate und ein Event erweitert:

Code:
using System;
using System.Data;

namespace Interfaces
{
public delegate void ClientListChange(); // Die Ereignis-Deklaration

public interface IServer
{
void _login (IClient client);
void _logout (IClient client);
bool _sendText (string text, IClient client);
DataTable ClientList { get; }
event ClientListChange OnClientListChange; // Hier ist das Event zum Anzeigen der angemeldeten Clients
}
 
public interface IClient
{
Guid _guid { get; }
string _nick { get; }
void _setText (string text); 
}
}


Im Server-Objekt habe ich das Event ebenfalls implementiert:

Code:
...
public event ClientListChange OnClientListChange;
...


Im Client-Objekt mache ich die Ereignis-Behandlung:

Code:
...
this.remServer = (IServer) Activator.GetObject(typeof(IServer), "tcp://" + txtIP.Text.Trim() + ":" + txtPort.Text.Trim() + "/chat.rem");
this.remServer.OnClientListChange += new ClientListChange(LoadClientList); // Hier ist die Ereignisbehandlung
this.remServer._login(myClient);


Wenn ich nun versuche den Client am Server anzumelden, bringt der Client die Exception, die ich diesem Posting unten beigefügt habe. Der Fehler tritt an der Stelle auf, an der ich die Ereignisbehandlung aufrufe. Leider erkenne ich nicht, was ich falsch gemacht habe. Ich wäre froh und dankbar, wenn ihr mir helfen könnten.

Viele Grüße, Jo



************** Ausnametext **************
System.Runtime.Serialization.SerializationException: Die Assembly Client, Version=1.0.1822.29431, Culture=neutral, PublicKeyToken=null kann nicht gefunden werden.
Server stack trace:
at System.DelegateSerializationHolder.GetDelegate(DelegateEntry de)
at System.DelegateSerializationHolder.GetRealObject(StreamingContext context)
at System.Runtime.Serialization.ObjectManager.ResolveObjectReference(ObjectHolder holder)
at System.Runtime.Serialization.ObjectManager.DoFixups()
at System.Runtime.Serialization.Formatters.Binary.ObjectReader.Deserialize(HeaderHandler handler, __BinaryParser serParser, Boolean fCheck, IMethodCallMessage methodCallMessage)
at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream, HeaderHandler handler, Boolean fCheck, IMethodCallMessage methodCallMessage)
at System.Runtime.Remoting.Channels.CoreChannel.DeserializeBinaryRequestMessage(String objectUri, Stream inputStream, Boolean bStrictBinding, TypeFilterLevel securityLevel)
at System.Runtime.Remoting.Channels.BinaryServerFormatterSink.ProcessMessage(IServerChannelSinkStack sinkStack, IMessage requestMsg, ITransportHeaders requestHeaders, Stream requestStream, IMessage& responseMsg, ITransportHeaders& responseHeaders, Stream& responseStream)
Exception rethrown at [0]:
at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
at Interfaces.IServer.add_OnClientListChange(ClientListChange value)
at Client.client.connect() in c:\Dokumente und Einstellungen\geske.INTERN\Desktop\RemotingTutorial\Client\client.cs:line 191
at Client.client.btnConnect_Click(Object sender, EventArgs e) in c:\Dokumente und Einstellungen\geske.INTERN\Desktop\RemotingTutorial\Client\client.cs:line 215
at System.Windows.Forms.Control.OnClick(EventArgs e)
at System.Windows.Forms.Button.OnClick(EventArgs e)
at System.Windows.Forms.Button.OnMouseUp(MouseEventArgs mevent)
at System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)
at System.Windows.Forms.Control.WndProc(Message& m)
at System.Windows.Forms.ButtonBase.WndProc(Message& m)
at System.Windows.Forms.Button.WndProc(Message& m)
at System.Windows.Forms.ControlNativeWindow.OnMessage(Message& m)
at System.Windows.Forms.ControlNativeWindow.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)

************** Geladene Assemblys **************
 
Nochmals hallo!

Ich möchte mal einen Teil meines Problems selbst beantworten: Der Fehler tritt auf, da das Event nicht korrekt deseriailisiert werden kann. Wie dies zu lösen wäre, weiß ich im Moment nicht. Für mein Problem gibt es natürlich einen einfachen Workaround. Also, vielleicht weiß ja noch jemand was zu dem Thema Serialisierung von Events.

Viele Grüße, Jo
 
Den Kampf mit diesem Problem habe ich vor etwa einem Monat verloren. Das .NET Framework versucht das Delegate, welches das Event behandelt, an den Server zu schicken, was natürlich nicht funktionieren kann, da die damit verknüpfte Methode beim Client ja nicht vorhanden ist. Zudem akzeptiert das .NET Framework keine "fremden" Delegates und sieht sie als Sicherheitsproblem an (-> .NET 2.0).

Ich habe schon versucht, dass Client und Server gegenseitig Remote-Objekte austauschen, hat aber irgendwie nicht funktioniert. Die letzte Möglichkeit wäre eine simple TCP-Verbindung vom Client zum Server. In diesem Thread wurde mir dieser Vorschlag gemacht, der mir bis jetzt aber nicht weiterhelfen konnte.
 
Hi Sunray!

Danke für Deine Antwort. Ich lege die Sache jetzt auch auf Eis und warte, wie es mit .NET 2.0 aussieht.

cu, Jo
 
Hi zusammen,

habe oben genanntes Problem umgangen, in dem ich nicht ein Event nutze, sondern die Daten beim Login/Logout am Server an den Client sende. Eigentlich ist das genau die Lösung, die auch erik s. in diesem Thread gewählt hat, nur habe ich den zu spät bemerkt. :-(

Jetzt habe ich aber folgendes Problem: Ich sende einen DataTable mit drei Spalten vom Typ Guid, string und IClient an den Client. Den Datentyp DataTable habe ich nur gewählt, weil ich den Hashtable aus irgendeinem Grund nicht serialisieren und deserialisieren konnte. Vermutlich habe ich hier schon etwas falsch gemacht.
Der Table enthält die Objekte aller User. Aber irgendwas scheint beim deserialisieren nicht zu stimmen, jedenfalls habe ich in der Spalte IClient später einen String und kein Objekt vom Typ IClient mehr. Kann mir da jemand sagen, was ich falsch gemacht habe.

cu, Jo

Hier ein wenig Code:

Senden des DataTable _clients an jeden Client
Code:
foreach(DataRow row in this._clients.Rows)
{
IClient user = (IClient)row["client"];
user._setUser(this._clients);
}


Hier die aufgerufene Methode im ClientObject
Code:
public void _setUser(DataTable clients)
{
myParent.lbClients.DataSource = clients; /* lbClients ist eine Listbox, die als Source den DataTable bekommt */
... /* Hier folgt jetzt etwas zur Demonstration meines Problems: */
DataRow row = clients.Rows[0];
Response.Write(row["client"].GetType().ToString()); /* Gibt leider "String" und nicht "IClient" als Typ aus. */
}
 
:offtopic:

@Sunray:
Das Remoting-Tutorial hab ich auch erst vor 7 Tagen mitbekommen. Konnte mich aber leider nicht mehr an unseren Thread erinnern.
Scheint aber unsere Lösung zu sein. ;)

GUTEN RUTSCH !

MfG cosmo
 
Hast dus auch mal mit einer Structure (dein eigener ValueType) oder mit einer, nur zum Transport dieser 3 Werte erstellten, als serialisierbar gekennzeichneten Klasse versucht? Ist vielleicht etwas "transparenter" als die DataTable.
 
Hi Sunray,

danke für die Anregung. Das werde ich bei Gelegenheit mal ausprobieren.
Viele Grüße und guten Rutsch

Jo
 
Zurück