Hier mal ein kleiner Beispiel-Code für Remoting über TCP-Channels.
Die Erklärung des Codes, steht in den Kommentaren.
Ohne Config-Dateien
Beispielprojekt ist im Anhang: RemotingVB.zip
Man benötigt:
Zwei WinForms Anwendungen (RemotingServer und RemotingClient) und eine DLL (RemoteableObject).
Auf die Form des Servers (Server.vb) kommt eine TextBox (Multiline) und ein Button um die TextBox zu leeren.
Auf die Form des Clients (Client.vb) kommt eine TextBox und ein Button zum Senden.
Code Server:
Spoiler anzeigen
Code Client:
Spoiler anzeigen
Die DLL besteht aus drei Dateien.
Cache.vb
Spoiler anzeigen
IObserver.vb
MyRemoteableObject.vb
Spoiler anzeigen
Mit Config-Dateien
Um Config-Dateien zu verwenden, wird beim Server in der Sub Load folgender Code gelöscht:
Spoiler anzeigen
Stattdessen fügt man folgenden Code an dessen Stelle ein:
Beim Client löscht man (ebenfalls in der Sub Load):
Spoiler anzeigen
und fügt folgenden Code ein:
Spoiler anzeigen
Nun legt man eine beim Server eine neue TextDatei mit dem Namen "RemoteServer.config" und beim Client eine neue TextDatei mit dem Namen "RemoteClient.config" an.
Diese Dateien müssen immer ins Ausgabeverzeichnis kopiert werden.
in die RemotingServer.config kommt folgendes:
Spoiler anzeigen
in die RemotingClient.config kommt folgendes:
Spoiler anzeigen
Eine vollständige Erklärung zu den Config-Dateien findet ihr auf MSDN
Hier noch ein Beispiel Projekt, mit den Config-Dateien: RemotingVBwConfig.zip
Edit: Getestet in FW 2.0, 3.5 und 4.0.
Wenn man die DLL (RemoteableObject.dll) mit FW 2.0 erstellt, kann man den Server und den Client mit verschiedenen Frameworks bauen. Somit ist es möglich, den Client mit FW 2.0 zu erstellen (max. Kompatibilität) und den Server mit FW 4.0 (max. Funktionalität).
Die Erklärung des Codes, steht in den Kommentaren.
Ohne Config-Dateien
Beispielprojekt ist im Anhang: RemotingVB.zip
Man benötigt:
Zwei WinForms Anwendungen (RemotingServer und RemotingClient) und eine DLL (RemoteableObject).
Auf die Form des Servers (Server.vb) kommt eine TextBox (Multiline) und ein Button um die TextBox zu leeren.
Auf die Form des Clients (Client.vb) kommt eine TextBox und ein Button zum Senden.
Code Server:
VB.NET-Quellcode
- Imports RemoteableObject
- Imports System.Runtime.Remoting
- Imports System.Runtime.Remoting.Channels
- Imports System.Runtime.Remoting.Channels.Tcp
- Public Class Server
- 'Implementiert das IObserver Interface, um die aktuelle Instanz des Servers, an die aktuelle Instanz des Caches anzuhängen und um die Methode Notify bereit zu stellen
- Implements RemoteableObject.IObserver
- ''' <summary>
- ''' Definiert den Port, über den die TCP-Verbindung stattfindet.
- ''' </summary>
- ''' <remarks></remarks>
- Const PORT = 1234
- ''' <summary>
- ''' Definiert den Namen des DienstObjekts
- ''' </summary>
- ''' <remarks></remarks>
- Const ServiceName As String = "SendMessage"
- ''' <summary>
- ''' Dieser Delegat schreibt Text in die TextBox, im Kontext des UI-Threads.
- ''' </summary>
- ''' <param name="text"></param>
- ''' <remarks></remarks>
- Delegate Sub SetTextFromUIThread(ByVal text As String)
- ''' <summary>
- ''' Das Objekt, welches über Remoting verfügbar gemacht wird.
- ''' </summary>
- ''' <remarks></remarks>
- Private _remotableObject As MyRemotableObject
- Private Sub Form1_Load(sender As Object, e As System.EventArgs) Handles Me.Load
- _remotableObject = New MyRemotableObject()
- 'Hier wird ein neuer TcpChannel mit dem gewünschten Port erstellt.
- Dim channel As TcpChannel = New TcpChannel(PORT)
- 'Vor der ersten Verwendung muss der Channel registriert werden
- ChannelServices.RegisterChannel(channel, False)
- 'Damit die Übertragung funktioniert, muss der Typ, welcher beim Remoting verwendet werden soll, als WellKnownServiceType registriert werden.
- 'Man kann diesen auf zwei Arten registrieren. Als Singleton oder SingleCall.
- 'SingleCall: Für jeden Aufruf durch einen Client wird eine neue Instanz erstellt. Es werden keine Statusinformationen gespeichert und die Instanz des Objekts
- ' wird nach dem Aufruf zerstört und vom GC abgeholt.
- RemotingConfiguration.RegisterWellKnownServiceType(GetType(MyRemotableObject), ServiceName, WellKnownObjectMode.SingleCall)
- 'Singleton: Ein Instanz, wird für alle Aufrufe durch Clients verwendet. Es können Statusinformationen gespeichert werden.
- ' Diese Methode kann verwendet werden, um Daten zwischen zwei Clients austauschen zu können. Die Instanz des Objekts wird nach dem Aufruf nicht zerstört,
- ' sondern bleibt noch für eine gewisse Zeit erhalten (Lease-Time).
- 'RemotingConfiguration.RegisterWellKnownServiceType(GetType(MyRemotableObject), ServiceName, WellKnownObjectMode.Singleton)
- 'Fügt sich selbst dem Cache als IObserver-Objekt hinzu.
- RemoteableObject.Cache.Attach(Me)
- End Sub
- 'Diese Methode wird am Server ausgeführt, wenn der Client die Methode _remotableObject.SetMessage aufgerufen wird.
- Public Sub Notify(ByVal text As String) Implements IObserver.Notify
- 'Ruft den Delegaten zum befüllen der Textbox, im Kontext des UI-Threads auf
- Me.Invoke(write, text)
- End Sub
- 'Erstellt einen neuen Delegaten (SetTextFromUIThread: Siehe oben), der auf die Methode TextReceived verweist.
- Private write As New SetTextFromUIThread(AddressOf TextReceived)
- 'Diese Methode wird durch den Delegaten aufgerufen.
- Private Sub TextReceived(ByVal text As String)
- TextBox1.Text &= String.Concat(text, vbCrLf)
- End Sub
- Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
- TextBox1.Clear()
- End Sub
- End Class
Code Client:
VB.NET-Quellcode
- Imports RemoteableObject
- Imports System.Runtime.Remoting
- Imports System.Runtime.Remoting.Channels
- Imports System.Runtime.Remoting.Channels.Tcp
- Public Class Client
- ''' <summary>
- ''' Die lokale Instanz, des RemoteObjekts
- ''' </summary>
- ''' <remarks></remarks>
- Private _remoteObject As MyRemotableObject
- Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
- 'Hier wird ein neuer TcpChannel ohne vorgegebenen Port erstellt, um eine Kommunikation zu ermöglichen.
- Dim chan As TcpChannel = New TcpChannel()
- 'Vor der ersten Verwendung muss der Channel registriert werden
- ChannelServices.RegisterChannel(chan, False)
- 'Hier wird das RemoteObjekt vom Server geholt und in _remoteObject gespeichert.
- Dim ServerNameOrIP As String = "localhost"
- Dim Port As Integer = 1234
- Dim ServiceName As String = "SendMessage"
- _remoteObject = DirectCast(Activator.GetObject(GetType(MyRemotableObject), String.Concat("tcp://", ServerNameOrIP, ":", Port.ToString, "/", ServiceName)), MyRemotableObject)
- End Sub
- Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
- 'Hier wird durch die Methode SetMessage, bereits am Server auseführt.
- _remoteObject.SetMessage(TextBox1.Text)
- End Sub
- ''' <summary>
- ''' Kann verwendet werden, um die Laufzeit eines Singleton-Objekts aktiv zu verlängern.
- ''' </summary>
- ''' <param name="Time"></param>
- ''' <remarks></remarks>
- Private Sub ExtendRemotingLifetime(ByVal Time As TimeSpan)
- DirectCast(RemotingServices.GetLifetimeService(_remoteObject), Lifetime.ILease).Renew(Time)
- End Sub
- End Class
Die DLL besteht aus drei Dateien.
Cache.vb
VB.NET-Quellcode
- ''' <summary>
- ''' Durch den Cache kann gewährleistet werden, dass alle Aufrufe auf das selbe Instanz zugreifen.
- ''' </summary>
- ''' <remarks></remarks>
- Public Class Cache
- ''' <summary>
- ''' Die aktuelle Instanz des Cache.
- ''' </summary>
- ''' <remarks></remarks>
- Private Shared myInstance As Cache
- ''' <summary>
- ''' Das aktuelle IObserver-Objekt.
- ''' </summary>
- ''' <remarks></remarks>
- Public Shared Observer As IObserver
- Sub New()
- MyBase.New()
- End Sub
- ''' <summary>
- ''' Hängt ein IObserver-Objekt an den Cache.
- ''' </summary>
- ''' <param name="observer"></param>
- ''' <remarks></remarks>
- Public Shared Sub Attach(ByRef observer As IObserver)
- Cache.Observer = observer
- End Sub
- ''' <summary>
- ''' Holt die aktuelle Instanz oder erstellt eine Neue.
- ''' </summary>
- ''' <returns></returns>
- ''' <remarks></remarks>
- Public Shared Function GetInstance() As Cache
- If Object.ReferenceEquals(myInstance, Nothing) Then
- myInstance = New Cache()
- End If
- Return myInstance
- End Function
- 'Diese WriteOnly Eigenschaft wird verwendet, um die Aufrufe vom Client an den Server weiterzuleiten. (Man könnte auch eine Sub verwenden)
- Public WriteOnly Property MessageString() As String
- Set(value As String)
- Observer.Notify(value)
- End Set
- End Property
- End Class
IObserver.vb
MyRemoteableObject.vb
VB.NET-Quellcode
- Imports System.Runtime.Remoting
- Imports System.Runtime.Remoting.Channels
- ''' <summary>
- ''' This Object is used for the Remoting
- ''' </summary>
- ''' <remarks></remarks>
- Public Class MyRemotableObject
- Inherits MarshalByRefObject
- Public Sub New()
- MyBase.New()
- End Sub
- 'Die Einstellungen der LeaseTime sind die Default-Einstellungen
- ''' <summary>
- ''' Die Zeit, die ein Singleton Objekt grundsätzlich lebt. (Minuten)
- ''' </summary>
- ''' <remarks></remarks>
- Const TimeToLive As Integer = 5
- ''' <summary>
- ''' Wann soll abgefragt werden, ob die LeaseTime überschritten wurde. (Sekunden)
- ''' </summary>
- ''' <remarks></remarks>
- Const PollEvery As Integer = 10
- ''' <summary>
- ''' Um wie viel soll die Zeit, bei einem aufruf durch einen Client, verlängert werden (Minuten)
- ''' </summary>
- ''' <remarks></remarks>
- Const AddForRemoteCall = 2
- ''' <summary>
- ''' Hiermit kann die Lebensdauer eines Singleton-Objektes bestimmt werden.
- ''' </summary>
- ''' <returns></returns>
- ''' <remarks></remarks>
- Public Overrides Function InitializeLifetimeService() As Object
- Dim lease As Lifetime.ILease = DirectCast(MyBase.InitializeLifetimeService, Lifetime.ILease)
- If lease.CurrentState = Lifetime.LeaseState.Initial Then
- lease.InitialLeaseTime = TimeSpan.FromMinutes(5)
- lease.SponsorshipTimeout = TimeSpan.FromSeconds(10)
- lease.RenewOnCallTime = TimeSpan.FromMinutes(2)
- End If
- Return lease
- End Function
- ''' <summary>
- ''' SetMessage schreibt den Text des Parameters "message", in die Eigenschaft des Observer-Objekts, das in der aktuellen Instanz von Cache gespeichert wird.
- ''' </summary>
- ''' <param name="message"></param>
- ''' <remarks></remarks>
- Public Sub SetMessage(ByVal message As String)
- Cache.GetInstance().MessageString = message
- End Sub
- End Class
Mit Config-Dateien
Um Config-Dateien zu verwenden, wird beim Server in der Sub Load folgender Code gelöscht:
VB.NET-Quellcode
- 'Hier wird ein neuer TcpChannel mit dem gewünschten Port erstellt.
- Dim channel As TcpChannel = New TcpChannel(PORT)
- 'Vor der ersten Verwendung muss der Channel registriert werden
- ChannelServices.RegisterChannel(channel, False)
- 'Damit die Übertragung funktioniert, muss der Typ, welcher beim Remoting verwendet werden soll, als WellKnownServiceType registriert werden.
- 'Man kann diesen auf zwei Arten registrieren. Als Singleton oder SingleCall.
- 'SingleCall: Für jeden Aufruf durch einen Client wird eine neue Instanz erstellt. Es werden keine Statusinformationen gespeichert und die Instanz des Objekts
- ' wird nach dem Aufruf zerstört und vom GC abgeholt.
- RemotingConfiguration.RegisterWellKnownServiceType(GetType(MyRemotableObject), ServiceName, WellKnownObjectMode.SingleCall)
- 'Singleton: Ein Instanz, wird für alle Aufrufe durch Clients verwendet. Es können Statusinformationen gespeichert werden.
- ' Diese Methode kann verwendet werden, um Daten zwischen zwei Clients austauschen zu können. Die Instanz des Objekts wird nach dem Aufruf nicht zerstört,
- ' sondern bleibt noch für eine gewisse Zeit erhalten (Lease-Time).
- 'RemotingConfiguration.RegisterWellKnownServiceType(GetType(MyRemotableObject), ServiceName, WellKnownObjectMode.Singleton)
- 'Fügt sich selbst dem Cache als IObserver-Objekt hinzu.
Stattdessen fügt man folgenden Code an dessen Stelle ein:
Beim Client löscht man (ebenfalls in der Sub Load):
VB.NET-Quellcode
- 'Hier wird ein neuer TcpChannel ohne vorgegebenen Port erstellt, um eine Kommunikation zu ermöglichen.
- Dim chan As TcpChannel = New TcpChannel()
- 'Vor der ersten Verwendung muss der Channel registriert werden
- ChannelServices.RegisterChannel(chan, False)
- 'Hier wird das RemoteObjekt vom Server geholt und in _remoteObject gespeichert.
- Dim ServerNameOrIP As String = "localhost"
- Dim Port As Integer = 1234
- Dim ServiceName As String = "SendMessage"
- _remoteObject = DirectCast(Activator.GetObject(GetType(MyRemotableObject), String.Concat("tcp://", ServerNameOrIP, ":", Port.ToString, "/", ServiceName)), MyRemotableObject)
und fügt folgenden Code ein:
VB.NET-Quellcode
- 'Lädt die Konfiguration aus der Datei "RemotingServer.config"
- RemotingConfiguration.Configure("RemotingClient.config", False)
- 'Erstellt das RemoteObjekt mit der geladenen Konfiguration
- _remoteObject = DirectCast(Activator.GetObject(GetType(MyRemotableObject), RemotingConfiguration.GetRegisteredWellKnownClientTypes(0).ObjectUrl), MyRemotableObject)
Nun legt man eine beim Server eine neue TextDatei mit dem Namen "RemoteServer.config" und beim Client eine neue TextDatei mit dem Namen "RemoteClient.config" an.
Diese Dateien müssen immer ins Ausgabeverzeichnis kopiert werden.
in die RemotingServer.config kommt folgendes:
XML-Quellcode
- <configuration>
- <system.runtime.remoting>
- <application name="RemotingServer">
- <service>
- <wellknown mode="SingleCall" type="RemoteableObject.MyRemotableObject, RemoteableObject" objectUri="SendMessage"/>
- </service>
- <channels>
- <channel ref="tcp" port="1234">
- <serverProviders>
- <formatter ref="binary" typeFilterLevel="Full"/>
- </serverProviders>
- <clientProviders>
- <formatter ref="binary"/>
- </clientProviders>
- </channel>
- </channels>
- </application>
- </system.runtime.remoting>
- </configuration>
in die RemotingClient.config kommt folgendes:
XML-Quellcode
- <configuration>
- <system.runtime.remoting>
- <application name="RemotingClient">
- <client>
- <wellknown type="RemoteableObject.MyRemotableObject, RemoteableObject" url="tcp://localhost:1234/SendMessage"/>
- </client>
- <channels>
- <channel ref="tcp" port="0">
- <serverProviders>
- <formatter ref="binary" typeFilterLevel="Full"/>
- </serverProviders>
- <clientProviders>
- <formatter ref="binary"/>
- </clientProviders>
- </channel>
- </channels>
- </application>
- </system.runtime.remoting>
- </configuration>
Eine vollständige Erklärung zu den Config-Dateien findet ihr auf MSDN
Hier noch ein Beispiel Projekt, mit den Config-Dateien: RemotingVBwConfig.zip
Edit: Getestet in FW 2.0, 3.5 und 4.0.
Wenn man die DLL (RemoteableObject.dll) mit FW 2.0 erstellt, kann man den Server und den Client mit verschiedenen Frameworks bauen. Somit ist es möglich, den Client mit FW 2.0 zu erstellen (max. Kompatibilität) und den Server mit FW 4.0 (max. Funktionalität).
SWYgeW91IGNhbiByZWFkIHRoaXMsIHlvdSdyZSBhIGdlZWsgOkQ=
Weil einfach, einfach zu einfach ist!
Weil einfach, einfach zu einfach ist!
Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „BiedermannS“ ()