Implementing Remoting


When you implement an application using remoting, there are three key components to the application:

Client

The application calling the server

Server Library

The DLL containing the objects to be called by the client

Host

The application running on the server that hosts remoting and the Server Library

Basically, you create your server-side objects in a Visual Basic class library project. Then, you expose the classes in that DLL from your server-side remoting host application. With the objects exposed on the server, you can then create client applications that call the objects in the Server Library DLL. You might also have some other optional components to support various scenarios:

Interface

A DLL containing interfaces that are implemented by the objects in the Server Library

Proxy

A DLL containing generated proxy code based on the objects in the Server Library

Shared Library

A DLL containing serializable objects that must be available to both the Server Library and the client

Each of these is discussed in detail as it is used later in the chapter. For now, it’s time to get into some code and see how remoting works.

A Simple Example

To begin, you will create a simple remoting application that consists of a library DLL containing the server-side code, a remoting host application, and a client to call the library DLL on the server.

Keep in mind that both the host and the client need access to the type information that describes the classes in the library DLL. The type information includes the name of the classes in the DLL and the methods exposed by those classes.

The host needs the information because it will be exposing the library DLL to clients via remoting. The client needs the information in order to know which objects to create and what methods are available on those objects.

You know that the library DLL will be on the server, so it is easy enough for the host application to just reference the DLL to get the type information. The client is a bit trickier though, as the library DLL won’t necessarily be on the client machine. You have three options for getting the type information to the client:

Reference the library DLL

This is the simplest approach, as the client just references the DLL directly and therefore has all the type information. The drawback is that the DLL must be installed on the client along with the client application.

Use an interface DLL

This approach is more complex. The classes in the library DLL must implement formal interfaces as defined in this interface DLL. The client can then reference just the interface DLL, so the library DLL doesn’t need to be installed on the client machine. The way the client invokes the server is different when using interfaces.

Generate a proxy DLL

This approach is of moderate complexity. The server must expose the objects via HTTP, so you can run the soapsuds.exe command-line utility. The utility creates an assembly containing the type information for the library DLL classes exposed by the server. The client then references this proxy assembly, rather than the library DLL.

You’ll implement all three options in this chapter, starting with the simplest - referencing the library DLL directly from the client application.

Library DLL

To begin, create the library DLL. This is just a regular Class Library project, so open Visual Studio .NET (Visual Studio) and create a new class library named SimpleLibrary. Remove Class1.vb and add a new class named Calculator. Because you’re creating a well-known remoting object, it must inherit from MarshalByRefObject:

 Public Class Calculator   Inherits MarshalByRefObject End Class

That’s really all there is to it. At this point, the Calculator class is ready to be exposed from a server via remoting. Of course, you need to add some methods that clients can call.

Any and all Public methods written in the Calculator class are available to clients. How you design the methods depends entirely on whether you plan to expose this class as SingleCall, Singleton, or Activated. For SingleCall, you know that an instance of Calculator will be created for each method call, so there’s absolutely no point in using any class-level variables. After all, they’ll be destroyed along with the object when each method call is complete.

Nor can you have the client call a sequence of methods on your object. Each method call gets its own object, and is entirely isolated from any previous or subsequent method calls. In short, each method must stand alone.

For illustration purposes, you need to prove that the server-side code is running in a different process from the client code. The easiest way to prove this is to return the thread ID where the code is running. You can compare this thread ID to the thread ID of the client process. If they are different, then you can be sure that the server-side code really is running on the server (or at least in another process on your machine).

Add the following method:

  Public Function GetThreadID() As Integer   Return Threading.Thread.CurrentThread.ManagedThreadId End Function 

You can add other Public methods as well if you’d like:

  Public Function Add(ByVal a As Integer, ByVal b As Integer) As Integer   Return a + b  End Function 

Because this is a calculator class, it seems appropriate that it should do some calculations. At this point, you have a simple but functional Calculator class. Build the solution to create the DLL. Your remoting host application will use this DLL to provide the calculator functionality to clients.

Host Application

With the server-side library complete, you can create a remoting host. It is recommended that you use IIS as a remoting host, but it is possible to create a custom host as well. You’ll use IIS later in the chapter, but for now let’s see how you can create a custom host in a Console Application for testing.

Tip 

Most custom hosts are created as a Windows Service so the host can run on the server even when no user is logged into the machine. However, for testing purposes, a console application is easier to create and run.

The advantage to a custom host is that you can host a remoting server on any machine that supports the .NET Framework. This includes Windows 98 and later. If you use IIS as a host, you can only host on Windows 2000 and later, which is a bit more restrictive.

The drawback to a custom host is that it isn’t as robust and capable as IIS - at least, not without a lot of work. For this chapter’s example, you won’t attempt to make your host as powerful as IIS. You’ll just stick with the basic process of creating a custom host.

Setting Up the Project

Create a new solution in Visual Studio, with a console application named SimpleServer. The remoting host will be interacting with remoting, so reference the appropriate framework DLL. Use the Add Reference dialog box to add a reference to System.Runtime.Remoting, as shown in Figure 27-3.

image from book
Figure 27-3

Then, in Module1, import the appropriate namespace:

  Imports System.Runtime.Remoting 

At this point, you can configure and use remoting, but before you do that, you need access to the DLL containing the classes you plan to expose via remoting - in this case, SimpleLibrary.dll.

Referencing the Library DLL

There are two ways to configure remoting: via a configuration file or via code. If you opt for the configuration file approach, then the only requirement is that SimpleLibrary.dll be in the same directory as your host application. You don’t even need to reference SimpleLibrary.dll from the host. If you opt to configure remoting via code, then your host must reference SimpleLibrary.dll.

Even if you go with the configuration file approach, referencing SimpleLibrary.dll from the host project enables Visual Studio to automatically keep the DLL updated in your project directory, and any setup project you might create will automatically include SimpleLibrary.dll. In general, it is a good idea to reference the library DLL from the host, and that’s what you’ll do here.

Add a reference to SimpleLibrary.dll by clicking the Browse button in the Add References dialog box and navigating to the SimpleLibrary\bin\Release directory, as shown in Figure 27-4. Note that if you are running in debug mode, then you will find the DLL in the Debug folder, rather than the Release folder.

image from book
Figure 27-4

All that remains now is to configure remoting.

Configuring Remoting

The typical way to do this is with a configuration file. Open the app.config file (add this file to your project if it isn’t already present) in the SimpleServer project. In this config file you’ll add a section to configure remoting. Remember that XML is case sensitive, so the slightest typo here will prevent remoting from being properly configured:

  <?xml version="1.0" encoding="utf-8" ?> <configuration>   <system.runtime.remoting>     <application>       <!-- The following section defines the classes you're             exposing to clients from this host. -->       <service>         <wellknown mode="SingleCall"              objectUri="Calculator.rem"              type="SimpleLibrary.Calculator, SimpleLibrary" />       </service>       <channels>         <channel ref="tcp" port="49341" />       </channels>     </application>   </system.runtime.remoting> </configuration 

Notice that all configuration is within the <system.runtime.remoting> element, and then within an <application> element. The real work happens first inside the <service> element. The <service> element tells remoting that you’re configuring server-side components. It is within this block that you define the classes you want to make available to clients. You can define both wellknown and Activated classes here. In this case, you’re defining a wellknown class:

 <wellknown mode="SingleCall"      objectUri="Calculator.rem"      type="SimpleLibrary.Calculator, SimpleLibrary" />

The mode will be either SingleCall or Singleton, as discussed earlier in the chapter.

The objectUri is the “end part” of the URL that clients will use to reach your server. You’ll revisit this in a moment, but the following shows basically how it fits (depending on whether you’re using the TCP or HTTP protocol):

 tcp://localhost:49341/Calculator.rem 

or

 http://localhost:49341/Calculator.rem 

The .rem extension on the objectUri is important. It indicates that remoting should handle the client request, and is used by the networking infrastructure to route the request to the right location. You can optionally use the .soap extension to get the same result. The .rem and .soap extensions are totally equivalent.

Finally, the type defines the full type name and assembly where the actual class can be found. Remoting uses this information to dynamically load the assembly and create the object when requested by a client. You can have many <wellknown> blocks here to expose all the server-side classes you want to make available to clients.

The other key configuration block is where you specify which remoting channel (protocol) you want to use. You can choose between the TCP and HTTP channels:

TCP

Slightly faster than HTTP, but less stable and not recommended.

HTTP

Slightly slower than TCP, but more stable. Recommended.

Since you’ll look at the HTTP channel later, you’re using the TCP channel now. Either way, you need to specify the IP port number on which you’ll be listening for client requests. When choosing a port for a server, you should keep the following port ranges in mind:

  • 01023 - Well-known ports reserved for specific applications such as Web servers, mail servers, and so on

  • 102449151 - Registered ports that are reserved for various widely used protocols such as DirectPlay

  • 4915265535 - Intended for dynamic or private use, such as for applications that might be performing remoting with .NET

You set remoting to use a TCP channel, listening on port 49341:

 <channels>   <channel ref="tcp" port="49341" /> </channels>

With the .config file created, the only thing remaining is to tell remoting to configure itself based on this information. To do this, add the following code to Sub Main:

  Sub Main()   RemotingConfiguration.Configure( _     AppDomain.CurrentDomain.SetupInformation.ConfigurationFile, True)   Console.Write("Press <enter> to exit")   Console.Read()  End Sub 

The Console.Write and Console.Read statements ensure that the application remains running until you are ready for it to terminate. The line that actually configures remoting is as follows:

 RemotingConfiguration.Configure( _   AppDomain.CurrentDomain.SetupInformation.ConfigurationFile)

You are calling the Configure method, which tells remoting to read a .config file and process the <system.runtime.remoting> element in that file. You want it to use your application configuration file, so you pass that path as a parameter. Fortunately, you can get the path from your AppDomain object, so you don’t have to hard-code the filename.

Configuring Remoting via Code

Your other option is to configure the remoting host via code. To do this you’d write different code in Sub Main:

 Sub Main()    RemotingConfiguration.RegisterWellKnownServiceType( _     GetType(SimpleLibrary.Calculator), _     "Calculator.rem", _     WellKnownObjectMode.SingleCall)   System.Runtime.Remoting.Channels.ChannelServices.RegisterChannel( _     New System.Runtime.Remoting.Channels.Tcp.TcpServerChannel(49341), True)   Console.Write("Press <enter> to exit")   Console.Read() End Sub

Here, you’re providing the exact same information as you did in the .config file, only via code. You call RegisterWellKnownServiceType, passing the mode, objectUri, and type data, just as you did in the .config file. Then, you call RegisterChannel, passing a new instance of the TcpServerChannel configured to use the port you chose earlier.

The result is the same as using the .config file. Most server applications use a .config file to configure remoting because it enables you to change things such as the channel and port without having to recompile the host application.

Build the solution. At this point your host is ready to run. Open a Command Prompt window, navigate to the bin directory, and run SimpleServer.exe.

The Client Application

The final piece of the puzzle is creating a client application that calls the server.

Setting Up the Project

This example creates a new Visual Studio solution with a Windows Application named SimpleClient. As discussed earlier, the client needs access to the type information for the classes it wants to call on the server. The easiest way to get this type information is to have it reference SimpleLibrary.dll. Because you’ll be configuring remoting, you also need to reference the remoting DLL. Then import the remoting namespace in Form1:

  Imports System.Runtime.Remoting 

Now you can write code to interact with the Calculator class. Add controls to the form as shown in Figure 27-5.

image from book
Figure 27-5

Name the controls as follows (in order): ConfigureButton, CodeConfigureButton, LocalThreadButton, LocalThread, RemoteThreadButton, RemoteThread. First, write the code to get the thread ID values for each object:

  Private Sub LocalThreadButton_Click( _   ByVal sender As System.Object, ByVal e As System.EventArgs) _   Handles LocalThreadButton.Click   LocalThread.Text = CStr(Threading.Thread.CurrentThread.ManagedThreadId) End Sub Private Sub RemoteThreadButton_Click( _     ByVal sender As System.Object, ByVal e As System.EventArgs) _     Handles RemoteThreadButton.Click  Dim calc As New SimpleLibrary.Calculator  RemoteThread.Text = CStr(calc.GetThreadID) End Sub 

Displaying the thread ID of the local process is easily accomplished. More interesting, though, is that your code to interact with the Calculator class doesn’t look special in any way. Where’s the remoting code?

Using the concept of location transparency, it is possible to write “normal” code that interacts with an object whether it is running locally or remotely. This is an important and desirable trait for distributed technologies, and remoting supports the concept. Looking at the preceding code, you can’t tell whether the Calculator object is local or remoting; its location is transparent.

All that remains is to configure remoting so that it knows that the Calculator object should, in fact, be created remotely. As with the server, you can configure clients either via a config file or through code.

Before you configure remoting, keep in mind that if remoting is not configured before the first usage of SimpleLibrary.Calculator, then the Calculator object will be created locally. If that happens, then configuring remoting won’t help, and you’ll never create remote Calculator objects.

To prevent this from happening, make sure you can’t interact with the class until after remoting is configured. Typically, this is done by configuring remoting as the application starts up, either in Sub Main or in the first form’s Load event, but in this case you’re going to configure remoting behind some buttons, so a different approach is required.

In Form_Load, add the following code:

  Private Sub Form1_Load( _   ByVal sender As System.Object, ByVal e As System.EventArgs) _   Handles MyBase.Load   RemoteThreadButton.Enabled = False End Sub 

This prevents you from requesting the remote thread. You won’t enable this button until after remoting has been configured either through the config file or code.

Configuring Remoting

To configure remoting via a config file, you first need to add a config file to the project. Select Project image from book Add New Item to add an Application Configuration file. Be sure to keep the default name of App.config. In this file, add the following code:

 <?xml version="1.0" encoding="utf-8" ?> <configuration>   <system.runtime.remoting>     <application>       <!-- The following section defines the classes you're             getting from the remote host. -->       <client>       <wellknown mode="SingleCall"            type="SimpleLibrary.Calculator, SimpleLibrary"           url="tcp://localhost:49341/Calculator.rem" />       </client>     </application>   </system.runtime.remoting> </configuration>

In this case, you’re using the <client> element, telling remoting that you’re configuring a client. Within the <client> block, you define the classes that should be run on a remote server, both wellknown and Activated. In this case you have a wellknown class:

 <wellknown       type=”SimpleLibrary.Calculator, SimpleLibrary”      url=”tcp://localhost:49341/Calculator.rem” />

On the client, you only need to provide remoting with two bits of information: the class and assembly that should be run remotely. This is done with the type attribute, which specifies the full type name and assembly name for the class, just as you did on the server. You also need to provide the full URL for the class on the server.

You defined this URL when you created the server, though it might not have been clear at the time. When you defined the class for remoting on the server, you specified an objectUri value (Calculator.rem). You also specified the channel (TCP) and port (49341) on which the server will listen for client requests. Combined with the server name itself, you have a URL:

 tcp://localhost:49341/Calculator.rem

The channel is tcp://, the server name is localhost (or whatever the server name might be), the port is 49341, and the object’s URI is Calculator.rem. This is the unique address of your SimpleLibrary.Calculator class on the remote server.

As with the server configuration, you might have multiple elements in the .config file, one for each server-side object you wish to use. These can be a mix of <wellknown> and <activated> elements. With the configuration set up, you just need to tell remoting to read the file. You do this behind the ConfigureButton control:

  Private Sub ConfigureButton_Click( _   ByVal sender As System.Object, ByVal e As System.EventArgs) _   Handles ConfigureButton.Click   RemotingConfiguration.Configure( _     AppDomain.CurrentDomain.SetupInformation.ConfigurationFile, True)   ConfigureButton.Enabled = False   CodeConfigureButton.Enabled = False   RemoteThreadButton.Enabled = True End Sub 

Once remoting is configured in an application, you can’t configure it again, so you’re disabling the two configuration buttons. You’re also enabling the button to retrieve the remote thread ID. Now that remoting has been configured, it is safe to interact with SimpleLibrary.Calculator. The line of code that configures remoting is the same as it was in the server:

 RemotingConfiguration.Configure( _   AppDomain.CurrentDomain.SetupInformation.ConfigurationFile)

Again, you’re telling remoting to read your application configuration file to find the <system.run time.remoting> element and process it.

Configuring Remoting via Code

Another option for configuring remoting is to do it via code. You must provide the same information in your code as you did in the .config file. Put this behind the CodeConfigureButton control:

  Private Sub CodeConfigureButton_Click( _   ByVal sender As System.Object, ByVal e As System.EventArgs) _   Handles CodeConfigureButton.Click   RemotingConfiguration.RegisterWellKnownClientType( _     GetType(SimpleLibrary.Calculator), "tcp://localhost:49341/Calculator.rem")   ConfigureButton.Enabled = False   CodeConfigureButton.Enabled = False   RemoteThreadButton.Enabled = True End Sub 

The RegisterWellKnownClientType method requires that you specify the type of the class to be run remotely - in this case, SimpleLibrary.Calculator. You also need to provide the URL for the class on the remote server, just as you did in the .config file.

Regardless of whether you do the configuration via code or the .config file, the result is that the .NET runtime now knows that any attempt to create a SimpleLibrary.Calculator object should be routed through remoting, so the object will be created on the server.

Compile and run the application. Try configuring remoting both ways. In both cases, you should discover that the local thread ID and the remote thread ID are different, proving that the Calculator code is running on the server, not locally in the Windows application (see Figure 27-6).

image from book
Figure 27-6

Of course, your specific thread ID values will vary from those shown here. The important point is that they are different from each other, establishing that the local code and remote code are running in different places.

Using IIS as a Remoting Host

You’ve seen how to create a very basic custom host. In most production environments, however, such a basic host isn’t directly useful. You typically need to create a Windows Service, add management and logging facilities, implement security, and so forth. Alternately, you could just use IIS as the host and get all those things automatically. It is often better to use IIS as a remoting host than to try to create your own.

Creating the Host

Using IIS as a host is a straightforward exercise. First, create a Web project by creating a new solution in Visual Studio with an Empty Web Site named SimpleHost, as shown in Figure 27-7.

image from book
Figure 27-7

When you click OK, Visual Studio will properly create and configure the virtual root on your server. The next task is to ensure that the SimpleLibrary.dll is in the bin directory under the virtual root. You could copy the DLL there by hand, but it is often easier to simply add a reference to the DLL from the website. This enables Visual Studio to automatically copy the DLL to the right location, and it has the added benefit that if you create a deployment project, the DLL is automatically included as part of the setup.

Add a reference to SimpleLibrary.dll using the Add References dialog box, as you did previously in the SimpleServer and SimpleClient projects. This way, Visual Studio will ensure that the DLL is available as needed.

All that remains now is to configure remoting. The only thing you need to do within an IIS host is add the <system.runtime.remoting> section to the web.config file. Remoting is automatically configured based on web.config by ASP.NET.

Select Project image from book Add New Item to add a Web Configuration file. Be sure to use the default name of web.config. This adds a web.config file to the project with a series of default settings. You may opt to change some of these settings for your environment. In particular, these settings enable you to control security options and so forth.

More important, add the remoting configuration to the file:

 <?xml version="1.0" encoding="utf-8" ?> <configuration>   <system.runtime.remoting>     <application>       <!-- The following section defines the classes you're             exposing to clients from this host. -->       <service>         <wellknown mode="SingleCall"              objectUri="Calculator.rem"              type="SimpleLibrary.Calculator, SimpleLibrary" />       </service>   </application> </system.runtime.remoting> 

An IIS host can only support the HTTP channel. In addition, the port on which the host listens is defined by IIS, not by your configuration file. Therefore, all you need to do here is define the classes you want to expose to clients. Do this within the <service> element, just like with a custom host. Again, you use a <wellknown> element to define your class:

 <wellknown mode="SingleCall"      objectUri="Calculator.rem"      type="SimpleLibrary.Calculator, SimpleLibrary" />

The <wellknown> element shown here is the exact same definition used with the custom host, and you’ll get the same result. The primary difference between your custom host and the IIS host is that IIS cannot use the TCP channel; it only uses the HTTP channel. This means that the URL for your server-side class is different:

  • http://localhost/SimpleHost/Calculator.rem

The channel defines the protocol, which is http://. The server name is localhost (or whatever your server name might be). The virtual root within IIS is SimpleHost, named just as it is with any Web project. Finally, the objectUri value for your class (Calculator.rem) rounds out the URL.

Again, the .rem extension is important. This extension (or the equivalent .soap extension) tells IIS to route the client request to ASP.NET, and it tells ASP.NET to route the request to remoting so it can be properly handled by invoking your Calculator class.

At this point, the remoting host is done and ready to go. It is using the HTTP protocol, so you can test it with the browser by navigating to the following URL:

  • http://localhost/SimpleHost/Calculator.rem?wsdl

This should return an XML description of the host service and all the classes exposed from the host.

Updating the Client Application

With a new host set up, you can change the client application to use this IIS host instead of the custom host. To do so, change the URL for the object when you configure remoting. If you’re using the .config file to configure remoting, you make the following change:

 <?xml version="1.0" encoding="utf-8" ?> <configuration>   <system.runtime.remoting>     <application>       <!− the following section defines the classes you're             getting from the remote host −>       <client>         <wellknown               type="SimpleLibrary.Calculator, SimpleLibrary"              url="http://localhost/SimpleHost/Calculator.rem" />        </client>     </application>   </system.runtime.remoting> </configuration>

After making this change to App.config, be sure to rebuild the project so Visual Studio copies the new .config file to the bin directory and renames it to SimpleClient.exe.config.

When configuring remoting via code, change the code as follows:

 Private Sub CodeConfigureButton_Click( _   ByVal sender As System.Object, ByVal e As System.EventArgs) _   Handles CodeConfigureButton.Click    RemotingConfiguration.RegisterWellKnownClientType( _     GetType(SimpleLibrary.Calculator), _     "http://localhost/SimpleHost/Calculator.rem")   ConfigureButton.Enabled = False   CodeConfigureButton.Enabled = False   RemoteThreadButton.Enabled = True  End Sub

In either case, you’re simply changing the URL, so remoting now routes your calls to the IIS host instead of your custom host.

Using the Binary Formatter in IIS

One thing to note about using IIS as a host is that it always uses the HTTP channel. The HTTP channel defaults to using the SoapFormatter instead of the BinaryFormatter to encode the data sent across the network. While SOAP is a fine format, it is extremely verbose. The BinaryFormatter generates about one-third the number of bytes as the SoapFormatter to send the same data.

As stated, the SoapFormatter class is the default formatter when using an HTTP channel with .NET remoting, and this class serializes any objects that it receives into a SOAP 1.1compliant text format. The HTTP channel uses the SoapFormatter to serialize the objects that it sends through the channel. After the object is received and serialized into XML, this formatter also adds any appropriate SOAP headers to the message before it is sent through the channel.

Besides the SoapFormatter, the BinaryFormatter class is typically used when sending an object through a TCP network protocol. When objects are sent using the BinaryFormatter, these objects are more compact and therefore require less network utilization.

For production code, it’s good practice to use the BinaryFormatter to reduce the amount of data sent across the network and to improve performance. The formatter is controlled by the client, so you need to update the client configuration of remoting.

You can change the .config file as follows:

       <?xml version="1.0" encoding="utf-8" ?>       <configuration>         <system.runtime.remoting>           <application>             <!− the following section defines the classes you're                   getting from the remote host −>             <client>               <wellknown                    type="SimpleLibrary.Calculator, SimpleLibrary"                   url="http://localhost/SimpleHost/Calculator.rem" />             </client>       <!-- use the binary formatter over the              http channel >       <channels>          <channel ref="http">           <clientProviders>             <formatter ref="binary" />           </clientProviders>         </channel>       </channels>     </application>   </system.runtime.remoting> </configuration>

The highlighted XML configures remoting, so when it initializes the HTTP channel, it does so with a BinaryFormatter instead of the default SoapFormatter. To do the equivalent to the XML configuration in code, you import a couple of namespaces into Form1:

  Imports System.Runtime.Remoting.Channels Imports System.Runtime.Remoting.Channels.Http 

This also requires that the SimpleClient project reference the System.Runtime.Remoting.dll assembly. Do this using the Add References dialog as you did earlier in the SimpleLibrary project. Next, add the following when configuring remoting:

 Private Sub CodeConfigureButton_Click( _   ByVal sender As System.Object, ByVal e As System.EventArgs) _   Handles CodeConfigureButton.Click   RemotingConfiguration.RegisterWellKnownClientType( _     GetType(SimpleLibrary.Calculator), _     "http://localhost/SimpleHost/Calculator.rem")    ' Use the binary formatter with the   ' HTTP channel.   Dim clientFormatter As New BinaryClientFormatterSinkProvider   Dim channel As New HttpChannel(Nothing, clientFormatter, Nothing)   ChannelServices.RegisterChannel(channel)   ConfigureButton.Enabled = False   CodeConfigureButton.Enabled = False   RemoteThreadButton.Enabled = True End Sub

As with the .config file approach, you’re specifically creating the HttpChannel object, specifying that it should use a BinaryFormatter, rather than the default.

At this point, you’ve explored the basic use of remoting. You’ve created a library DLL, a client that uses the library DLL, and two different types of remoting hosts, so the library DLL can run on the server.

There are many other facets of remoting to explore, more than what fits into this single chapter. The rest of the chapter explores some of the more common features that you might encounter or use in your applications. You’ll have to take them pretty fast, but the complete code for each is available in the code download for the book.

Using Activator.GetObject

In your simple client you configured remoting so that all attempts to use SimpleLibrary.Calculator were automatically routed to a specific server via remoting. If you want more control and flexibility, you can take a different approach by using the System.Activator class. The full code for this example is in the ActivatorClient project.

Instead of configuring remoting to always know where to find the remote class, you can specify it as you create the remote object. Because you won’t be configuring remoting, you don’t need a reference to System.Runtime.Remoting.dll, nor do you need any of the remoting configuration code you had in the client to this point.

Simply replace the use of the New keyword with a call to Activator.GetObject. To use the custom host, you’d use the following code to retrieve the remote thread ID:

 Private Sub RemoteThreadButton_Click( _   ByVal sender As System.Object, ByVal e As System.EventArgs) _   Handles RemoteThreadButton.Click   Dim calc As SimpleLibrary.Calculator   calc = CType(Activator.GetObject( _     GetType(SimpleLibrary.Calculator), _     "tcp://localhost:49341/Calculator.rem"), _     SimpleLibrary.Calculator)   RemoteThread.Text = CStr(calc.GetThreadID) End Sub

For this to work, the SimpleServer application must be running before the RemoteThread button is clicked. The Activator.GetObject method accepts the type of object to create (SimpleLibrary.Calculator) and the URL where the object can be found. To use the IIS host, you’d change the URL as follows:

 calc = CType(Activator.GetObject( _   GetType(SimpleLibrary.Calculator), _   "http://localhost/SimpleHost/Calculator.rem"), _   SimpleLibrary.Calculator)

With this approach you lose location transparency, as it is obvious that you’re using a remote object; but you gain explicit control over where the remote object is created. This can be useful when you want to programmatically control the URL on a per-call basis.

Interface-Based Design

One drawback to the simple implementation used thus far is that the library DLL (SimpleLibrary.dll) must be installed on the client machine. Sometimes this isn’t desirable because you don’t want clients to have access to the server-side code. There are two solutions to this problem: using an interface DLL or using a generated proxy.

Interface DLL

To use this approach, you create a new DLL that contains interface definitions for your server-side classes and their methods. For instance, in the SimpleInterface project, you have this interface defined:

  Public Interface ICalculator   Function GetThreadID() As Integer   Function Add(ByVal a As Integer, ByVal b As Integer) As Integer End Interface 

This interface defines the methods on your Calculator class. You need to update the Calculator class to implement this interface. The SimpleLibrary project must reference the SimpleInterface DLL; then you can do the following in your Calculator class:

 Public Class Calculator   Inherits MarshalByRefObject  Implements SimpleInterface.ICalculator  Public Function GetThreadID() As Integer _    Implements SimpleInterface.ICalculator.GetThreadID    Return AppDomain.GetCurrentThreadId  End Function  Public Function Add(ByVal a As Integer, ByVal b As Integer) As Integer _    Implements SimpleInterface.ICalculator.Add       Return a + b   End Function End Class

At this point, the SimpleLibrary.Calculator class can be invoked either directly or via the ICalculator interface. Be sure to rebuild the custom and IIS host projects so that the new SimpleLibrary and SimpleInterface DLLs are both copies to the host directories. Note that because SimpleLibrary.Calculator is still available natively, your existing client applications (SimpleClient and ActivatorClient) will continue to run just fine.

Updating the Client Application

The InterfaceClient project only references SimpleInterface.dll, not SimpleLibrary.dll, so the client machine doesn’t need to install SimpleLibrary.dll for the client to run, meaning the client has no access to the actual server-side code.

Because you don’t have access to the types in SimpleLibrary, you can’t use them in your code. The only types you can use come from SimpleInterface. This means that your code to retrieve the remote thread ID is a bit different. To use the custom host, do the following:

 Private Sub RemoteThreadButton_Click( _   ByVal sender As System.Object, ByVal e As System.EventArgs) _   Handles RemoteThreadButton.Click    Dim calc As SimpleInterface.ICalculator   calc = CType(Activator.GetObject( _     GetType(SimpleInterface.ICalculator), _     "tcp://localhost:49341/Calculator.rem"), _     SimpleInterface.ICalculator)   RemoteThread.Text = CStr(calc.GetThreadID) End Sub

Note that the calc variable is now declared as type ICalculator, rather than Calculator. In addition, you’re using Activator.GetType. This is required when using interfaces because you can’t use the New keyword at all. You can’t do the following:

 calc = New SimpleInterface.ICalculator()

The result is a compiler error because it isn’t possible to create an instance of an interface. Therefore, you can’t just configure remoting and use location transparency; you must use Activator.GetObject to have remoting create an instance of the object on the server.

Remoting knows how and where to create the object based on the URL you provide. It then converts the object to the right type (SimpleInterface.ICalculator) based on the type you provide in the GetObject call. If the remote object doesn’t implement this interface, then you’ll get a runtime exception.

Using Generated Proxies

Another way to create a client that doesn’t reference the library DLL is to use the soapsuds.exe command-line utility to create a proxy assembly for the service and the classes it exposes. This proxy assembly is then referenced by the client application, giving the client access to the server type information so that it can interact with the server objects.

Proxy DLL

To create the proxy DLL, run the soapsuds.exe utility with the following command line:

 > soapsuds -url:http://localhost/SimpleHost/Calculator.rem?wsdl -oa:SimpleProxy.dll

Note that you’re going against the IIS host here because it uses the HTTP protocol. This won’t work against your current custom host, as the soapsuds.exe utility doesn’t understand the tcp:// prefix. To use this against a custom host, you’d have to make sure the custom host used the HTTP protocol.

Creating the Client Application

The code download includes a ProxyClient project. This is a Windows application that references only SimpleProxy.dll. There is no reference to SimpleLibrary.dll or SimpleInterface.dll - this client relies entirely on the generated proxy assembly to interact with the server.

The best part of this is that the generated proxy contains the same namespace and class names as the service on the server. In other words, it appears that you are working with SimpleLibrary.Calculator, because the proxy is set up with that same namespace and class name. To get the remote thread ID, write the following code:

 Private Sub RemoteThreadButton_Click( _   ByVal sender As System.Object, ByVal e As System.EventArgs) _   Handles RemoteThreadButton.Click   Dim calc As New SimpleLibrary.Calculator()   RemoteThread.Text = CStr(calc.GetThreadID) End Sub

This is the same code used in the original simple example. You’ve come full circle at this point, but now the client application doesn’t directly reference your library DLL.




Professional VB 2005 with. NET 3. 0
Professional VB 2005 with .NET 3.0 (Programmer to Programmer)
ISBN: 0470124709
EAN: 2147483647
Year: 2004
Pages: 267

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net