Understanding SAO and CAO Activation Methods in .NET Remoting
 
Published: 28 Apr 2008
Abstract
In this article Abhishek differentiates Server Activated Objects (SAO) and Client Activated Objects (CAO) in .NET Remoting. He examines the implementation procedures of these activation methods separately in Visual Basic 2005 using examples. Abhishek also provides the complete project along with the article to enable you to test drive the application instantly.
by Abhishek Kumar Singh
Feedback
Average Rating: This article has not yet been rated.
Views (Total / Last 10 Days): 45609/ 91

Introduction

Remoting is one of the important and advance features in .NET, which is an infrastructure used to provide inter-process or cross domain communication. Instead of giving the definition and theoretical explanations of its component, I will describe the practical uses and take-care points which we must know to successfully implement remoting applications using .NET. I have used Visual Basic 2005 for examples in this article.

As we know, in general we should have three components to develop remote system.

·         A remotable object (classes which defines remote methods)

·         A remote server host which can listen client request for the object on a given port

·         A remote client application which can request to the host server to call remote methods

·         A remote server host and client application could be any application like console, windows application, or windows server. In my example I will use console application for both.

Let us come to the major part of Remoting - remotable object. You must know that there are two main types of remotable object.

Marshal-by-value

We use it for cross process communication usually in the same domain, in LAN, or in our daily programming on the call by value concept.

Marshal-by-reference (MarshalByRefObject in .NET)

We use it in remoting for cross domain communication usually over WAN or internet on the call by reference concept.

MarshalByRef objects can be activated in two ways:

Server Activation (SAO)

·         SAO-SingleCall

·         SAO-Singleton

Client Activation (CAO)

SAO - SingleCall activation implementation

In SAO, SingleCall remote object activation, each remote method call creates new instance of the remote object at the server end. When a client finishes its remote calls, the server deletes object instance which was created for it. Therefore, we cannot do state management at the server among multiple clients. In other words, we cannot share variable or manage state among different calls since a new object initializes for each client remote method call. It also means that the server object constructor New() executes for each remote object creation at the client so as for each remote method call by client.

For example, if client A halts at some line of code in the execution then a call from other client (say B, C, D, etc.) does not wait in any queue at the server and so does not wait for client A to finish its execution. All executes are an independent thread.

Now we will see how we can implement SAO in .NET. For better understanding I have separated the remote object creation code and remote class/method implementation code in Module and Class file respectively in this example. There are few commented lines which I used in my tests, please remove it if you want.

Listing 1: SAO (Well-known)-SingleCall Module and Main() implementation for server in VB.NET (Module1.vb)

Imports System
Imports System.IO
Imports System.Runtime.Remoting
Imports System.Runtime.Remoting.Channels
Imports System.Runtime.Remoting.Channels.Http
Module Module1
    Sub Main()
 
        ' ------- SAO Singleton ----------
        Dim channel As HttpChannel = New HttpChannel(1)
        Try
            ChannelServices.RegisterChannel(channel, False)
            Dim serverType As Type = Type.GetType("RemoteServer.CServer")
            RemotingConfiguration.RegisterWellKnownServiceType(serverType, _
                "TestRemoteServer", WellKnownObjectMode.SingleCall)
 
            Console.WriteLine("Remote Service started...Press Enter key to stop." _
                & Now.ToString())
            Console.Read()
 
        Catch ex As Exception
            Console.WriteLine(ex.ToString())
            Console.Read()
            ChannelServices.UnregisterChannel(channel)
        Finally
 
        End Try
 
        ' ---------------------------------
    End Sub
End Module

Listing 2: SAO (Well-known)-SingleCall Class implementation for server in VB.NET (Server.vb)

Imports System.Runtime.Remoting
Imports System.Runtime.Remoting.Channels
Imports System.Runtime.Remoting.Channels.Http
Imports System.Security.Permissions
Imports System.Runtime.Remoting.Lifetime
 
Public Class CServer
    Inherits MarshalByRefObject
    Implements ILibrary.IServer
    Public sClientCallsInOrder As Integer = 0
 
    Public Function GetServerMessage(ByVal ClientName As StringAs String _
      Implements ILibrary.IServer.GetServerMessage
        sClientCallsInOrder = sClientCallsInOrder + 1
        Console.WriteLine("**Call By :: " & sClientCallsInOrder & " :: " & _
          ClientName & " :: " & Now.ToString())
        Dim ResponseMessage As String = Nothing
        If Not ClientName Is Nothing Then
            If ClientName.ToUpper() = "CLIENT_A" Then
                Dim x
                x = 10
                ' Threading.Thread.Sleep(20000)
            End If
            ResponseMessage = "Hello " & ClientName & "! Welcome at Remote Server"
        End If
        'sClientCallsInOrder = sClientCallsInOrder & " :: [" & ClientName & "]"
        Console.WriteLine("   Finish :: " & sClientCallsInOrder & " :: " & _
          ClientName & " :: " & Now.ToString())
 
        Return (ResponseMessage)
    End Function
 
    Public Sub New()
        Console.WriteLine("Object constructor called :: " & Now.ToString())
    End Sub
 
    Protected Overrides Sub Finalize()
        MyBase.Finalize()
    End Sub
End Class
SAO - Singleton activation implementation

If you have implementation code ready for server in SAO-SingleCall we need a very small change to change it to SAO-Singleton activation. For example, in Code Listing 1 which is implemented for SAO-SingleCall, we can just modify WellKnownObjectMode.SingleCall to WellKnownObjectMode.Singleton in the Module1.vb. So our complete Module1.vb code could be as given below.

Listing 3: Module1.vb for SAO-Singleton activation

Imports System
Imports System.IO
Imports System.Runtime.Remoting
Imports System.Runtime.Remoting.Channels
Imports System.Runtime.Remoting.Channels.Http
Module Module1
    Sub Main()
 
        ' ------- SAO Singleton ----------
        Dim channel As HttpChannel = New HttpChannel(1)
        Try
            ChannelServices.RegisterChannel(channel, False)
            Dim serverType As Type = Type.GetType("RemoteServer.CServer")
            RemotingConfiguration.RegisterWellKnownServiceType(serverType, _
                "TestRemoteServer", WellKnownObjectMode.Singleton)
 
            Console.WriteLine("Remote Service started...Press Enter key to stop." _
              & Now.ToString())
            Console.Read()
        Catch ex As Exception
            Console.WriteLine(ex.ToString())
            Console.Read()
            ChannelServices.UnregisterChannel(channel)
        Finally
        End Try
        ' ---------------------------------
    End Sub
End Module

At minimum we can leave Server.vb (Listing 2) unchanged for the test.

Idea behind Singleton activation

In the case of SAO, Singleton activation, the server creates single and shared remote instance for all clients. So the remote object's constructor New() will run only once at the server. For example, if client A halts at the server at some point of time, then a remote call from other clients (say B,C,D, etc.) will remain in the queue and wait until client A finishes the execution at the server. When a call from client A completes its execution at server then other client's calls will be processed by picking it from the waiting queue.

In the example given above, let us consider a situation. At certain point of execution if client A uses Thread.Sleep(20000) to sleep for 20 seconds, other client's remote method calls continues normally by picking one-by-one from the queue. Client A will resume its execution after 20 seconds.

Therefore, in Singleton activation we can do state management and do variables/values sharing among clients. For example, we can implement to count total number of client calls using shared member variable at server and incrementing it by 1 in the remote method.

All the above scenarios are possible because the server creates only one instance of an object for all clients in SAO - Singleton activation.

Overall Processing Steps in SAO - Singleton

Let us assume that Server S starts listening for the client's call. The very first client, CLIENT_A, creates a remote object in OnStart(). At this moment no activity happens at the server, meaning the remote object instance creation does not request any from server. CLIENT_A makes the first remote method call, then server initializes its class object (so runs default constructor New()) at the first time. Hence, the server initializes all member variables ONLY at the very first remote method call from the very first client. Remember, this happens only once unless and until the server remote application is stopped and started again. This is true even after some time if no client calls come for long (there is a default idle timeout set for the server which I will discuss later), and any new/old client starts method call again.

That is why we can do state management among several clients in SAO-Singleton activation, since member variable remains common and active in the server memory during its lifetime. All these are possible because the server has only one remote object instance in the member at any case.

Note

SAO can support only default constructor. SAO can never be more than one instance of the server object at any point of time.

But Singleton server object would be garbage collected at the server if it remains inactive up to its default LEASE-BASE LIFETIME. Default time is 5 minutes for the first time and 2 minutes for the rest. Lease time is the duration unto which object should remain in memory, preventing it from automatic garbage collection.

Once the server object instance is created it will exist and server all clients unless and until the server application gets stopped intentionally or unintentionally.

To increase or decrease this default lifetime, add the following namespace in server class:

Imports System.Runtime.Remoting.Lifetime.

You can override the InitializeLifetimeService method as given below (this sample code is taken from msdn).

Listing 4: Overriding InitializeLifetimeService method

Public Overrides Function InitializeLifetimeService() As Object
        Dim lease As ILease = CType(MyBase.InitializeLifetimeService(), ILease)
        If lease.CurrentState = LeaseState.Initial Then
            lease.InitialLeaseTime = TimeSpan.FromMinutes(1)
            lease.SponsorshipTimeout = TimeSpan.FromMinutes(2)
            lease.RenewOnCallTime = TimeSpan.FromSeconds(2)
        End If
        Return lease
End Function

You can explore more on it if you wish. See MSDN for more information about it.

For infinite lease time set ease.InitialLeaseTime = Nothing.

SAO Client implementation for both SAO-SingleCall/Singleton

Listing 5: Module1.vb for remote client

Module Module1
    Sub Main()
        Dim oClientService As New ClientService
        oClientService.OnStart()
        Console.Read()
    End Sub
End Module

Listing 6: ClientService.vb for remote client

Imports System.Data
Imports System.Runtime.Remoting
Imports System.Runtime.Remoting.Channels
Imports System.Runtime.Remoting.Channels.Http
 
Public Class ClientService
 
    Private m_oTmrSend As System.Threading.Timer
    Private m_oRemote As ILibrary.IServer
    Private oChannel As HttpChannel = Nothing
    Private m_iTimeInterval As Integer
    Private m_Busy As Boolean = False
  
    Public Sub OnStart()
 
        oChannel = New HttpChannel(0)
        ChannelServices.RegisterChannel(oChannel, False)
 
        m_oRemote = Activator.GetObject(GetType(ILibrary.IServer), _
     "http://" & "localhost:1" & "/TestRemoteServer")
 
        Console.WriteLine(" Remote Client Started at " & Now.ToString())
 
        Console.WriteLine("CLIENT_A :: Channel Registered :" & _
          oChannel.ChannelName)
        Dim timerDelegate As Threading.TimerCallback = AddressOf DoAction
        m_oTmrSend = New System.Threading.Timer(timerDelegate, Me, 0, 10000)
    End Sub
    Public Sub DoAction(ByVal sender As Object)
Console.WriteLine("Received from Server: " & _
   m_oRemote.GetServerMessage("Client_A"))
 
     End Sub
 
    Protected Overrides Sub Finalize()
        MyBase.Finalize()
        ChannelServices.UnregisterChannel(oChannel)
        Console.WriteLine("CLIENT_A :: Channel Unregistered :: " & Now.ToString())
 
    End Sub
End Class
CAO activation implementation

In CAO, the server object's lifetime is controlled by clients, which is the reason we do not normally prefer this activation method in remoting applications. Not preferring CAO is good when the server does most of the critical business logic and execution. But when the client performs critical tasks where data loss may affect the system, then we may need to choose CAO instead of SAO.

When we register ActivatedClientType at the client then we create the remote object using new operator- server initialize instance of remote object. So it means the server executes default constructor.

The server executes the constructor for each client only once unless and until the client gets restarted or creates new instance of remote object again for subsequent remote method call.

Since the server creates the remote object instance for each client separately, class variables can be common for specific clients only. It means we can have state management for client by client separately, which will not be common to all clients. It also means that we can manage state of particular among all method calls of the same client at the server. Therefore, in CAO remoting implementation we can not have single server object intendance to serve all clients.

In CAO remoting implementation server object never gets garbage collected even if it remains idle for long or all clients stop invoking remote methods. The lifetime of server object instance is controlled by the respective clients. CAO is also called statefull objects.

A Client can use either of the following ways to create CAO object at the server

Using New operator to create object instance

Limitation in this approach - Unlike SAO’s, you can not use shared interface or base classes. It means that you will need to provide your compiled server class to the client application. Sharing the actual server implementation to the client is not a feasible way. One better option would be to use soapsuds on server object to get the metadata which could be used by the client.

Using Activator.CreateInstance method

You can use it to create remote object instance by passing constructor parameters.

Creating metadata of server object for CAO specific client

1. Go to the Visual Studio command prompt. In my computer it is as:

D:\Program Files\Microsoft Visual Studio 8\SDK\v2.0>.

Move to the assembly folder of the project in the command prompt as given below for my sample.

D:\Program Files\Microsoft Visual Studio 8\SDK\v2.0>cd D:\RemoteServer\RemoteServer\bin\Release

2. Execute soapsuds.exe command with the parameter as given below.

D:\Program Files\Microsoft Visual Studio 8\SDK\v2.0>soapsuds -ia:RemoteServer -nowp -oa:Remote_Server_metadata.dll

Details:

* Soapsuds.exe: It is a tool provided in Microsoft .Net Framework to create a metadata (proxy) of server object (service).

* ia: An option to set with soapsuds.exe to specify the assembly for which metadata is to be generated. The assembly name extension (.dll or .exe) does not require mentioning here.

* RemoteServer: The server assembly name used in this article’s example. Replace it with yours.

* nowp: To specify, not to create, wrapped proxy.

* oa: To specify the target file name (metadata file).

* Remote_Server_metadata.dll: Target file name used for article’s example. Replace it with your suitable name.

Figure 1: Using soapsuds.exe at .Net SDK command prompt to create server metadata

After a successful execution of command you would see two new files in the Release directory as shown in the figure.

Figure 2: Metadata files of server after executing soapsuds.exe

You can go through the options for soapsuds in detail on MSDN.

In the client application add reference to the metadata DLL.

Figure 3: Adding server metadata file into the client application

CAO using Activator.CreateInstance()

Listing 7: ClientService.vb implementation using Activator.CreateInstance() which uses metadata referenced file to establish remote connection and call remote method in CAO mechanism

Imports System.Data
Imports System.Runtime.Remoting
Imports System.Runtime.Remoting.Channels
Imports System.Runtime.Remoting.Channels.Http
 
Public Class ClientService
 
    Private m_oTmrSend As System.Threading.Timer
    Private m_oRemote As RemoteServer.Server
    Private oChannel As HttpChannel = Nothing
    Private m_iTimeInterval As Integer
 
    Public Sub OnStart()
 
        oChannel = New HttpChannel(0)
        ChannelServices.RegisterChannel(oChannel, False)
        Dim url() = {New System.Runtime.Remoting.Activation.UrlAttribute( _
              "http://localhost:1/TestServerForCAOClient")}
        Try
            m_oRemote = CType( _
              Activator.CreateInstance(GetType(RemoteServer.Server), Nothing, url), _
            RemoteServer.Server)
 
            Console.WriteLine(" Remote Client Started at " & Now.ToString())
 
            Console.WriteLine("CLIENT_A :: Channel Registered :" & _
              oChannel.ChannelName)
            Dim timerDelegate As Threading.TimerCallback = AddressOf DoAction
            m_oTmrSend = New System.Threading.Timer(timerDelegate, Me, 0, 10000)
        Catch ex As Exception
            Console.WriteLine(ex.ToString())
            Console.Read()
        End Try
    End Sub
    Public Sub DoAction(ByVal sender As Object)
        Console.WriteLine("Received from Server: " & _
          m_oRemote.GetServerMessage("Client_A"))
    End Sub
 
    Protected Overrides Sub Finalize()
        MyBase.Finalize()
        ChannelServices.UnregisterChannel(oChannel)
        Console.WriteLine("CLIENT_A :: Channel Unregistered :: " & Now.ToString())
    End Sub
End Class

CAO using new operator

We can implement client class using new to create server object in CAO as given below.

Listing 8: ClientService.vb implementation by using new to create CAO object

Imports System.Data
Imports System.Runtime.Remoting
Imports System.Runtime.Remoting.Channels
Imports System.Runtime.Remoting.Channels.Http
 
Public Class ClientService
 
    Private m_oTmrSend As System.Threading.Timer
    Private m_oRemote As RemoteServer.Server
    Private oChannel As HttpChannel = Nothing
    Private m_iTimeInterval As Integer
 
    Public Sub OnStart()
 
        oChannel = New HttpChannel(0)
        ChannelServices.RegisterChannel(oChannel, False)
       Try
            RemotingConfiguration.RegisterActivatedClientType( _
        GetType(RemoteServer.Server), "http://localhost:1/TestServerForCAOClient")
            m_oRemote = New RemoteServer.Server
           
            Console.WriteLine(" Remote Client Started at " & Now.ToString())
 
            Console.WriteLine("CLIENT_A :: Channel Registered :" & _
              oChannel.ChannelName)
            Dim timerDelegate As Threading.TimerCallback = AddressOf DoAction
            m_oTmrSend = New System.Threading.Timer(timerDelegate, Me, 0, 10000)
        Catch ex As Exception
            Console.WriteLine(ex.ToString())
            Console.Read()
        End Try
    End Sub
 
 
    Public Sub DoAction(ByVal sender As Object)
        Console.WriteLine("Received from Server: " & _
          m_oRemote.GetServerMessage("Client_A"))
    End Sub
 
    Protected Overrides Sub Finalize()
        MyBase.Finalize()
        ChannelServices.UnregisterChannel(oChannel)
        Console.WriteLine("CLIENT_A :: Channel Unregistered :: " & Now.ToString())
 
    End Sub
End Class
Downloads

Conclusion

When implementing remoting applications we must be sure that the used activation model would be giving optimal performance. Now we should have remote calling and data transfer as the main objective, but also know how the server instance would be activate and managed in its life cycle by understanding the feature of SAO and CAO. Certainly deciding which one to use depends upon different factors, like sensitivity of data, synchronization, sharing requirements among clients, complexity of communication, etc.

You comments and suggestion are appreciated. Thank you for giving your time in ASPAlliance.

Abhishek Kumar Singh



User Comments

No comments posted yet.

Product Spotlight
Product Spotlight 





Community Advice: ASP | SQL | XML | Regular Expressions | Windows


©Copyright 1998-2021 ASPAlliance.com  |  Page Processed at 2021-03-01 6:23:11 PM  AspAlliance Recent Articles RSS Feed
About ASPAlliance | Newsgroups | Advertise | Authors | Email Lists | Feedback | Link To Us | Privacy | Search