|
As always, I’ve written the simplest example I could think of to demonstrate .NET Remoting. You can download it from this book’s web site, www.introducingmicrosoft.net, and work along with me. The sample is based on the same time-fetching application that I’ve been using throughout this book, rewritten to use remoting.
A simple remoting example starts here.
The downloaded code consists of three separate solutions because I wanted to emphasize the fact that its parts are separate. As I’ll do throughout this chapter, I’ve called them the client, the host, and the object. I’ve purposely avoided the word server to avoid confusion between the object itself and the application that makes the object available to remote clients, which I call the host. As we’ll see later in this chapter, it’s fairly easy to take a remoting object and plug it into a different host.
I use the words host and object instead of server to avoid confusion.
Writing the object that I want to expose via remoting is quite easy. All I have to do in this simple case is derive my new object class from the base class System.MarshalByRefObject, as shown in the following code:
A remoting object must inherit from System.MarshalByRefObject.
Public Class Class1 Inherits MarshalByRefObject
This base class must be used for all objects that marshal by reference, which I described in the preceding section. The GetTime method, accepting a Boolean parameter that specifies whether or not to return the seconds digits, is the same as it’s been throughout this book. I also added calls to System.Diagnostics.Trace, so you can see in the Microsoft Visual Studio debugger output window when objects are created and when the method is called.
Next I wrote my host application. Unlike in COM, a remoting client cannot cause the host application to be launched on the server machine. It’s the responsibility of the server machine’s administrator to have running whatever remoting host applications he needs. In this case, I wrote a simple Windows Forms application so that it would look nice when you ran it and so that you could see trace strings in the output window of your debugger.
The remoting host is a Windows Forms application.
Setting up my host to perform remoting required three calls to system functions. To access these functions, I need to add to my project a reference to the system DLL System.Runtime.Remoting. My host application code is shown in Listing 10-1.
The remoting host code is shown here.
Listing 10-1: Host channel registration code.
Public Chnl As Runtime.Remoting.Channels.Tcp.TcpServerChannel Private Sub Button1_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Button1.Click ’ Create a new TCP channel on port 1234 Chnl = New Runtime.Remoting.Channels.Tcp.TcpServerChannel(1234) ’ Register channel with runtime services Runtime.Remoting.Channels.ChannelServices.RegisterChannel(Chnl) ’ Register the well-known endpoint Runtime.Remoting.RemotingConfiguration. _ RegisterWellKnownServiceType( _ GetType(SimplestRemotingObjectVB.Class1), _ "SomeUriName", _ Runtime.Remoting.WellKnownObjectMode.Singleton) End Sub
When the user clicks the Register button, I create the channel object on which I want my remoting object to receive its requests. The .NET Framework comes with channels that use HTTP and TCP, and it also defines interfaces that allow you to write your own for any protocol you like, such as Microsoft Message Queuing Service (MSMQ). In this case, I’ve chosen the TCP channel. In its constructor, I have to provide the port on which I want it to accept requests, here an arbitrary number 1234. Next I register the channel with the remoting runtime services, saying that the host program will accept incoming remoting requests on it. Finally the function Runtime.Remoting.RemotingConfiguration.RegisterWellKnownServiceType makes the connection between the remoting runtime and the object to be remoted. I pass it the type of object to create when the client asks. I have to set a reference to the remoting object I discussed in the previous paragraph to get this type. I also pass it the URI name that the client will ask for to get this type of object, here the string SomeUriName. The final parameter is the mode that governs how many objects the remoting runtime infrastructure will allow to be created. Here I specify a singleton mode, which means it will create only one object no matter how many client requests it receives. I’ll discuss the other modes later in this chapter. The channel can service more than one type of object by repeating this call, specifying different object types and URI names. That’s all I have to do on the server side.
The client side is roughly analogous. When the user clicks the Register button, I create a TCP channel and register it, as shown in Listing 10-2. I don’t have to specify the port; it will be passed in the call in which I create the object. I place the registration on a separate button because it must be done only once, whereas creating objects and calling their methods can be done as often as you like.
The client code is shown here.
Listing 10-2: Client-side registration code.
Dim Chnl As Runtime.Remoting.Channels.Tcp.TcpClientChannel Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As _ System.EventArgs) Handles Button1.Click ’ Create a new client channel. Port is automatically assigned Chnl = New Runtime.Remoting.Channels.Tcp.TcpClientChannel ’ Register the channel Runtime.Remoting.Channels.ChannelServices.RegisterChannel(Chnl) ... End Sub
When the user clicks the Get Time button, I execute the code you see in Listing 10-3. I create the object using a call to System.Activator.GetObject, passing the type of the object and the URL at which it can be located. This call talks to the host via the runtime remoting infrastructure, sets up the proxies and stubs, and gives me back a reference to the client-side proxy. I then make the call on it to get the time. Figure 10-3 shows the debugger output window containing the trace statements I put in the object so you can follow its creation and calls. As I did on my server, I had to set a reference on the client to the remoting object so that the client could read its metadata.
Listing 10-3: Client-side object creation and method call.
Private Sub Button2_Click(ByVal sender As System.Object, ByVal e As _ System.EventArgs) Handles Button2.Click ’ Create the object via the Activator, passing the port and URL Dim foo As SimplestRemotingObjectVB.Class1 foo = Activator.GetObject(GetType(SimplestRemotingObjectVB.Class1), _ "tcp://localhost:1234/SomeUriName") ’ Call the method as if the object were local Label1.Text = foo.GetTime(CheckBox1.Checked) End Sub
Figure 10-3: Debugger output window showing trace statements of object creation and call.
To make this sample program work, I had to set references to the component on each side, both client and server. The server needs the actual object code, but the client doesn’t. The client needs only the metadata to know what methods to expect on it. This application as currently shown calls for the client to have a full copy of the server-side code, which you might not want to distribute. My customers report that it’s very easy to rip the code out of the server- side component methods, leaving only the shells, to build a metadata-only assembly that you can give to client developers.
|