Introduction
Searching a lot, I didn't found a really easy remoting sample to show in the courses that I do, so I decide to publish my own article on how to do remoting in an easy way.
First of all, this article is based in VB.NET using .NET Framework 1.1 (Visual Studio 2003).
I will explain how can you do communication with components using Remoting, I will do a brief introduction to remoting now:
At old times, when application development started, we worked with COM, which allowed us to separate UI applications from functional libraries (DLLs). Later we received DCOM that allowed us to communicate between two different components in different computers. DCOM is a very proprietary RPC-based communication protocol for COM-based distributed component architectures. Even though DCOM allows us to create scalable and reliable architectures in the Intranet environment, there are a lot of problems with DCOM when you try to integrate it with different platforms and technologies in an Internet environment.
Now with the new .NET technologies, we receive WebServices and .NET Remoting. The web services allow us to communicate between different platforms and technologies, using standards HTTP and SOAP, that are very good, and very easy to implement. But web services lack some points when you want to work with State Management with .NET. Remoting provides support for both stateful and stateless environments through Singleton and SingleCall objects.
Talking about Type Systems Web services support only the data types defined in the XSD type system, limiting the number of objects that can be serialized, while .NET Remoting uses binary communication, providing support for rich type system. Of course that Web Services has some better points than .NET Remoting, as for example the Interoperability, providing support to interoperability across platforms (ideal for heterogeneous environments), while .NET Remoting requires the client be built using .NET.
Though both the .NET Remoting infrastructure and ASP.NET Web services can enable cross-process communication, each is designed to benefit a different target audience.
For applications that require interoperability and must function over public networks, Web services are probably the best bet. For those that require communications with other .NET components and where performance is a key priority, .NET Remoting is the best choice.
Now let's talk about our solution!! Because Remoting object will not be active by default, we need to create an Application Host Domain (this is how MS calls them), in order to load our exposed library in memory, and expose it as a Remote object. Our solution will consist of four projects, as follows:
- IRemoteLib: This will be a sample interface, that will be used by the client and the server part.
- LibraryExposed: is the IRemoteLib implementation and it will inherit from
MarshalByRefObject
, here are all the methods used by the client.
- EasyInvoke: This is a WinForm application (it's our Application Host Domain implementation) that will load LibraryExposed and will expose it to our client. We will instance our library as Server-activated object as Singleton activation (the difference in a singleton and single call lies in lifetime management. While single-call objects are stateless in nature, singletons are stateful objects, meaning that they can be used to retain state across multiple method calls. Singleton types never have more than one instance at any one time, sharing the same instance between all clients, while SingleCall types always have one instance per client request).
- ClientApplication: This is a WinForm application that will call the remote object.
Using the code
Now let's start with source code. First of all, we need to create our Interface (IRemoteLib).
Public Interface IRemoteLib
Event LibCalled(ByVal message As String)
Sub HelloWorldMethod()
Function HelloWorld() As String
End Interface
Now let's continue with the implementation (LibraryExposed).
Public Class RemoteLib
Inherits MarshalByRefObject
Implements IRemoteLib
Public Event LibCalled(ByVal Message As String) Implements IRemoteLib.LibCalled
Public Function HelloWorld() As String Implements IRemoteLib.HelloWorld
RaiseEvent LibCalled("Call HelloWorld ")
Return "Hello World"
End Function
Public Sub HelloWorldMethod() Implements IRemoteLib.HelloWorldMethod
RaiseEvent LibCalled("Call HelloWorldMethod ")
End Sub
End Class
As you see everything is very easy, just pay attention at Inherits MarshalByRefObject
. This is where we specified that this will be a remoting object. We do it this way, because we want to specify a singleton, and we want that the client access the remote object using a proxy class.
Now let's implement the EasyInvoke (because it's a WinForm, you need first to create a WinForm, just with a ListBox
docked at it). This is the LibraryExposed remote object activation:
Dim objServer As IRemoteLib
Private Sub Form1_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
Try
Dim ServiceChannel As IChannel
Dim serverProv As BinaryServerFormatterSinkProvider = _
New BinaryServerFormatterSinkProvider
Dim clientProv As BinaryClientFormatterSinkProvider = _
New BinaryClientFormatterSinkProvider
Dim Props As IDictionary = New Hashtable
Dim IpInjProvider As New IpInjectorSinkProvider
serverProv.TypeFilterLevel = _
System.Runtime.Serialization.Formatters.TypeFilterLevel.Full
Props("port") = "8000"
Props("name") = "EasyInvoke"
ServiceChannel = New TcpChannel(Props, clientProv, serverProv)
ChannelServices.RegisterChannel(ServiceChannel)
RemotingConfiguration.RegisterWellKnownServiceType( _
GetType(RemoteLib), _
"EasyInvoke", WellKnownObjectMode.Singleton)
lstConsole.Items.Add("RemoteLib Started as remoting")
Init()
Catch ex As Exception
lstConsole.Items.Add("Error:" + ex.Message)
End Try
End Sub
This was how to register our LibraryExposed as a remote object. Now let's finish our server as an enhancement that will allow us to retrieve which client is connecting and which method is using our remote object.
Private Sub Init()
Dim server As IRemoteLib
Dim Channel As System.Runtime.Remoting.Channels.tcp.TcpChannel
Dim serverProv As BinaryServerFormatterSinkProvider = _
New BinaryServerFormatterSinkProvider
Dim clientProv As BinaryClientFormatterSinkProvider = _
New BinaryClientFormatterSinkProvider
Dim props As IDictionary = New Hashtable
serverProv.TypeFilterLevel = _
System.Runtime.Serialization.Formatters.TypeFilterLevel.Full
props("port") = 0
Channel = New System.Runtime.Remoting.Channels.tcp.TcpChannel(props, _
clientProv, serverProv)
ChannelServices.RegisterChannel(Channel)
Try
server = CType(Activator.GetObject(GetType(IRemoteLib), _
"tcp://localhost:8000/EasyInvoke"), IRemoteLib)
If server Is Nothing Then
MsgBox("server not found or bad initialized!")
End If
server.HelloWorldMethod()
Catch ex As Exception
Debug.WriteLine(ex.Message)
End Try
AddHandler server.LibCalled, AddressOf ReceiveLibCall
End Sub
<System.Runtime.Remoting.Messaging.OneWay()> _
Public Sub ReceiveLibCall(ByVal message As String)
lstConsole.Items.Add(message)
End Sub
Here we add an enhancement to our server side, just to listen the events from the remote object to display which method is in use by the client at our ListBox
(lstConsole
).
And now the ClientApplication implementation:
You should create a WinForm with three buttons, one connect button (butConnect
), one HelloWorldSub button (butHelloWorldSub
), and one HelloworldFunc button (butHelloWorldFunc
) and add this code:
Private server As IRemoteLib
Private Channel As System.Runtime.Remoting.Channels.tcp.TcpChannel
Private serverProv As BinaryServerFormatterSinkProvider
Private clientProv As BinaryClientFormatterSinkProvider
Private props As IDictionary = New Hashtable
Private Sub SetChannel()
serverProv = New BinaryServerFormatterSinkProvider
clientProv = New BinaryClientFormatterSinkProvider
serverProv.TypeFilterLevel = _
System.Runtime.Serialization.Formatters.TypeFilterLevel.Full
props("port") = 0
Channel = New TcpChannel(props, clientProv, serverProv)
End Sub
Private Sub Init()
SetChannel()
ChannelServices.RegisterChannel(Channel)
Try
server = CType(Activator.GetObject(GetType(IRemoteLib), _
"tcp://localhost:8000/EasyInvoke"), IRemoteLib)
If server Is Nothing Then
MsgBox("server not found!")
End If
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
And now implement the buttons click:
Private Sub butHelloWorldSub_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles butHelloWorldSub.Click
Try
server.HelloWorldMethod()
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
Private Sub butHelloWorldFunc_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles butHelloWorldFunc.Click
Try
MsgBox(server.HelloWorld)
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
Private Sub butConnect_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles butConnect.Click
If butConnect.Text = "Connect" Then
Init()
butConnect.Text = "Disconnect"
butHelloWorldFunc.Enabled = True
butHelloWorldSub.Enabled = True
Else
ChannelServices.UnregisterChannel(Channel)
butHelloWorldFunc.Enabled = False
butHelloWorldSub.Enabled = False
server = Nothing
Channel = Nothing
serverProv = Nothing
clientProv = Nothing
butConnect.Text = "Connect"
End If
End Sub
That's all folks! I hope you enjoy it!