Remoting

for RuBoard

While a complete discussion of remoting is beyond the scope of this book, a brief introduction provides a powerful example of how metadata and marshal by reference (MBR) work. Remoting also provides a mechanism for having executable servers.

Unlike remoting in Microsoft's COM technology, it requires a minimal amount of infrastructure programming. The small amount that is required allows programmers either a degree of flexibility or the ability to customize remoting for their particular applications.

The .NET framework provides two ways to provide connections between two applications on different computers. Web Services, discussed in Chapter 11, enable computers that do not host the Common Language Runtime to communicate with computers that do. The remoting technology discussed here builds distributed applications between computers that host the CLR.

Remoting Overview

The key parts of Remoting are:

  • Communication channels for transport of messages.

  • Interception to allow for message generation for communication over the channels.

  • Formatters to put the messages into a byte stream that is sent over the channel. These are the same formatters that were discussed in the section on serialization.

Interception

Proxies and stubs (referred to in .NET as dispatchers) transform the function calls on the client or server side into messages that are sent over the network. This is called interception, because the proxies and dispatchers intercept a method call to send it to its remote destination. Unlike COM, metadata provides the information so the CLR can generate the proxies and stubs for you.

A proxy takes the function call off the stackframe of the caller and transforms it into a message. The message is then sent to its destination. A dispatcher takes the message and transforms it into a stackframe so that a call can be made to the object.

For example, assume the UnregisterCustomer method from the Customer assembly runs in one App Domain and is called from another. It makes no difference whether the App Domains are in the same process or on the same machine.

The proxy would take the integer id argument on the stackframe of the client making the call and put it in a message that encoded the call and its argument. On the server side, the dispatcher would take that message and create a function call on the server's stack for the call UnregisterCustomer (int id) and make that call into the object. The client and server codes do not know that they are being remoted .

Channels And Formatters

The formatter converts the message into a byte stream. The .NET framework comes with two formatters, binary and SOAP (text-based XML discussed in Chapter 11 on Web Services). The byte stream is then sent over a communication channel.

The .NET framework comes with two channels, although you can write your own. The HTTP channel uses the HTTP protocol and is good for communicating over the Internet or through firewalls. The TCP channel uses the TCP (sockets) protocol and is designed for high-speed communication. You have four permutations of formatters and transport: binary over TCP, binary over HTTP, SOAP over HTTP, and SOAP over TCP.

Remote Objects

Clients obtain a proxy by activating a remote object. Remote objects must derive from MarshalByRefObject , because you work with a proxy to the object reference, not with the object reference itself. This is the same concept discussed in the section on contexts, where marshal by reference is also used to access context bound objects.

Local objects passed as method parameters from one application domain to another can be passed by value ( copied ) or by reference.

To be passed by value, they must be serializable. The object is serialized, sent across the transport layer, and recreated on the other side. We have already seen this in the AppDomain example.

To be passed by reference, the class must derive from MarshalByRefObject . The Remoting example illustrates pass by reference.

Remote objects can be either server or client activated. Server-activated objects are not created until the first method call on the object. Server-activated objects come in two flavors. SingleCall objects are stateless. Each method cause a new object to be created. Singleton objects can be used by multiple client activation requests . Singleton objects can maintain state. SingleCall objects will scale better than Singleton objects because they do not retain state and can be load balanced.

Client-activated objects are activated when the client requests them. While they can last for multiple calls and hold state, they cannot store information from different client activations. This is similar to calling CoCreateInstanceEx in DCOM.

Activation

Objects are activated on the client side in one of three ways by using the Activator class.

  • Activator.GetObject is used to get a reference to a server-activated object.

  • Activator.CreateInstance is used to create a client-activated object. You can pass parameters to the object's constructor using the overloaded CreateInstance method that takes an array of objects to be passed to the constructor.

  • The C# new syntax can be used to create a server- or client-activated object. A configuration file is used to describe how new should be used.

Sample Remotable Object

For our Remoting example, we remote our Customers object from the Customer assembly.

In the remoting example directory there are two solutions. One represents the client program, the other the server program. Each can be built independently of the other. Start the server program first. Notice that it waits for a client request. You can then run the client program, which will run against objects that live inside the server. We will discuss the details of the client and server code and output in the next few sections.

Notice that we had to make only two simple changes to our object. The Customers class in the server project had to be made remotable by inheriting from MarshalByRefObject.

 public class Customers : MarshalByRefObject, ICustomer 

The CustomerListItem that was going to be transferred by value had to be made serializable.

 [Serializable]  public struct CustomerListItem  {    public int CustomerId;    public string FirstName;    public string LastName;    public string EmailAddress;  } 
Sample Remoting Program

In the Remoting example the client accesses a server-activated object. The server is the TcpServerChannel class that uses using a binary format with the TCP protocol. The channel will use port 8085. The server registers the type being remoted, the endpoint name to refer to this object, and the type of activation. The server then waits for client requests.

 TcpServerChannel chan = new TcpServerChannel(8085);  ChannelServices.RegisterChannel(chan);  RemotingConfiguration.RegisterWellKnownServiceType(                    typeof(Customers), "AcmeCustomer",                    WellKnownObjectMode.Singleton);  . . . 

The server has to be started before the client program can access the object.

The client sets up a TcpClientChannel object and then connects to the object. In the Activator.GetObject method call it specifies the type of the object it wants, and the endpoint where the server is listening to for object requests. If you want to run the client and server on separate machines, substitute the server machine name for localhost in the endpoint. Unlike COM location transparency, the client has to specify a specific endpoint; there is no redirection through an opaque registry entry.

 TcpClientChannel chan = new TcpClientChannel();  ChannelServices.RegisterChannel(chan);  Customers obj = (Customers)Activator.GetObject(                    typeof(Customers),                    "tcp://localhost:8085/AcmeCustomer");  if (obj == null) System.Console.WriteLine(                              "Could not locate server");  else  . . . 

The client then uses the proxy to make calls on the object as if it were a local instance.

 bool bRet = RemotingServices.IsTransparentProxy(obj);  . . .  ArrayList ar;  ar = obj.GetCustomer(-1);  ShowCustomerArray(ar);  obj.RegisterCustomer("Boris", "Badenough",                                  "boris@no-goodnicks.com");  Console.WriteLine();  ar = obj.GetCustomer(-1);  ShowCustomerArray(ar); 

To run the program, start the server program in one console window, then run the client program from another console window.

The output depends on what kind of server-activated object is being activated. [13] If the server activation type is Singleton , which supports the maintaining state, you get the behavior you would expect from the nonremoted case. A new customer is added, and you find that new customer in the list when you ask for all the existing customers. As you would expect, the initial activate call results in the Customers constructor being called once for each server invocation, no matter how many times the client program is run.

[13] In the example, you can try out both Singleton and SingleCall activation by commenting out the appropriate line in the code in server.cs .

 Object reference is a proxy?: True  Client: AppDomain Client.exe Thread 19 Context 0  1   Rocket         Squirrel       rocky@frosbitefalls.com  2   Bullwinkle     Moose          moose@wossamotta.edu  1   Rocket         Squirrel       rocky@frosbitefalls.com  2   Bullwinkle     Moose          moose@wossamotta.edu  3   Boris          Badenough      boris@no-goodnicks.com 

If the activation type is SingleCall , which creates a new object instance for every method call, the results are quite different. Four different objects are created. The first is created by the initial activate request. The second is created by the initial call to GetCustomer . The third is created by the RegisterCustomer call. The fourth is created by the second call to GetCustomer . The last object created never sees the new customer, because no state is saved. Note that the static nextCustId member of the Customer class is treated as a static with respect to the new object instances of the Customer class, just as you would expect. Same client code, different results! Since the object is already activated, if you run the client program a second time for the same server invocation, the Customers constructor will be called only three times.

 Object reference a proxy?: True  Client: AppDomain Client.exe Thread 19 Context 0  3   Rocket         Squirrel       rocky@frosbitefalls.com  4   Bullwinkle     Moose          moose@wossamotta.edu  8   Rocket         Squirrel       rocky@frosbitefalls.com  9   Bullwinkle     Moose          moose@wossamotta.edu 

Since the client uses a proxy, the object executes inside the server's application domain, but on a different thread than the main server thread. The object's constructor is not called until the first method call on the object. Notice how in both cases we have remoted an ArrayList of types without any special work aside from making the type serializable. The presence of metadata makes the programmer's work much easier.

Metadata and Remoting

In order for the client to request an object of a specific type, metadata about the type has to be available to the client. For some applications, a reference can be made to the actual assembly where the object is stored.

For many applications, however, you do not want to give the client access to your source code. For the metadata that the client needs, a reference need only be made to an object without the implementation details.

One way to do this is to build a version of the object that has methods with no implementation. This interface class can then be built into an assembly that can be given to the client. You can throw the System. NotSupportedException in the methods if you wish to make sure that it is never used by mistake for the real object.

 [System.Serializable]  public struct CustomerListItem  {    public int CustomerId;    public string FirstName;    public string LastName;    public string EmailAddress;  }  . . .  public class Customers : MarshalByRefObject, ICustomer  {    public int RegisterCustomer(string firstName,                       string lastName, string emailAddress)    {      throw new NotSupportedException();    }    public void UnregisterCustomer(int id)    {      throw new NotSupportedException();    }    public void ChangeEmailAddress(int id,                                        string emailAddress)    {      throw new NotSupportedException();    }    public ArrayList GetCustomer(int id)    {      throw new NotSupportedException();    }  } 

For Web Services you use the SOAPSUDS tool to extract the metadata from the service, and then generate an assembly that has the required metadata. You can then build a proxy DLL and have the client program refer to it. This is conceptually equivalent to the first approach.

The server, of course, has to reference the real object's assembly.

Unlike the COM model, there is no reference counting, interface negotiation, building and registering separate proxies and stubs, worrying about global identifiers, or use of the registry. Because of metadata, all you have to do is inherit from MarshalByRefObject to make an object remotable.

Remoting Configuration Files

You use configuration files to define where the object is activated. The client can then use the new operator to create the object. The big advantage here is that as the object location changes (such as a URL or TCP channel), or the formatter you want to use changes, the client does not have to be rebuilt.

Multiple classes can be configured on the client. Configuration files are loaded into the client using the RemotingConfiguration. Configure method.

for RuBoard


Application Development Using C# and .NET
Application Development Using C# and .NET
ISBN: 013093383X
EAN: 2147483647
Year: 2001
Pages: 158

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