Monday, October 13, 2008

.Net Remoting

What is .NET Remoting?
.NET Remoting is an enabler for application communication. It is a generic system for different applications to use to communicate with one another. .NET objects are exposed to remote processes, thus allowing interprocess communication. The applications can be located on the same computer, different computers on the same network, or even computers across separate networks.
.NET Remoting versus Distributed COM
In the past interprocess communication between applications was handled through Distributed COM, or DCOM. DCOM works well and the performance is adequate when applications exist on computers of similar type on the same network. However, DCOM has its drawbacks in the Internet connected world. DCOM relies on a proprietary binary protocol that not all object models support, which hinders interoperability across platforms. In addition, have you tried to get DCOM to work through a firewall? DCOM wants to communicate over a range of ports that are typically blocked by firewalls. There are a ways to get it to work, but they either decrease the effectiveness of the firewall (why bother to even have the firewall if you open up a ton of ports on it), or require you to get a firewall that allows support for binary traffic over port 80.
.NET Remoting eliminates the difficulties of DCOM by supporting different transport protocol formats and communication protocols. This allows .NET Remoting to be adaptable to the network environment in which it is being used.
.NET Remoting versus Web Services
Unless you have been living in a cave, or are way behind in your reading, you have probably read something about Web services. When you read the description of .NET Remoting it may remind you a lot of what you're read about Web services. That is because Web services fall under the umbrella of .NET Remoting, but have a simplified programming model and are intended for a wide target audience.
Web services involve allowing applications to exchange messages in a way that is platform, object model, and programming language independent. Web services are stateless and know nothing about the client that is making the request. The clients communicate by transferring messages back and forth in a specific format known as the Simple Object Access Protocol, or SOAP. (Want to get some funny looks in the hallway? Stand around in the hallway near the marketing department with your colleagues and discuss the benefits of using SOAP).
The following list outlines some of the major differences between .NET Remoting and Web services that will help you to decide when to use one or the other:
ASP.NET based Web services can only be accessed over HTTP. .NET Remoting can be used across any protocol.
Web services work in a stateless environment where each request results in a new object created to service the request. .NET Remoting supports state management options and can correlate multiple calls from the same client and support callbacks.
Web services serialize objects through XML contained in the SOAP messages and can thus only handle items that can be fully expressed in XML. .NET Remoting relies on the existence of the common language runtime assemblies that contain information about data types. This limits the information that must be passed about an object and allows objects to be passed by value or by reference.
Web services support interoperability across platforms and are good for heterogeneous environments. .NET Remoting requires the clients be built using .NET, or another framework that supports .NET Remoting, which means a homogeneous environment.

Channels
Remote objects are accessed through Channels. Channels physically transport the messages to and from remote objects. There are two existing channels TcpChannel and HttpChannel. Their names give away the protocols that they use. In addition, the TcpChannel or HttpChannel can be extended, or a new channel created if you determine the existing channels do not meet your needs.
Create a Remotable Object
A remotable object is nothing more than an object that inherits from MarshalByRefObject. The following sample demonstrates a simple class to expose the omnipresent hello world. This object exposes a single method HelloWorld that will return a string. The only values that can be returned from methods are the classes in the .NET Framework that are serializable such as string and DataSet. In addition, if you need to return a user-defined object then the object needs to be marked as serializable.
Create a new C# class library project. Add a class called SampleObject and put in the following code. Add a reference to System.Runtime.Remoting in the project, otherwise the TcpChannel will not be found. Compile the class to make sure you have everything correct. using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
namespace Futureapps.Remoting
{
///
/// Sample object to demonstrate the use of .NET Remoting.
///

public class SampleObject : MarshalByRefObject
{
///
/// Constructor
///

public SampleObject()
{
}
///
/// Return a hello message
///

/// Hello world message
public string HelloWorld()
{
return "Hello World!";
}
}
}
Create a Server To Expose the Remotable Object
We need to create a server object that will act as a listener to accept remote object requests. For this example we will use the TCP/IP channel. We first create an instance of the channel and then register it for use by clients at a specific port. The service can be registered as WellKnownObjectMode.SingleCall, which results in a new instance of the object for each client, or as WellKnownObjectMode.Singleton, which results in one instance of the object used for all clients.
It is not necessary to create the server listener if you are planning to use IIS. For obvious reasons, IIS only supports the use of the HttpChannel. Create a virtual directory for your application and then put code to register your service in the Application_Start event.
For our example, we'll go ahead and create a server listener in case you don't have IIS. Since the service needs to be bound to an available port, for our example I chose 8080, which is a port that I know to be unused on my computer. You may need to choose a different port depending upon what ports you have available. To see a list of the used ports on your computer open a command prompt and issue the command "netstat --a". It may produce a long listing so make sure the command prompt buffer sizes are set to allow scrolling. Compile the class to make sure you have everything correct.
Create a new C# console application project. Add a class called SampleServer and paste in the following code. Add a reference to System.Runtime.Remoting in the project, otherwise the TcpChannel will not be found. In addition, add a reference to the project containing the SampleObject, otherwise the code will not compile because it won't know how to find a reference to SampleObject. using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
namespace Futureapps.Remoting
{
///
/// Sample server to demonstrate the use of .NET Remoting.
///

public class SampleServer
{
public static int Main(string [] args)
{
// Create an instance of a channel
TcpChannel channel = new TcpChannel(8080);
ChannelServices.RegisterChannel(channel);

// Register as an available service with the name HelloWorld
RemotingConfiguration.RegisterWellKnownServiceType(
typeof(SampleObject),
"HelloWorld",
WellKnownObjectMode.SingleCall );
System.Console.WriteLine("Press the enter key to exit...");
System.Console.ReadLine();
return 0;
}
}
}

Create a Client To Use the Remotable Object
Now that we have our remotable object and a server object to listen for requests, let's create a client to use it. Our client will be very simple. It will connect to the server, create an instance of the object using the server, and then execute the HelloWorld method.
Create a new C# console application project. Add a class called SampleClient and paste in the following code. Add a reference to System.Runtime.Remoting in the project, otherwise the TcpChannel will not be found. In addition, add a reference to the project containing the SampleObject, otherwise the code will not compile because it won't know how to find a reference to SampleObject. Compile the class to make sure you have everything correct. using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
namespace Futureapps.Remoting
{
///
/// Sample client to demonstrate the use of .NET Remoting.
///

public class SampleClient
{
public static int Main(string [] args)
{
// Create a channel for communicating w/ the remote object
// Notice no port is specified on the client
TcpChannel chan = new TcpChannel();
ChannelServices.RegisterChannel(chan);
// Create an instance of the remote object
SampleObject obj = (SampleObject) Activator.GetObject(
typeof(Futureapps.Remoting.SampleObject),

"tcp://localhost:8080/HelloWorld" ); // Use the object
if( obj.Equals(null) )
{
System.Console.WriteLine("Error: unable to locate server");
}
else
{
Console.WriteLine(obj.HelloWorld());
}
return 0;
}
}
}
Test the Remoting Sample
Once you have created the projects and successfully compiled each of them you are ready to try it out. Assuming you chose a free TCP/IP port for the service, start the server executable. After the server successfully starts it will result in a console window being displayed with the message "Press the enter key to exit". The server is listening so you are now ready to run the client. Executing the client should result in "Hello World!" being displayed in a separate console window. The client window will then close while the server remains open and available.
If you have multiple computers available to you on a network you could execute the server on one machine and the client on another just to prove to yourself that it really is remoting. In order to run on separate machines you would need to change the reference to localhost in the sample client to point to the appropriate location.
Exampple#2.

Step 1: Creating The Shared Library
Click on File->New->Project. Choose to create a new "C# Library" and name it ResumeServerLibrary then click
on OK. This will create the "shared vocabulary" that both our .NET Remote client and Server will use to
communicate.
The full code is below, if you would like to skip the database access portions, replace the ResumeLoader
object with:public class ResumeLoader : System.MarshalByRefObject
{
public ResumeLoader()
{
System.Console.WriteLine("New Referance Added!");
}
public Resume GetResumeByUserID(decimal userID)
{
return new Resume(1);
}
}
The namespaces required for the object. Please remember, if you're getting errors that the System.Runtime.Remoting.Channels.Tcp namespace does not exist, make sure you added the referance to System.Runtime.Remoting.dll as described
above.using System;
using System.Runtime;
using System.Data.SqlClient;
The namespace we're using for this particular object is DotNetRemoteTest, the object below is a
MarshalByRefObject which means that instead of passing ResumeLoader over the wire, we're creating a
referance and all of the work including the database work happens completely on the server side.namespace DotNetRemoteTest
{
public class ResumeLoader : System.MarshalByRefObject
{
private SqlConnection dbConnection;
public ResumeLoader()
{
this.dbConnection = new System.Data.SqlClient.SqlConnection();
this.dbConnection.ConnectionString =
"data source=GRIMSAADO2K;initial catalog=underground;integrated security=SSPI;pers" +
"ist security info=True;workstation id=GRIMSAADO2K;packet size=4096";
/*Your connection string will be different. Database connections are beyond the scope
of this article
*If you do not know how to create a database connection, please use the alternate
version of this object */
System.Console.WriteLine("New Referance Added!");
}
public Resume GetResumeByUserID(decimal userID)
{
Resume resume = new Resume();
try
{
dbConnection.Open();
SqlCommand cmd = new SqlCommand(
"SELECT ResumeID, UserID, Title, Body FROM Resume as theResume WHERE theResume.UserID="
+ userID +""
, dbConnection
);
SqlDataReader aReader = cmd.ExecuteReader();
if(aReader.Read())
{
resume.ResumeID=aReader.GetDecimal(0);
resume.UserID=aReader.GetDecimal(1);
resume.Title=aReader.GetString(2);
resume.Body=aReader.GetString(3);
}
aReader.Close();
dbConnection.Close();
}
catch(Exception x) { resume.Title="Error:"+x; }
return resume;
}
}
The Resume object needs to be serializable in order to be a return type of a remotely referanced .NET Remote
object. The reason for this is the object will have to be turned into raw data to be passed over the network, then
re-assembled into an object again on the other end.
This object is extremely simple, and to add to the simplicity of this tutorial, the constructor even initializes the
fields with some default content. [Serializable]
public class Resume
{
private decimal resumeID, userID;
private String body, title;
public Resume(decimal resumeID)
{
this.ResumeID=resumeID;
this.UserID=1;
this.Body="This is the default body of the resume";
this.Title="This is the default Title";
}
public decimal ResumeID
{
get { return resumeID; }
set { this.resumeID=value; }
}
public decimal UserID
{
get { return userID; }
set { this.userID=value; }
}
public String Body
{
get { return body; }
set
{
this.body=value;
}
}
public String Title
{
get { return title; }
set
{ this.title=value; }
}
}//END OF RESUME OBJECT
}//END OF DotNetRemoteTest namespace
Compile this project and you should have a DLL avaiable now to be added to your other assemblies.
Step 2: Create The Server Object
There are actually several ways to create the server object, the most straightforward is described below.
In Visual Studio.NET, Click on File->New Project. Choose a Command Line Application and name it
ResumeSuperServer.
First things first, we need to add referances to the required DLLs that will make this program work. Go
to Project->Add Referance, and add a referance to the DLL that we created in Step1 by clicking the "Browse"
button.
In order to use the .NET remote functionality, you must add a referance to the DLL using Project->Add Referance. Under the .NET tab, choose System.Runtime.Remoting.DLL and click on OK. Side note, to the authors of the .NET books out
there that will remain nameless--Details like this are extremely important to someone trying to compile a
.NET Remoting "hello world" :).
Next you will need to add a referance to System.Runtime.Remoting.dll the same way we did in Step 1.
The object below is fairly simple and straight forward. Below I'll explain each of the 3 lines of code that really
matter to .NET remoting.
The TcpServerChannel is one of the two types of channels supported by .NET remoting. This will set up the
port number we want our object to respond to requests on, and the ChannelServices.RegisterChannel will bind
that port number to the TCP/IP stack on the operating system.TcpServerChannel channel = new TcpServerChannel(9932);
ChannelServices.RegisterChannel(channel);
The other type of channel that can be set up is HTTP and is done simply by using the HttpServerChannel
object in the System.Runtime.Remoting.Channels.Http namespace. The differances between using an HTTP and
a TCP channel can be summed up simply--If you are working with a local network connection it's best to use
TCP because of it's enhanced performance over using HTTP. If you're working over the internet HTTP can
sometimes be the only choice depending on firewall configurations. Keep in mind that if you do have control
over the firewall, almost all firewalls can be configured to allow TCP traffic through on the port you've chosen to
use for you object in addition to the DNAT you'll likely need to employ in most situations. If you don't know how
to alter these rules, ask your system administrator.RemotingConfiguration.RegisterWellKnownServiceType(typeof(ResumeLoader),
"ResumeLoader", WellKnownObjectMode.SingleCall);
This line sets a few prameters on your service and binds the object you want to the name you want to use on this
remote object. The first parameter is the object you're binding, typeof(ResumeLoader). The second parameter is
the String that is the name for the object on the TCP or HTTP channel. For example, remote clients would refer to the above object as "tcp://localhost:9932/ResumeLoader". The third parameter tells the container what should be done with the object
when a request for the object comes in. WellKnownObjectMode.Single call makes a new instance of the object
for each client while WellKnownObjectMode.Singleton uses one instance of the object for all callers.
The complete code for the object is below.
using System;
using System.Runtime;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
using System.Data.SqlClient;
using DotNetRemoteTest;
namespace ResumeServerServer
{
public class ResumeSuperServer
{
public static void Main(String[] args)
{
TcpServerChannel channel = new TcpServerChannel(9932);
ChannelServices.RegisterChannel(channel);
RemotingConfiguration.RegisterWellKnownServiceType(typeof(ResumeLoader),
"ResumeLoader", WellKnownObjectMode.SingleCall);
System.Console.WriteLine("Press Any Key");
System.Console.ReadLine();
}
}
}
Compile this program and note the location of the generated .EXE file.
Step 3: Create The Remote Client
The ResumeClinet object is our test user of our newly created ResumeSuperServer remote object. To start this
project go to File->New->Project. Choose a Console Application as the application type and enter "ResumeClient" as the project's name. As in step 2, make sure you add a referance to our shared DLL created in step 1 and the
System.Runtime.Remoting DLL.
The code below has two lines of particular interest to .NET remoting. The first line creates a TCP client channel.
This channel is not bound to a port. The seond line actually gets a referance to our remote ResumeLoader object.
The Activator.GetObject method returns a type of Object that we can then cast into our ResumeLoader. The parameters we pass in are extremely similar to what we passed to the RemotingConfiguration object on the server project. The first parameter is the Type of the object, the second is the URI of our remote object.
ChannelServices.RegisterChannel(new TcpClientChannel());
ResumeLoader loader = (ResumeLoader)Activator.GetObject(
typeof(ResumeLoader), "tcp://localhost:9932/ResumeLoader");
The complete code for our ResumeClient is below.
using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
using DotNetRemoteTest;
namespace ResumeClient
{
public class ResumeClient
{
public static void Main(string[] args)
{
ChannelServices.RegisterChannel(new TcpClientChannel());
ResumeLoader loader = (ResumeLoader)Activator.GetObject(
typeof(ResumeServer), "tcp://localhost:9932/ResumeLoader");
if(rs==null)
{ Console.WriteLine("Unable to get remote referance"); }
else
{
Resume resume = loader.GetResumeByUserID(1);
Console.WriteLine("ResumeID:"+ resume.ResumeID);
Console.WriteLine("UserID:"+ resume.UserID);
Console.WriteLine("Title:"+ resume.Title);
Console.WriteLine("Body:"+ resume.Body);
}
Console.ReadLine();//Keep the window from closing before we can read the result.
}//END OF MAIN METHOD
}//END OF ResumeClient Object
}//END OF ResumeClientNamespace
Compile this project and note the location of the executable.
Testing It Out
Create a table in your database. with the following basic schema:Table Name-Resume
ResumeID, numeric (autonumber)
UserID, numeric
Title, Char(30)
Body, Text
Double click on the Server exe that we created in step 2, then double click on the Client executable we created in
step 3. If everything works out, you should see the row from the database that had the ResumeID of 1.
In closing, .NET remoting is very simple to use and can provide an excellent way to work with resources across
your network or the internet.
Summary
.NET Remoting is a powerful way to enable interprocess communication. It is more complicated to program against than Web services. You need to decide for yourself whether your standard architecture is to use .NET Remoting or Web services.
Future Columns
The next column will be on the use of encryption in the .NET framework. We'll take a look at some of the encryption algorithms available in the framework and ways to use them. If you have a particular topic in mind please email me at vigneshwar_gupta@yahoo.com