Hosting the Remoted Object and Configuring the Client

Hosting the Remoted Object and Configuring the Client

The class library cannot be remoted on its own; you need to build an application that will run on the second machine and accept requests for the remoted class. This application can be a Windows application, a console application, or a Windows service. The application must be running in order for remoting requests to be fulfilled; the runtime will not start the application when requests for the remoted class come in. If you write a service, as discussed in Chapter 12, "Writing a Windows Service," you can arrange for it to start automatically whenever the server is booted , even if nobody logs on. That can make it simple to have your remoted objects available. In this section, you'll create a Windows application, because it's simple to write and control. Then you'll adjust the client to use the remoted object.

Creating and Configuring the Server Application

Even though the application will be deployed on a separate machine, you can create it within the same solution that already contains the class library and the client application. Add a Windows application called GreetingServer to the solution. Add a button named Listen , with text of Listen , and a label set to an empty string. This button will start the listener that provides access to the remoted object. Double-click the button and edit the handler to read like this:

 
 private: System::Void Listen_Click(System::Object *  sender,                                    System::EventArgs *  e) {    Runtime::Remoting::RemotingConfiguration::Configure(                 "GreetingServer.exe.config");    ListeningLabel->Text = "Listening"; } 

You don't need to write code to listen for requests or to serve out the requested objects. All this code does is load the remoting configuration from the configuration file. Right-click the project in Solution Explorer and choose Add, Add New Item, and select a configuration file. Then edit app.config to read like this:

 
 <configuration>    <system.runtime.remoting>       <application>          <service>             <wellknown                mode="Singleton"                 type="Greeting.Greeter, Greeting"                 objectUri="Greeter"              />          </service>          <channels>             <channel ref="tcp"  port="9555" />          </channels>       </application>    </system.runtime.remoting> </configuration> 

There are two important tags (or elements) in this configuration file: the service tag and the channels tag. Services, in other words remoted objects, are offered in two ways: as well-known objects and as activated objects. Well-known objects are offered by the server, and the client code is expected to know the name and ask for them by name . The server takes it from there. Activated objects are discussed later in this chapter, in the "Choosing Lifetime and Lifecycle Options Appropriately" section. A single hosting application can host multiple remoted objects by having multiple tags within the service element.

The wellknown element has three attributes: the mode , the type , and the objectUri . Using Singleton for the mode causes a single object to be created the first time the client code requests the object, and for the object to persist and be used for every request that follows . Using SingleCall for the mode causes a new object to be created for each method call that comes from the client, and then disposed when the call ends. It's more expensive than Singleton and should be used only when you know you will need it. The type attribute identifies the class (using the format Namespace.ClassName ) and assembly (the DLL, without the .DLL extension) that is being exposed to remoting. The objectUri attribute defines the name that will be used by the client code to refer to this object. It doesn't need to match either of the names used in the type attribute, but it makes life simpler for those using this object if you keep the object name the same, as this example does.

The channels tag defines the mechanisms over which this server will be offering objects. There are two protocols provided by the .NET Framework: TCP and HTTP. Although most introductory examples feature HTTP, most developers with remoting experience actually find TCP simpler to use. This example shows that it certainly isn't complicated. Using HTTP involves IIS; using TCP does not. The channel tag has two attributes: ref sets the protocol, and port is a port that you choose fairly arbitrarily. Use a large number (between 9000 and 10,000 is good) that you don't believe is already in use on your server for some other remoting application.

When the configuration file is complete, add a post-build event, following the process first discussed in Chapter 11, but using this command line:

 
 copy app.config "$(TargetPath).config" 

When you build several projects in the same solution, the executable files associated with the project all go to a Debug or Release folder under the solution folder, not under the project folder. This command uses the full path and name of the executable being created, and adds the string .config, to build the location and filename to be used when copying app.config. Unlike the relative paths used in some other copy commands involving app.config in other chapters, this path is absolute. If your project folder's complete path involves a folder with spaces in the name (and there are plenty in a path like C:\Documents and Settings\ yourname\My Documents\Visual Studio Projects , which is quite likely the folder where your projects are created), the copy command will fail without the double quotes around the copy target.

Because you're relying on a post-build event to copy the configuration file for you, you'll need to rebuild the application when you change the configuration file. If you prefer, you can edit app.config manually and then apply the same edits to GreetingServer.exe.config to avoid having to build the application.

DON'T LOSE YOUR EDITS

If you edit only GreetingServer.exe.config to make a configuration change, the next time you build your project, the contents of app.config will be copied over your changes. Never edit GreetingServer.exe. config without making the same changes to app.config.


Notice that you don't need to change anything in your class library to make your class remoted. Once it inherits from MarshalByRefObject , it's remotable. The only code you write is the single line in the application that loads the configuration file. The real work is getting the configuration file right. That can be a lot of work; when the configuration file is wrong the error messages are rarely helpful. Work slowly and methodically when you are changing a remoting configuration file and trying to solve errors.

Changing the Client to Use the Remoted Object

Just as the class library needed very few changes to become a remoted library, so the client code needs very few changes to use the remoted object. Again, a configuration file does the heavy lifting . In the constructor of the Form1 class, before the line that creates a new Greeter object, add this line:

 
 Runtime::Remoting::RemotingConfiguration::Configure("GreetingClient.exe.config"); 

Add a configuration file to the GreetingClient project, and edit it to read like this:

 
 <configuration>    <system.runtime.remoting>       <application>          <client>             <wellknown                type="Greeting.Greeter, Greeting"                url="tcp://111.222.33.44:9555/Greeter"             />          </client>       </application>    </system.runtime.remoting> </configuration> 

Make sure that you replace the IP address in this file with the IP address of the second machine, where you plan to deploy the remoted object and server application. The numbers after the IP address are the port: They must match the port in the server application's configuration file. The last part of the url attribute must match the objectUri attribute in the server application's configuration file, and the type attributes in the two files must match. You don't need a channels element in the client configuration file.

Add a post-build event to the GreetingClient project, with this command line:

 
 copy app.config "$(TargetPath).config" 

There is an unfortunate side-effect of building several executable projects under the same solution; the debugger doesn't seem to know where to find things. To work around this, set the working directory for the debugger to match the location of all the executable and configuration files. Open the properties page for the GreetingClient project, and select Debugging. As in Figure 14.3, set the WorkingDirectory attribute to $(TargetDir) .

Figure 14.3. Setting the working directory for debugging, to ensure that the configuration file is found.

graphics/14fig03.jpg

Build the solution and make sure both configuration files were copied to the Greeting\Debug folder.

Deploying to the Remote Machine

Copy greetingserver.exe, greetingserver.exe.config, and greeting.dll to a working folder on the other machine. It doesn't matter what the folder is called, and it doesn't have to be under wwwroot . Also copy people.xml to the root of the C drive on that machine, because the code looks in that hard-coded location. You don't need to copy any other files, edit any of the files you copy, or configure anything on the other machine.

On the remote machine, double-click greetingserver.exe to run it. Click the Listen button. When the Listening text appears, return to your client machine and press F5 to run the client under the debugger. Click each button in turn and confirm it is still working. That's how simple remoting is.

Perhaps you suspect that the client code is still using the local version of the Greeter object that it used during testing. You can easily prove that it's not. Stop debugging, and edit Greeter.cpp, changing the Greet() method to read like this:

 
 String* Greeter::Greet() {     return "Hey, it's me!"; } 

DEBUG LOCALLY

It can be awkward to make changes to a project that uses remoting when it is spread across two machines. A debug cycle that involves copying files from the development machine to the production server quickly becomes annoying. If you comment out the call to Configure() in the constructor of the client's form object, you'll be using the local version of the Greeter class, which will make development go much more quickly if you are expanding. Just remember to restore the line when you are ready to test the fully-deployed solution.


Build the solution and run it, and then click the Greet button again. You'll see "Hello!" , not the new text, proving that it's the remote version that's being called.

Once everything's working, it's a good idea to break it on purpose so you can see what happens. Try each of these tasks in turn, returning everything to a working state after each one, and observe the error messages you receive:

  • Close the server application, and then run the client and click a button.

  • Remove people.xml from the root of C on the server and click each of the three buttons in turn. See how the exception thrown on the server comes back to you?

  • Edit one of the configuration files so that the port in each is not the same.

  • Edit the client configuration file to use the wrong IP address.

  • Edit the type attribute in the server configuration file to point to a non-existent assembly.

  • Edit the type attribute in the server configuration file to point to a non-existent class within an existing assembly.

Seeing these error messages under controlled conditions should make the debugging process a little simpler when you're using remoting on your own projects.



Microsoft Visual C++. NET 2003 Kick Start
Microsoft Visual C++ .NET 2003 Kick Start
ISBN: 0672326000
EAN: 2147483647
Year: 2002
Pages: 141
Authors: Kate Gregory

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