Introduction
There are several ways to execute a process on other computers or other application domains which are related to each other through a network (LAN or Internet). Remote executing means running a process in another computer without using any resource or CPU load in the local computer. Using a client/server application is the only solution for doing so. We can create and use such applications in a wide range of platforms and methodologies. For example, we can write a client and a server program which communicate by sending and receiving network packets. In this case, we have to deal with creating sockets, passing the data packets over the network socket, and allowing the component on the server side to accept the packet, process the request, and pass the proper data back to the client component. Also, we can use DCOM (Distributed Component Object Model), which is a programming model for calling methods of objects running on a server.
Another way is using .NET Remoting which is built on the .NET Framework technology. In this case, a managed object can be accessed by a remote client. When a client makes a call to an object of the server program, a proxy object grabs the call first. The proxy object behaves like the remote object, except that the proxy object is running locally. As you may know, there is also the XML Web Services technology built on the .NET Framework which in some cases is the same as .NET Remoting. It is not very ideal to use Web Services for our task (Remote Executing).
Now, here is a question that why I have used .NET Remoting for this task? Remoting, as supported by the .NET Framework, allows applications to share class data and methods among computers on the network, similar to the concept of web services. By Remoting, class methods can be hosted on a network device and called from any CLR based program. When creating sockets, we have to worry about maintaining an open port connection. Typically, the application on the server side would register on a port and wait for incoming messages. If the client application is configured to do something special by receiving incoming messages, it's required that the incoming message be checked and verified for doing the proper task. But in .NET Remoting, we don't have to worry about maintaining the open port and also doing the extra task of verifying the incoming messages. The trouble with DCOM is that the model is intended for communication between COM objects. COM is not the technology on which the .NET Framework is built. So it missed up some great functionality and flexibility that exists in the .NET Framework.
A Brief introduction to .NET Remoting
Using the .NET Remoting architecture leads to reduction in the amount of time that you have to spend working on the networking issues. Remoting provides you with a number of choices such as configuration of the communication used. Configuration areas are the choice of channel, type of hosting application, the activation model, the configuration method, and the method of exposing the server metadata to the client application. The channel is the means of communication used by an application to call to a remote object; the selection is between HTTP and TCP. The HTTP channel which uses SOAP formatting, is mostly used for Internet communication where firewalls need to be negotiated. The TCP channel which uses binary formatting, has a performance gain by using direct socket connections over an arbitrary port selected by the developer; however, it may not be the solution for network communication through firewalls.
Remote Execution
This .NET Remoting based application includes three different programs:
- Server or remotable type,
- Listening or host application, and
- Client or calling application domain.
The host and the client are executable applications and use all types of application domains. The host application can even be hosted in a Windows Service and IIS (if HTTP channel is used). The server program is a class library which will be referenced in the client applications.
Building a Remote Server
The type of this remote server project is a class library. In order to enable objects in the client application to use an instance of the server class, it must be inherited from MarshalByRefObject
. The following code shows the RemoteExecuteServer
class declaration and the implementation:
public class RemoteExecuteServer : MarshalByRefObject
{
public void Run()
{
Process myProcess = new Process();
ProcessStartInfo info = new ProcessStartInfo("notepad.exe");
myProcess.StartInfo = info;
myProcess.Start();
myProcess.Close();
}
}
The class is inherited from MarshalByRefObject
and contains a method to simply run a process.
Building a Host Application
By itself, the remotable class defined above is not special. To enable objects in other application domains to create instances of this object remotely, you must build a host or listener application to choose and register a channel, and also register your type with the .NET remoting system so that it can use your channel to listen for requests for your type.
As is mentioned above, you can build host applications using different types of application domains such as Windows Forms application, an ASP.NET Web application, a console application, a Windows Service, or any other managed application domain. I use a simple Windows Form for this task. Because remote configuration is accomplished on a per-application-domain basis, the application domain must be running to listen for requests.
Here, I've created a Windows Forms application as a host application. The host application only needs to configure and register the remote server type. For doing so, I've simply put the following code to the Form_Load()
method:
private void Form1_Load(object sender, System.EventArgs e)
{
RemotingConfiguration.Configure(@"..\..\REHost.exe.config");
}
The host application must be able to find the REHost.exe.config file to load the configuration for the RemoteExecuteServer
class. This configuration file should be saved in the same directory as the host application exe file. Note that you have to copy the configuration file beside the host application exe file in the debug or release directory. Otherwise, you�ll therefore need to use the �..\..\� syntax in the file path to reach the configuration file in your source code directory.
The required settings for this .NET Remoting system can be achieved not only inside the code but with configuration files as well. These files are XML formatted so that they are easily readable and easily parsed by the .NET Framework. The Remoting configuration files are needed for the host and the client applications. I've created the REHost.exe.config file beside my host application.
The following code shows the REHost.exe.config configuration file for this host application domain:
<configuration>
<system.runtime.remoting>
<application name="ReServer">
<service>
<wellknown
mode="Singleton"
type="ReServer.RemoteExecuteServer,ReServer"
objectUri="RemoteExecuteServer "
/>
</service>
<channels>
<channel ref="tcp" port="8084"/>
</channels>
</application>
<debug loadTypes="true" />
</system.runtime.remoting>
</configuration>
The host application uses the information in this file to listen for and route remote requests to an instance of a remotable type. The file specifies the Singleton server-activation mode, the type name, and the assembly of the type on behalf of which it is to listen, and the object URI or external name of the object. The file also tells the Remoting system to listen for requests on port 8084 using the system-provided TcpChannel
.
Building a Client Application
The client application is the last step for this demonstration. In this example, the client application will be a standard Windows application with a main form, but it could also be any other type of .NET application.
To build a client of the defined remote type which is hosted by the host application created in the previous part, my application must register itself as a client for that remote object, and then invoke it within the client's application domain. The .NET Remoting system will intercept your client calls, forward them to the remote object, and return the results to your client.
For a client program to use the class methods in the remote class, the client must know what methods are available and how to access them. The client program must be compiled with a proxy class to specify the format of the remote class methods. All references to the remote class methods are passed to the proxy class for processing. In order to create and consume that proxy class, the client application needs a reference to the remoting application�s DLL.
An object of the remote class (RemoteExecuteServer
) is declared in Form1
and the related code for registration and configuration are written in the constructor of the Form1
class, as shown in the following code example:
try
{
RemotingConfiguration.Configure("REClient.exe.config");
}
catch (Exception e)
{
}
remObject = new RemoteExecuteServer();
Actually, the object will be created in the server and only its reference will be redirected to the client. Here in the button click event handler of the client application, I've called the Run()
method from the remote server class.
private void btnRun_Click(object sender, System.EventArgs e)
{
remObject.Run();
}
As you can see in the example, the client class must be able to find the REClient.exe.config file to load the configuration for the remote class. This file should be saved in the same directory as the client executable file.
The following code example shows the REClient.exe.config configuration file for this host application domain.
<configuration>
<system.runtime.remoting>
<application name="REClient">
<client>
<wellknown type="REServer.RemoteExecuteServer, REServer"
url="tcp://ServerName:8080/RemoteExecuteServer"/>
</client>
<channels>
<channel ref="tcp"
port="8084"/>
</channels>
</application>
</system.runtime.remoting>
</configuration>
It's better to create the above projects in one solution. After you compile the entire solution, first of all, you should run the host application, and then you can run the client application and execute a process in the server domain.
Although the above .NET Remoting application is a good start and has shown how to execute a remote process, some areas need improving in order for the application to become more flexible. One of the important features we can add is an ability to be notified from the server. Assume, we execute a process on the server domain but somehow the process gets killed, so how can we be informed about that? This will be the topic of the next issue of this article.
Notification from Server
Due to some reasons, we might want to build an event-driven Remoting application. It means the server application signals the occurrence of an action for the client application to do a special task. Here, we want to add functionality to notify the client when the process execution (executed by the client in a server domain) status is changed.
One of the solutions for doing so is using Events. In event communication, what is needed is an intermediary between the source and the receiver. The .NET Framework defines a special type called Delegate
that provides the functionality of a function pointer.
If the end of the process execution is the only reason to notify the client application, we can use Asynchronous Remoting. So far, all the method invocations that are used in this example have been synchronous. In a synchronous method call, the thread that executes the method call waits until the called method finishes execution. Such types of synchronous calls can take a long time and make the other process in the client application to freeze while it is waiting for a long process to finish executing on the server. If the client is a Windows Forms application, it makes the user interface very non-responsive. An asynchronous method calls the method and returns immediately, so it leaves the invoked method to complete its execution.
Asynchronous programming like Events is implemented with the help of delegate types. Both the server and host applications that I created above no longer need additional code that supports asynchronous method calls. The asynchronous call functionality is completely dependent on the client application. So I must change my client application to be able to call a remote method asynchronously. Delegates can store references to methods. We declare a delegate and its object as follows in the client class definition. This delegate is capable of invoking methods with no parameter and return type.
delegate void LongRuningProcess();
LongRuningProcess longProcess;
AsyncCallback ab;
IAsyncResult ar;
This delegate can hold references to the Run()
method which is declared in the remote server class. The objects of AsyncCallback
delegate and IAsyncResult
interface are defined as well. AsyncCallback
provides a way for client applications to complete an asynchronous operation. This callback delegate is supplied to the client when the asynchronous operation is initiated. In fact, this delegate is used to store the reference to the callback method that we want to execute when the remote execution finishes.
AsyncCallback
uses the IAsyncResult
interface to obtain the status of the asynchronous operation. Here, the IAsyncResult
interface is used to monitor an asynchronous call and relate the beginning and the end of an asynchronous method call.
Now, we need some methods to initialize the remote server and run a remote process. For doing so, we can write the following code:
public void InitRemoteServer()
{
try
{
RemotingConfiguration.Configure("REClient.exe.config");
}
catch (Exception e)
{
}
RemoteExecuteServer remObject = new RemoteExecuteServer();
ab = new AsyncCallback(OnExitProcessExecution);
longProcess = new LongRuningProcess(remObject.Run);
}
The above method instantiates a remote object and instantiates the AsyncCallback
delegate object to hold the reference of the OnExitProcessExecution
callback method which is called when the asynchronous call is completed. A LongRunningProcess
delegate object is also created to hold the reference of the remote Run()
method.
After we have the delegate object available, we can call its BeginInvoke()
method to call the remote Run()
method asynchronously, as shown here:
ar = longProcess.BeginInvoke(ab, null);
Now, by running a remote process as above when the process execution is ends, the OnExitProcessExecution
callback method is called so that the client application can be notified about the finishing of the remote process execution.
void OnExitProcessExecution(IAsyncResult ar)
{
longProcess.EndInvoke(ar);
}
The EndInvoke
method gets the return value of the asynchronous operation represented by the IAsyncResult
passed by this interface. If the asynchronous operation has not completed, this method will wait until the result is available.
Conclusion
As is stated above, there are several methods to make a connection between a server application and its clients. I described the use of .NET Remoting as a proper solution in this article. What I emphasized on throughout this article is receiving notifications from the server by clients. Although I have used the Asynchronous Callback method this is not the only way for doing so. Surely, there are different solutions that can be used other than using the Asynchronous Callback method. Using Events is one solution which you can take advantage of in your client-server applications. In this matter, you can use the proper event handlers in your client program which are called when specific tasks occur in the server program.