.NET Remoting
The .NET framework provides a distributed computing technology called .NET remoting that allows a program to access objects on another machine over a network. .NET remoting is similar in concept to RMI (remote method invocation) in Java and RPC (remote procedure call) in procedural programming languages. .NET remoting is also similar to Web services (Chapter 22) with a few key differences. With Web services, a client application communicates with a Web service that is hosted by a Web server. The client and the Web service can be written in any language, as long as they can transmit messages in SOAP. With .NET remoting, a client application communicates with a server application, both of which must be written in .NET languages. Using .NET remoting, a client and a server can communicate via method calls and objects can be transmitted between applicationsa process known as marshaling the objects.
Channels
The client and the server are able to communicate with one another through channels. Channels typically use either the HTTP protocol or the TCP protocol to transmit messages. The advantage of an HTTP channel is that firewalls usually permit HTTP connections by default, while they normally block unfamiliar TCP connections. The advantage of a TCP channel is better performance than an HTTP channel. In a .NET remoting application, the client and the server each create a channel, and both channels must use the same protocol to communicate with one another. In our example, we use HTTP channels.
Marshaling
There are two ways to marshal an objectby value and by reference. Marshal by value requires that the object be serializablethat is, capable of being represented as a formated message that can be sent between applications through a channel. The receiving end of the channel deserializes the object to obtain a copy of the original object. To enable an object to be serialized and deserialized, its class must either be declared with attribute [ Serializable ] or must implement interface ISerializable.
Marshal by reference requires that the object's class extend class MarshalByRefObject of namespace System. An object that is marshaled by reference is referred to as a remote object, and its class is referred to as a remote class. When an object is marshaled by reference, the object itself is not transmitted. Instead, two proxy objects are createda transparent proxy and a real proxy. The transparent proxy provides all the public services of a remote object. Typically, a client calls the methods and properties of the transparent proxy as if it were the remote object. The transparent proxy then calls the Invoke method of the real proxy. This sends the appropriate message from the client channel to the server channel. The server receives this message and performs the specified method call or accesses the specified property on the actual object, which resides on the server. In our example, we marshal a remote object by reference.
Weather Information Application Using .NET Remoting
We now present a .NET remoting example that downloads the Traveler's Forecast weather information from the National Weather Service Web site:
http://iwin.nws.noaa.gov/iwin/us/traveler.html
[Note: As we developed this example, the National Weather Service indicated that the information provided on the Traveler's Forecast Web page would be provided by a Web service in the near future. The information we use in this example depends directly on the format of the Traveler's Forecast Web page. If you have trouble running this example, please refer to the FAQ page at www.deitel.com/faq.html. This potential problem demonstrates a benefit of using Web services or .NET remoting to implement distributed computing applications that may change in the future. Separating the server part of the application that depends on the format of an outside data source from the client part of the application allows the server implementation to be updated without requiring any changes to the client.]
Our .NET remoting application consists of five components:
- Serializable class CityWeather, which represents the weather report for one city.
- Interface Report, which declares a property Reports that the marshaled object provides to a client application to obtain a collection of CityWeather objects.
- Remote class ReportInfo, which extends class MarshalByRefObject, implements interface Report and will be instantiated only on the server.
- A WeatherServer application that sets up a server channel and makes the ReportInfo class available at a particular URI (uniform resource identifier).
- A WeatherClient application that sets up a client channel and requests a ReportInfo object from the WeatherServer to retrieve the day's weather report.
Class CityWeather
Class CityWeather (Fig. 23.9) contains weather information for one city. Class CityWeather is declared in namespace Weather (line 5) for reusability and will be published in the Weather.dll class library file that both the server application and the client application must reference. For this reason, you should place this class (and interface Report from Fig. 23.10) in a class library project. Class CityWeather is declared with attribute Serializable (line 7), which indicates that an object of class CityWeather can be marshaled by value. This is necessary because CityWeather objects will be returned by the ReportInfo object's Reports property, and the return values of the methods and properties declared by a remote class must themselves be marshaled by value from the server to the client. (The argument values in method calls will also be marshaled by value from the client to the server.) So when the client calls a get accessor that returns CityWeather objects, the server channel will serialize the CityWeather objects in a message that the client channel can deserialize to create copies of the original CityWeather objects. Class CityWeather also implements interface IComparable (line 8) so that an ArrayList of CityWeather objects can be sorted alphabetically.
Figure 23.9. Class CityWeather.
1 // Fig. 23.9: CityWeather.cs 2 // Class representing the weather information for one city. 3 using System; 4 5 namespace Weather 6 { 7 [ Serializable ] 8 public class CityWeather : IComparable 9 { 10 private string cityName; 11 private string description; 12 private string temperature; 13 14 public CityWeather( string city, string information, 15 string degrees ) 16 { 17 cityName = city; 18 description = information; 19 temperature = degrees; 20 } // end constructor 21 22 // read-only property that gets city's name 23 public string CityName 24 { 25 get 26 { 27 return cityName; 28 } // end get 29 } // end property CityName 30 31 // read-only property that gets city's weather description 32 public string Description 33 { 34 get 35 { 36 return description; 37 } // end get 38 } // end property Description 39 40 // read-only property that gets city's temperature 41 public string Temperature 42 { 43 get 44 { 45 return temperature; 46 } // end get 47 } // end property Temperature 48 49 // implementation of CompareTo method for alphabetizing 50 public int CompareTo( object other ) 51 { 52 return string.Compare( 53 CityName, ( ( CityWeather ) other ).CityName ); 54 } // end method Compare 55 56 // return string representation of this CityWeather object 57 // (used to display the weather report on the server console) 58 public override string ToString() 59 { 60 return cityName + " | " + temperature + " | " + description; 61 } // end method ToString 62 } // end class CityWeather 63 } // end namespace Weather |
Figure 23.10. Interface Report in namespace Weather.
(This item is displayed on page 1274 in the print version)
1 // Fig. 23.10: Report.cs 2 // Interface that defines a property for getting 3 // the information in a weather report. 4 using System; 5 using System.Collections; 6 7 namespace Weather 8 { 9 public interface Report 10 { 11 ArrayList Reports 12 { 13 get; 14 } // end property Reports 15 } // end interface Report 16 } // end namespace Weather |
CityWeather contains three instance variables (lines 1012) for storing the city's name, high/low temperatures and weather condition description. The CityWeather constructor (lines 1420) initializes the three instance variables. Lines 2347 declare three read-only properties that allow the values of the three instance variables to be retrieved. CityWeather implements IComparable, so it must declare a method called CompareTo that takes an object reference and returns an int (lines 5054). Also, we want to alphabetize CityWeather objects by their cityNames, so CompareTo calls string method Compare with the cityNames of the two CityWeather objects. Class CityWeather also overrides the ToString method to display information for this city (lines 5861). Method ToString is used by the server application to display the weather information retrieved from the Traveler's Forecast Web page in the console.
Interface Report
Figure 23.10 shows the code for interface Report. Interface Report is also declared in namespace Weather (line 7) and will be included with class CityWeather in the Weather.dll class library file, so it can be used in both the client and server applications. Report declares a read-only property (lines 1114) with a get accessor that returns an ArrayList of CityWeather objects. The client application will use this property to retrieve the information in the weather reporteach city's name, high/low temperature and weather condition.
Class ReportInfo
Remote class ReportInfo (Fig. 23.11) implements interface Report (line 10) of namespace Weather (specified by the using directive in line 8). ReportInfo also extends base class MarshalByRefObject. Class ReportInfo is part of the remote WeatherServer application and will not be directly available to the client application.
Figure 23.11. Class ReportInfo, which implements interface Report, is marshaled by reference.
1 // Fig. 23.11: ReportInfo.cs 2 // Class that implements interface Report, retrieves 3 // and returns data on weather 4 using System; 5 using System.Collections; 6 using System.IO; 7 using System.Net; 8 using Weather; 9 10 public class ReportInfo : MarshalByRefObject, Report 11 { 12 private ArrayList cityList; // cities, temperatures, descriptions 13 14 public ReportInfo() 15 { 16 cityList = new ArrayList(); 17 18 // create WebClient to get access to Web page 19 WebClient myClient = new WebClient(); 20 21 // get StreamReader for response so we can read page 22 StreamReader input = new StreamReader( myClient.OpenRead( 23 "http://iwin.nws.noaa.gov/iwin/us/traveler.html" ) ); 24 25 string separator1 = "TAV12"; // indicates first batch of cities 26 string separator2 = "TAV13"; // indicates second batch of cities 27 28 // locate separator1 in Web page 29 while ( !input.ReadLine().StartsWith( separator1 ) ); // do nothing 30 ReadCities( input ); // read the first batch of cities 31 32 // locate separator2 in Web page 33 while ( !input.ReadLine().StartsWith( separator2 ) ); // do nothing 34 ReadCities( input ); // read the second batch of cities 35 36 cityList.Sort(); // sort list of cities by alphabetical order 37 input.Close(); // close StreamReader to NWS server 38 39 // display the data on the server side 40 Console.WriteLine( "Data from NWS Web site:" ); 41 42 foreach ( CityWeather city in cityList ) 43 { 44 Console.WriteLine( city ); 45 } // end foreach 46 } // end constructor 47 48 // utility method that reads a batch of cities 49 private void ReadCities( StreamReader input ) 50 { 51 // day format and night format 52 string dayFormat = 53 "CITY WEA HI/LO WEA HI/LO"; 54 string nightFormat = 55 "CITY WEA LO/HI WEA LO/HI"; 56 string inputLine = ""; 57 58 // locate header that begins weather information 59 do 60 { 61 inputLine = input.ReadLine(); 62 } while ( !inputLine.Equals( dayFormat ) && 63 !inputLine.Equals( nightFormat ) ); 64 65 inputLine = input.ReadLine(); // get first city's data 66 67 // while there are more cities to read 68 while ( inputLine.Length > 28 ) 69 { 70 // create CityWeather object for city 71 CityWeather weather = new CityWeather( 72 inputLine.Substring( 0, 16 ), 73 inputLine.Substring( 16, 7 ), 74 inputLine.Substring( 23, 7 ) ); 75 76 cityList.Add( weather ); // add to ArrayList 77 inputLine = input.ReadLine(); // get next city's data 78 } // end while 79 } // end method ReadCities 80 81 // property for getting the cities' weather reports 82 public ArrayList Reports 83 { 84 get 85 { 86 return cityList; 87 } // end get 88 } // end property Reports 89 } // end class ReportInfo |
Lines 1446 declare the ReportInfo constructor. Line 19 creates a WebClient (namespace System.Net) object to interact with a data source that is specified by a URLin this case, the URL for the NWS Traveler's Forecast page (http://iwin.nws.noaa.gov/iwin/us/traveler.html). Lines 2223 call WebClient method OpenRead, which returns a Stream that the program can use to read data containing the weather information from the specified URL. This Stream is used to create a StreamReader object, so the program can read the Web page's HTML markup line-by-line.
The section of the Web page in which we are interested consists of two batches of citiesAlbany through Reno, and Salt Lake City through Washington, D.C. The first batch occurs in a section that starts with the string "TAV12," while the second batch occurs in a section that starts with the string "TAV13." We declare variables separator1 and separator2 to store these strings. Line 29 reads the HTML markup one line at a time until "TAV12" is encountered. Once "TAV12" is reached, the program calls utility method ReadCities to read a batch of cities into ArrayList cityList. Next, line 33 reads the HTML markup one line at a time until "TAV13" is encountered, and line 34 makes another call to method ReadCities to read the second batch of cities. Line 36 calls method Sort of class ArrayList to sort the CityWeather objects in alphabetical order by city name. Line 37 closes the StreamReader connection to the Web site. Lines 4245 output the weather information for each city to the server application's console display.
Lines 4979 declare utility method ReadCities, which takes a StreamReader object and reads the information for each city, creates a CityWeather object for it and places the CityWeather object in cityList. The do...while statement (lines 5963) continues to read the page one line at a time until it finds the header line that begins the weather forecast table. This line starts with either dayFormat (lines 5253), indicating the header for the daytime information, or nightFormat (lines 5455), indicating the header for the nighttime information. Because the line could be in either format based on the time of day, the loop-continuation condition checks for both. Line 65 reads the next line from the Web page, which is the first line containing temperature information.
The while statement (lines 6878) creates a new CityWeather object to represent the current city. It parses the string containing the current weather data, separating the city name, the weather condition and the temperature. The CityWeather object is added to cityList. Then the next line from the page is read and stored in inputLine for the next iteration. This process continues while the length of the string read from the Web page is greater than 28 (the lines containing weather data are all longer than 28 characters). The first line shorter than this signals the end of that forecast section in the Web page.
Read-only property Reports (lines 8288) implements the Report interface's Reports property to return cityList. The client application will remotely call this property to retrieve the day's weather report.
Class WeatherServer
Figure 23.12 contains the code for the server application. The using directives in lines 57 specify .NET remoting namespaces System.Runtime.Remoting, System.Runtime.Remoting.Channels and System.Runtime.Remoting.Channels.Http. The first two namespaces are required for .NET remoting, and the third is required for HTTP channels. Namespace System.Runtime.Remoting.Channels.Http requires the project to reference the System.Runtime.Remoting assembly, which can be found under the .NET tab in the Add References menu. The using directive at line 8 specifies namespace Weather, which contains interface Report. Remember to add a reference to Weather.dll in this project.
Figure 23.12. Class WeatherServer exposes remote class ReportInfo.
1 // Fig. 23.12: WeatherServer.cs 2 // Server application that uses .NET remoting to send 3 // weather report information to a client 4 using System; 5 using System.Runtime.Remoting; 6 using System.Runtime.Remoting.Channels; 7 using System.Runtime.Remoting.Channels.Http; 8 using Weather; 9 10 class WeatherServer 11 { 12 static void Main( string[] args ) 13 { 14 // establish HTTP channel 15 HttpChannel channel = new HttpChannel( 50000 ); 16 ChannelServices.RegisterChannel( channel, false ); 17 18 // register ReportInfo class 19 RemotingConfiguration.RegisterWellKnownServiceType( 20 typeof( ReportInfo ), "Report", 21 WellKnownObjectMode.Singleton ); 22 23 Console.WriteLine( "Press Enter to terminate server." ); 24 Console.ReadLine(); 25 } // end Main 26 } // end class WeatherServer |
Lines 1516 in Main register an HTTP channel on the current machine at port 50000. This is the port number that clients will use to connect to the WeatherServer remotely. The argument false at line 16 indicates that we do not wish to enable security, which is beyond the scope of this introduction. Lines 1921 register the ReportInfo class type at the "Report" URI as a Singleton remote class. If a remote class is registered as Singleton, one remote object will be created when the first client requests that remote class, and that remote object will service all clients. The alternative mode is SingleCall, where one remote object is created for each individual remote method call to the remote class. [Note: A Singleton remote object does not have an infinite lifetime; it will be garbage collected after being idle for 5 minutes. A new Singleton remote object will be created if another client requests one later.] The ReportInfo remote class is now available to clients at the URI "http://IPAddress:50000/Report" where IPAddress is the IP address of the computer on which the server is running. The channel remains open as long as the server application continues running, so line 24 waits for the user running the server application to press Enter before terminating the application.
Class WeatherClientForm
WeatherClientForm (Fig. 23.13) is a Windows application that uses .NET remoting to retrieve weather information from WeatherServer and displays the information in a graphical, easy-to-read manner. The GUI contains 43 Labelsone for each city in the Traveler's Forecast. All the Labels are placed in a Panel with a vertical scrollbar. Each Label displays the weather information for one city. Lines 79 are using directives for the namespaces that are required to perform .NET remoting. For this project, you must add references to the System.Runtime.Remoting assembly and the Weather.dll file we created earlier.
Figure 23.13. The Weather Client accesses a ReportInfo object remotely and displays the weather report.
(This item is displayed on pages 1278 - 1280 in the print version)
1 // Fig. 23.13: WeatherClient.cs
2 // Client that uses .NET remoting to retrieve a weather report.
3 using System;
4 using System.Collections;
5 using System.Drawing;
6 using System.Windows.Forms;
7 using System.Runtime.Remoting;
8 using System.Runtime.Remoting.Channels;
9 using System.Runtime.Remoting.Channels.Http;
10 using Weather;
11 12 public partial class WeatherClientForm : Form 13 { 14 public WeatherClientForm() 15 { 16 InitializeComponent(); 17 } // end constructor 18 19 // retrieve weather data 20 private void WeatherClientForm_Load( object sender, EventArgs e ) 21 { 22 // setup HTTP channel, does not need to provide a port number 23 HttpChannel channel = new HttpChannel(); 24 ChannelServices.RegisterChannel( channel, false ); 25 26 // obtain a proxy for an object that implements interface Report 27 Report info = ( Report ) RemotingServices.Connect( 28 typeof( Report ), "http://localhost:50000/Report" ); 29 30 // retrieve an ArrayList of CityWeather objects 31 ArrayList cities = info.Reports; 32 33 // create array and populate it with every Label 34 Label[] cityLabels = new Label[ 43 ]; 35 int labelCounter = 0; 36 37 foreach ( Control control in displayPanel.Controls ) 38 { 39 if ( control is Label ) 40 { 41 cityLabels[ labelCounter ] = ( Label ) control; 42 ++labelCounter; // increment Label counter 43 } // end if 44 } // end foreach 45 46 // create Hashtable and populate with all weather conditions 47 Hashtable weather = new Hashtable(); 48 weather.Add( "SUNNY", "sunny" ); 49 weather.Add( "PTCLDY", "pcloudy" ); 50 weather.Add( "CLOUDY", "mcloudy" ); 51 weather.Add( "MOCLDY", "mcloudy" ); 52 weather.Add( "TSTRMS", "rain" ); 53 weather.Add( "RAIN", "rain" ); 54 weather.Add( "SNOW", "snow" ); 55 weather.Add( "VRYHOT", "vryhot" ); 56 weather.Add( "FAIR", "fair" ); 57 weather.Add( "RNSNOW", "rnsnow" ); 58 weather.Add( "SHWRS", "showers" ); 59 weather.Add( "WINDY", "windy" ); 60 weather.Add( "NOINFO", "noinfo" ); 61 weather.Add( "MISG", "noinfo" );
62 weather.Add( "DRZL", "rain" ); 63 weather.Add( "HAZE", "noinfo" ); 64 weather.Add( "SMOKE", "mcloudy" ); 65 weather.Add( "SNOWSHWRS", "snow" ); 66 weather.Add( "FLRRYS", "snow" ); 67 weather.Add( "FOG", "noinfo" ); 68 69 // create the font for the text output 70 Font font = new Font( "Courier New", 8, FontStyle.Bold ); 71 72 // for every city 73 for ( int i = 0; i < cities.Count; i++ ) 74 { 75 // use array cityLabels to find the next Label 76 Label currentCity = cityLabels[ i ]; 77 78 // use ArrayList cities to find the next CityWeather object 79 CityWeather city = ( CityWeather ) cities[ i ]; 80 81 // set current Label's image to image 82 // corresponding to the city's weather condition - 83 // find correct image name in Hashtable weather 84 currentCity.Image = new Bitmap( @"images" + 85 weather[ city.Description.Trim() ] + ".png" ); 86 currentCity.Font = font; // set font of Label 87 currentCity.ForeColor = Color.White; // set text color of Label 88 89 // set Label's text to city name and temperature 90 currentCity.Text = "
" + city.CityName + city.Temperature; 91 } // end for 92 } // end method WeatherClientForm_Load 93 } // end class WeatherClientForm
|
Method WeatherClientForm_Load (lines 2092) retrieves the weather information when this Windows application loads. Line 23 creates an HTTP channel without specifying a port number. This causes the HttpChannel constructor to choose any available port number on the client computer. A specific port number is not necessary because this application does not have its own clients that need to know the port number in advance. Line 24 registers the channel on the client computer. This will allow the server to send information back to the client. Lines 2728 declare a Report variable and assign to it a proxy for a Report object instantiated by the server. This proxy allows the client to remotely call ReportInfo's properties by redirecting method calls to the server. RemotingServices method Connect connects to the server and returns a reference to the proxy for the Report object. Note that we are executing the client and the server on the same computer, so we use localhost in the URL that represents the server application. To connect to a WeatherServer on a different computer, you must modify this URL accordingly. Line 31 retrieves the ArrayList of CityWeather objects generated by the ReportInfo constructor (lines 1446 of Fig. 23.11). Variable cities now refers to an ArrayList of CityWeather objects that contains the information taken from the Traveler's Forecast Web page.
Because the application presents weather data for so many cities, we must establish a way to organize the information in the Labels and to ensure that each weather description is accompanied by an appropriate image. The program uses an array to store all the Labels, and a Hashtable (discussed further in Chapter 27, Collections) to store weather descriptions and the names of their corresponding images. A Hashtable stores keyvalue pairs, in which both the key and the value can be any type of object. Method Add adds keyvalue pairs to a Hashtable. The class also provides an indexer to return the value for a particular key in the Hashtable. Line 34 creates an array of Label references, and lines 3544 place the Labels we created in the designer in the array so that they can be accessed programmatically to display weather information for individual cities. Line 47 creates Hashtable object weather to store pairs of weather conditions and the names for images associated with those conditions. Note that a given weather description name does not necessarily correspond to the name of the PNG file containing the correct image. For example, both "TSTRMS" and "RAIN" weather conditions use the rain.png image file.
Lines 7391 set each Label so that it contains a city name, the current temperature in the city and an image corresponding to the weather conditions for that city. Line 76 retrieves the Label that will display the weather information for the next city. Line 79 uses ArrayList cities to retrieve the CityWeather object that contains the weather information for the city. Lines 8485 set the Label's image to the PNG image that corresponds to the city's weather conditions. This is done by eliminating any spaces in the description string by calling string method trim and retrieving the name of the PNG image from the weather Hashtable. Lines 8687 set Label properties to achieve the visual effect seen in the sample output. Line 90 sets the Text property to display the city's name and high/low temperatures. [Note: To preserve the layout of the client application's window, we set the MaximumSize and MinimumSize properties of the Windows Form to the same value so that the user cannot resize the window.]
Web Resources for .NET Remoting
This section provided a basic introduction to .NET remoting. There is much more to this powerful .NET framework capability. The following Web sites provide additional information for readers who wish to investigate these capabilities further. In addition, searching for ".NET remoting" with most search engines yields many additional resources.
msdn.microsoft.com/library/en-us/cpguide/html/cpconaccessingobjectsinotherapplicationdomainsusingnetremoting.asp
The .NET Framework Developer's Guide on the MSDN Web site provides detailed information on .NET remoting, including articles that include choosing between ASP.NET and .NET remoting, an overview of .NET remoting, advanced .NET remoting techniques and .NET remoting examples.
msdn.microsoft.com/library/en-us/dndotnet/html/introremoting.asp
This site offers a general overview of .NET remoting capabilties.
search.microsoft.com/search/results.aspx?qu=.net+remoting
This microsoft.com search provides links to many .NET remoting articles and resources.