Remoting über TCP-Channel (Ohne Config-Datei und mit Config-Datei)

    • VB.NET

      Remoting über TCP-Channel (Ohne Config-Datei und mit Config-Datei)

      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

      VB.NET-Quellcode

      1. Imports RemoteableObject
      2. Imports System.Runtime.Remoting
      3. Imports System.Runtime.Remoting.Channels
      4. Imports System.Runtime.Remoting.Channels.Tcp
      5. Public Class Server
      6. '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
      7. Implements RemoteableObject.IObserver
      8. ''' <summary>
      9. ''' Definiert den Port, über den die TCP-Verbindung stattfindet.
      10. ''' </summary>
      11. ''' <remarks></remarks>
      12. Const PORT = 1234
      13. ''' <summary>
      14. ''' Definiert den Namen des DienstObjekts
      15. ''' </summary>
      16. ''' <remarks></remarks>
      17. Const ServiceName As String = "SendMessage"
      18. ''' <summary>
      19. ''' Dieser Delegat schreibt Text in die TextBox, im Kontext des UI-Threads.
      20. ''' </summary>
      21. ''' <param name="text"></param>
      22. ''' <remarks></remarks>
      23. Delegate Sub SetTextFromUIThread(ByVal text As String)
      24. ''' <summary>
      25. ''' Das Objekt, welches über Remoting verfügbar gemacht wird.
      26. ''' </summary>
      27. ''' <remarks></remarks>
      28. Private _remotableObject As MyRemotableObject
      29. Private Sub Form1_Load(sender As Object, e As System.EventArgs) Handles Me.Load
      30. _remotableObject = New MyRemotableObject()
      31. 'Hier wird ein neuer TcpChannel mit dem gewünschten Port erstellt.
      32. Dim channel As TcpChannel = New TcpChannel(PORT)
      33. 'Vor der ersten Verwendung muss der Channel registriert werden
      34. ChannelServices.RegisterChannel(channel, False)
      35. 'Damit die Übertragung funktioniert, muss der Typ, welcher beim Remoting verwendet werden soll, als WellKnownServiceType registriert werden.
      36. 'Man kann diesen auf zwei Arten registrieren. Als Singleton oder SingleCall.
      37. 'SingleCall: Für jeden Aufruf durch einen Client wird eine neue Instanz erstellt. Es werden keine Statusinformationen gespeichert und die Instanz des Objekts
      38. ' wird nach dem Aufruf zerstört und vom GC abgeholt.
      39. RemotingConfiguration.RegisterWellKnownServiceType(GetType(MyRemotableObject), ServiceName, WellKnownObjectMode.SingleCall)
      40. 'Singleton: Ein Instanz, wird für alle Aufrufe durch Clients verwendet. Es können Statusinformationen gespeichert werden.
      41. ' 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,
      42. ' sondern bleibt noch für eine gewisse Zeit erhalten (Lease-Time).
      43. 'RemotingConfiguration.RegisterWellKnownServiceType(GetType(MyRemotableObject), ServiceName, WellKnownObjectMode.Singleton)
      44. 'Fügt sich selbst dem Cache als IObserver-Objekt hinzu.
      45. RemoteableObject.Cache.Attach(Me)
      46. End Sub
      47. 'Diese Methode wird am Server ausgeführt, wenn der Client die Methode _remotableObject.SetMessage aufgerufen wird.
      48. Public Sub Notify(ByVal text As String) Implements IObserver.Notify
      49. 'Ruft den Delegaten zum befüllen der Textbox, im Kontext des UI-Threads auf
      50. Me.Invoke(write, text)
      51. End Sub
      52. 'Erstellt einen neuen Delegaten (SetTextFromUIThread: Siehe oben), der auf die Methode TextReceived verweist.
      53. Private write As New SetTextFromUIThread(AddressOf TextReceived)
      54. 'Diese Methode wird durch den Delegaten aufgerufen.
      55. Private Sub TextReceived(ByVal text As String)
      56. TextBox1.Text &= String.Concat(text, vbCrLf)
      57. End Sub
      58. Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
      59. TextBox1.Clear()
      60. End Sub
      61. End Class


      Code Client:
      Spoiler anzeigen

      VB.NET-Quellcode

      1. Imports RemoteableObject
      2. Imports System.Runtime.Remoting
      3. Imports System.Runtime.Remoting.Channels
      4. Imports System.Runtime.Remoting.Channels.Tcp
      5. Public Class Client
      6. ''' <summary>
      7. ''' Die lokale Instanz, des RemoteObjekts
      8. ''' </summary>
      9. ''' <remarks></remarks>
      10. Private _remoteObject As MyRemotableObject
      11. Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
      12. 'Hier wird ein neuer TcpChannel ohne vorgegebenen Port erstellt, um eine Kommunikation zu ermöglichen.
      13. Dim chan As TcpChannel = New TcpChannel()
      14. 'Vor der ersten Verwendung muss der Channel registriert werden
      15. ChannelServices.RegisterChannel(chan, False)
      16. 'Hier wird das RemoteObjekt vom Server geholt und in _remoteObject gespeichert.
      17. Dim ServerNameOrIP As String = "localhost"
      18. Dim Port As Integer = 1234
      19. Dim ServiceName As String = "SendMessage"
      20. _remoteObject = DirectCast(Activator.GetObject(GetType(MyRemotableObject), String.Concat("tcp://", ServerNameOrIP, ":", Port.ToString, "/", ServiceName)), MyRemotableObject)
      21. End Sub
      22. Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
      23. 'Hier wird durch die Methode SetMessage, bereits am Server auseführt.
      24. _remoteObject.SetMessage(TextBox1.Text)
      25. End Sub
      26. ''' <summary>
      27. ''' Kann verwendet werden, um die Laufzeit eines Singleton-Objekts aktiv zu verlängern.
      28. ''' </summary>
      29. ''' <param name="Time"></param>
      30. ''' <remarks></remarks>
      31. Private Sub ExtendRemotingLifetime(ByVal Time As TimeSpan)
      32. DirectCast(RemotingServices.GetLifetimeService(_remoteObject), Lifetime.ILease).Renew(Time)
      33. End Sub
      34. End Class


      Die DLL besteht aus drei Dateien.

      Cache.vb
      Spoiler anzeigen

      VB.NET-Quellcode

      1. ''' <summary>
      2. ''' Durch den Cache kann gewährleistet werden, dass alle Aufrufe auf das selbe Instanz zugreifen.
      3. ''' </summary>
      4. ''' <remarks></remarks>
      5. Public Class Cache
      6. ''' <summary>
      7. ''' Die aktuelle Instanz des Cache.
      8. ''' </summary>
      9. ''' <remarks></remarks>
      10. Private Shared myInstance As Cache
      11. ''' <summary>
      12. ''' Das aktuelle IObserver-Objekt.
      13. ''' </summary>
      14. ''' <remarks></remarks>
      15. Public Shared Observer As IObserver
      16. Sub New()
      17. MyBase.New()
      18. End Sub
      19. ''' <summary>
      20. ''' Hängt ein IObserver-Objekt an den Cache.
      21. ''' </summary>
      22. ''' <param name="observer"></param>
      23. ''' <remarks></remarks>
      24. Public Shared Sub Attach(ByRef observer As IObserver)
      25. Cache.Observer = observer
      26. End Sub
      27. ''' <summary>
      28. ''' Holt die aktuelle Instanz oder erstellt eine Neue.
      29. ''' </summary>
      30. ''' <returns></returns>
      31. ''' <remarks></remarks>
      32. Public Shared Function GetInstance() As Cache
      33. If Object.ReferenceEquals(myInstance, Nothing) Then
      34. myInstance = New Cache()
      35. End If
      36. Return myInstance
      37. End Function
      38. 'Diese WriteOnly Eigenschaft wird verwendet, um die Aufrufe vom Client an den Server weiterzuleiten. (Man könnte auch eine Sub verwenden)
      39. Public WriteOnly Property MessageString() As String
      40. Set(value As String)
      41. Observer.Notify(value)
      42. End Set
      43. End Property
      44. End Class


      IObserver.vb
      Spoiler anzeigen

      VB.NET-Quellcode

      1. ''' <summary>
      2. ''' Das IObserver Interface, definiert die Signatur der Methode, die der Server ausführt.
      3. ''' </summary>
      4. ''' <remarks></remarks>
      5. Public Interface IObserver
      6. Sub Notify(ByVal text As String)
      7. End Interface


      MyRemoteableObject.vb
      Spoiler anzeigen

      VB.NET-Quellcode

      1. Imports System.Runtime.Remoting
      2. Imports System.Runtime.Remoting.Channels
      3. ''' <summary>
      4. ''' This Object is used for the Remoting
      5. ''' </summary>
      6. ''' <remarks></remarks>
      7. Public Class MyRemotableObject
      8. Inherits MarshalByRefObject
      9. Public Sub New()
      10. MyBase.New()
      11. End Sub
      12. 'Die Einstellungen der LeaseTime sind die Default-Einstellungen
      13. ''' <summary>
      14. ''' Die Zeit, die ein Singleton Objekt grundsätzlich lebt. (Minuten)
      15. ''' </summary>
      16. ''' <remarks></remarks>
      17. Const TimeToLive As Integer = 5
      18. ''' <summary>
      19. ''' Wann soll abgefragt werden, ob die LeaseTime überschritten wurde. (Sekunden)
      20. ''' </summary>
      21. ''' <remarks></remarks>
      22. Const PollEvery As Integer = 10
      23. ''' <summary>
      24. ''' Um wie viel soll die Zeit, bei einem aufruf durch einen Client, verlängert werden (Minuten)
      25. ''' </summary>
      26. ''' <remarks></remarks>
      27. Const AddForRemoteCall = 2
      28. ''' <summary>
      29. ''' Hiermit kann die Lebensdauer eines Singleton-Objektes bestimmt werden.
      30. ''' </summary>
      31. ''' <returns></returns>
      32. ''' <remarks></remarks>
      33. Public Overrides Function InitializeLifetimeService() As Object
      34. Dim lease As Lifetime.ILease = DirectCast(MyBase.InitializeLifetimeService, Lifetime.ILease)
      35. If lease.CurrentState = Lifetime.LeaseState.Initial Then
      36. lease.InitialLeaseTime = TimeSpan.FromMinutes(5)
      37. lease.SponsorshipTimeout = TimeSpan.FromSeconds(10)
      38. lease.RenewOnCallTime = TimeSpan.FromMinutes(2)
      39. End If
      40. Return lease
      41. End Function
      42. ''' <summary>
      43. ''' SetMessage schreibt den Text des Parameters "message", in die Eigenschaft des Observer-Objekts, das in der aktuellen Instanz von Cache gespeichert wird.
      44. ''' </summary>
      45. ''' <param name="message"></param>
      46. ''' <remarks></remarks>
      47. Public Sub SetMessage(ByVal message As String)
      48. Cache.GetInstance().MessageString = message
      49. End Sub
      50. End Class





      Mit Config-Dateien

      Um Config-Dateien zu verwenden, wird beim Server in der Sub Load folgender Code gelöscht:
      Spoiler anzeigen

      VB.NET-Quellcode

      1. 'Hier wird ein neuer TcpChannel mit dem gewünschten Port erstellt.
      2. Dim channel As TcpChannel = New TcpChannel(PORT)
      3. 'Vor der ersten Verwendung muss der Channel registriert werden
      4. ChannelServices.RegisterChannel(channel, False)
      5. 'Damit die Übertragung funktioniert, muss der Typ, welcher beim Remoting verwendet werden soll, als WellKnownServiceType registriert werden.
      6. 'Man kann diesen auf zwei Arten registrieren. Als Singleton oder SingleCall.
      7. 'SingleCall: Für jeden Aufruf durch einen Client wird eine neue Instanz erstellt. Es werden keine Statusinformationen gespeichert und die Instanz des Objekts
      8. ' wird nach dem Aufruf zerstört und vom GC abgeholt.
      9. RemotingConfiguration.RegisterWellKnownServiceType(GetType(MyRemotableObject), ServiceName, WellKnownObjectMode.SingleCall)
      10. 'Singleton: Ein Instanz, wird für alle Aufrufe durch Clients verwendet. Es können Statusinformationen gespeichert werden.
      11. ' 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,
      12. ' sondern bleibt noch für eine gewisse Zeit erhalten (Lease-Time).
      13. 'RemotingConfiguration.RegisterWellKnownServiceType(GetType(MyRemotableObject), ServiceName, WellKnownObjectMode.Singleton)
      14. 'Fügt sich selbst dem Cache als IObserver-Objekt hinzu.


      Stattdessen fügt man folgenden Code an dessen Stelle ein:
      Spoiler anzeigen

      VB.NET-Quellcode

      1. 'Lädt die Konfiguration aus der Datei "RemotingServer.config"
      2. RemotingConfiguration.Configure("RemotingServer.config", False)


      Beim Client löscht man (ebenfalls in der Sub Load):
      Spoiler anzeigen

      VB.NET-Quellcode

      1. 'Hier wird ein neuer TcpChannel ohne vorgegebenen Port erstellt, um eine Kommunikation zu ermöglichen.
      2. Dim chan As TcpChannel = New TcpChannel()
      3. 'Vor der ersten Verwendung muss der Channel registriert werden
      4. ChannelServices.RegisterChannel(chan, False)
      5. 'Hier wird das RemoteObjekt vom Server geholt und in _remoteObject gespeichert.
      6. Dim ServerNameOrIP As String = "localhost"
      7. Dim Port As Integer = 1234
      8. Dim ServiceName As String = "SendMessage"
      9. _remoteObject = DirectCast(Activator.GetObject(GetType(MyRemotableObject), String.Concat("tcp://", ServerNameOrIP, ":", Port.ToString, "/", ServiceName)), MyRemotableObject)


      und fügt folgenden Code ein:
      Spoiler anzeigen

      VB.NET-Quellcode

      1. 'Lädt die Konfiguration aus der Datei "RemotingServer.config"
      2. RemotingConfiguration.Configure("RemotingClient.config", False)
      3. 'Erstellt das RemoteObjekt mit der geladenen Konfiguration
      4. _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:
      Spoiler anzeigen

      XML-Quellcode

      1. <configuration>
      2. <system.runtime.remoting>
      3. <application name="RemotingServer">
      4. <service>
      5. <wellknown mode="SingleCall" type="RemoteableObject.MyRemotableObject, RemoteableObject" objectUri="SendMessage"/>
      6. </service>
      7. <channels>
      8. <channel ref="tcp" port="1234">
      9. <serverProviders>
      10. <formatter ref="binary" typeFilterLevel="Full"/>
      11. </serverProviders>
      12. <clientProviders>
      13. <formatter ref="binary"/>
      14. </clientProviders>
      15. </channel>
      16. </channels>
      17. </application>
      18. </system.runtime.remoting>
      19. </configuration>


      in die RemotingClient.config kommt folgendes:
      Spoiler anzeigen

      XML-Quellcode

      1. <configuration>
      2. <system.runtime.remoting>
      3. <application name="RemotingClient">
      4. <client>
      5. <wellknown type="RemoteableObject.MyRemotableObject, RemoteableObject" url="tcp://localhost:1234/SendMessage"/>
      6. </client>
      7. <channels>
      8. <channel ref="tcp" port="0">
      9. <serverProviders>
      10. <formatter ref="binary" typeFilterLevel="Full"/>
      11. </serverProviders>
      12. <clientProviders>
      13. <formatter ref="binary"/>
      14. </clientProviders>
      15. </channel>
      16. </channels>
      17. </application>
      18. </system.runtime.remoting>
      19. </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! :D

      Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „BiedermannS“ ()