using System;
using System.Runtime.Remoting; //General remoting support.
using System.Runtime.Remoting.Messaging; //For [OneWay] attribute.
using System.Runtime.Remoting.Activation; //For UrlAttribute
using System.Runtime.Remoting.Channels; //Programmatic channel registration.
using System.Runtime.Remoting.Channels.Tcp;
using System.Runtime.Remoting.Channels.Http;
using System.Threading;
using nsCRemoteObj;
using nsCRemoteObjInterface; //Referencing the interface assembly only.
namespace nsCRemoteObjClient
{ //nsCRemoteObjClient
class CRemoteObjClient
{ //CRemoteObjClient
//(a) Delegate to CRemoteObj.SetupUser():
//(b) Signateure: public string SetupUser(string sname, string spasswd, out string sTime)
//(c) Refer to <BLOCK 1-C>
public delegate string SetupUserDelegate(string string1, string string2, out string string3);
[STAThread]
static void Main(string[] args)
{ //Main()
//Miscellaneous:
Console.WriteLine("CRemoteObjClient started. Time:{0}", System.DateTime.Now.ToString() );
Console.WriteLine();
//There're two ways to publish a remote object:
// * Scenario 1: server-activated remote object
// * Scenario 2: client-activated remote object
//*****************************************************//
//Scenario 1: If the remote object is published as "server-activated" object (wellknown object), use Activator.GetObject()
// If so, client only need to reference interface assembly CRemoteObjInterface - there's no need to reference the remote object assembly (CRemoteObj).
//<BLOCK 1>
try
{ //try-A
//<BLOCK 1-A> Retrieve interface/proxy:
//Register channel:
ChannelServices.RegisterChannel( new HttpChannel() );
//NOTE:
//(1) We also illustrates how to use interface class of remote object class, as opposed to the remote object class itself.
//(2) You can also call RemotingConfiguration.Configure("cremoteobjclient.config") instead of Activator.GetObject.
// In that case, config file name: "/config file/cremoteobjclient(wellknown).config"
IRemoteObj obj = (IRemoteObj) Activator.GetObject(
typeof(IRemoteObj),
"http://localhost:8085/CRemoteObjURI"
);
//<BLOCK 1-A END>
//******************* Do something with your proxy "obj" ***********************
//<BLOCK 1-B>
// * Invoke method on remote object through interface.
// * LeaseTime
//NOTE: Since obj is of type: IRemoteObj (it's an interface), we can't do this:
// obj.evStatus += event_delegate;
// But working with interface instead of the remote class itself allows greater separation between provider and client.
for(int i=0; i<100; i++)
{ //for-loop-A
//ATTENTION:
//(1) If you publish your remote object with mode=SingleCall, everytime you call AuthenticateLoser, you get a new instance of the remote object.
//This can be seen from server side console - pay attention to object ID: a different instance/objID every time the method is invoked.
//(2) To see the effect of mode=Singleton (WellKnownObjectMode enum), modify server side config file: "cremoteobjserver(wellknown).config" <lifetime> tag, set:
// leaseTime="100MS" sponsorshipTimeout="0MS" renewOnCallTime="100MS" leaseManagerPollTime="10MS"
// Default LeaseTime = 100 millisecond, meaning, your Singleton obj will be marked ready-to-dispose 100 milli-sec after last time you invoke a method on the remote object.
// Invoke AuthenticateLoser after that, you will get a different instance of the remote object.
// MSDN Reference: "Lifetime Leases"
//(3) We've hard coded a few user/passwd in our remote object class, CRemoteObj.
// The data is packaged in a HashTable object inside CRemoteObj.AuthenticateLoser() - key: user name; values: password.
Console.WriteLine("i: {0}; Authentication result: {1}",
i.ToString(),
obj.AuthenticateLoser("Dexter", "AceInTheOpen")
);
Thread.Sleep(101); //To see the effect of LeaseTime on remote object's lifetime, Sleep for 100 milli-seconds + a little more.
//Pay attention to server console and objID.
} //for-loop-A
//<BLOCK 1-B END>
//<BLOCK 1-C> Asynchronous programming/remoting
/*
//(1) Objectives:
// (a) Invoke method on remote object asynchronously.
// (b) Retrieve return and output value.
//(2) MSDN reference, .NET Framework Developer's Guide:
// (a) "Asynchronous Delegates Programming Sample"
// (b) "Asynchronous Remoting"
// (c) "Asynchronous Programming Overview"
// MSDN online: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpovrasynchronousprogrammingoverview.asp
//(3) Quote from MSDN:
// "Asynchronous programming in a remoting scenario is identical to asynchronous programming in a single application domain or context."
//
// Prequisite: Relevant framework facility: IAsyncResult, WaitHandle, WaitOne, Delegates, BeginInvoke, EndInvoke.
// If you're not familiar with asynchronous programming in general, read this sample first.
SetupUserDelegate d = new SetupUserDelegate(obj.SetupUser);
//Invoke CRemoteObj.SetupUser
string sTime;
IAsyncResult ar = d.BeginInvoke(
"Dexter",
"AceInOpen",
out sTime,
null,
null
);
//Blocks the current thread until obj.AuthenticateLoser completes.
ar.AsyncWaitHandle.WaitOne();
if(ar.IsCompleted)
{
//Retrieve (a) output sTime, (b) return value.
string ret = d.EndInvoke(out sTime, ar);
Console.WriteLine("obj.SetupUser return. sTime: {0} {1}return value: {2}", sTime, Environment.NewLine, ret);
Console.WriteLine();
}
//Passing custom object to/from remote object:
Console.WriteLine("Invoking CRemoteObj.UpdateProfile. Passing custom object to method.");
CProfile p = new CProfile("John", "Kennedy", "JFK", "ace");
CProfile p2 = obj.UpdateProfile(p);
Console.WriteLine("UpdateProfile completed. return p2 passwd: {0}", p2.passwd);
*/
//<BLOCK 1-C END>
} //try-A
catch(Exception err)
{
Console.WriteLine("Error parsing configuration file: {0}", err.ToString());
}
//<BLOCK 1 END>
//<BLOCK 2>
//*****************************************************//
//Scenario 2: if the remote object is published as client-activated
try
{ //try-B
//<BLOCK 2-A>
//OPTION 2a: Use configuration file, then use "new" keyword to instantiate client-activated object.
// Disadvantage of this option is that it requires the client to reference CRemoteObj (server class) assembly - instead of just the interface assembly CRemoteObjInterface.
//NOTE:
//(1) Configure remoting channel and stuff with config file instead.
// File name: "/config file/cremoteobjclient(client).config --> Just rename it to "cremoteobjclient.config" and put it beside the executatble "CRemoteObjClient.exe".
//(2) If you wish to specify security setting in config file, file name of config file must be "application_name.exe.config"
/*
RemotingConfiguration.Configure("cremoteobjclient.config");
CRemoteObj obj = new CRemoteObj();
*/
//<BLOCK 2-A END>
//<BLOCK 2-B>
/*
//OPTION 2b: client activated remote object
ChannelServices.RegisterChannel( new TcpChannel(6792) );
object[] attrs = { new UrlAttribute("tcp://localhost:8086/CRemoteObjServer") };
ObjectHandle handle = (ObjectHandle) Activator.CreateInstance("CRemoteObj", "nsCRemoteObj.CRemoteObj", attrs);
CRemoteObj obj = (CRemoteObj) handle.Unwrap(); //Hey, no need to reference remote object assembly unless we use "new" to instantiate the remote object.
//BUT, since you need to play with events, you can't cast "obj" to "IRemoteObj":
// obj.evStatus += event_delegate;
*/
//<BLOCK 2-B END>
//<BLOCK 2-C>
/*
//******************* Do something with your proxy "obj" ***********************
CStatusEventSink esStatusEvSink = new CStatusEventSink();
StatusEvent event_delegate = new StatusEvent(esStatusEvSink.StatusHandler);
obj.evStatus += event_delegate; //Register sink delegate handler with proxy's event.
for(int i=0; i<5; i++)
{
Console.WriteLine("i: {0}; Authentication result: {1}",
i.ToString(),
obj.AuthenticateLoser("Paul", "12345")
);
}
*/
//<BLOCK 2-C END>
} //try-B
catch(Exception err)
{
Console.WriteLine("Error CRemoteObjClient: {0}", err.ToString());
}
//<BLOCK 2 END>
} //Main()
} //CRemoteObjClient
} //nsCRemoteObjClient