Click to See Complete Forum and Search --> : Remoting Problem


Richard210363
October 24th, 2005, 02:02 PM
I have a problem with my remoting app.

I have a Console app that acts as a server and sets up the remoting channel.
I have a remote object that is called by a client through the channel.
This remote object is a singleton object.

I have a client that uses the channel to call the remote object via the channel.

The remote object runs Windows Media Encoder by instantiating it and then running it.

I run the remote object first from the Console app server using the servers Main method.

I then want any client that uses the channel to use the same singleton remote object.

In this way I can get the client to remotely control an already running app.


However, using the code below, the Console app server starts the remote object that runs the Windows Media Encoder BUT if I then use the remote client the remote object appears to re-start and a new instance of the Windows Media Encoder appears.


My questions are:
Is this the right way to get a remote client to 'talk' to an alrerady running application on a different machine?

Can I do a remote client version of:
SampleObject obj = SampleObject.GetInstance();
so that the Singleton code runs.?

Thanks for any help.
Richard

The code is included below.

Console App Server:
############################################

using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;

namespace CodeGuru.Remoting
{
/// <remarks>
/// Sample server to demonstrate the use of .NET Remoting.
/// </remarks>
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 );


//start the encoder
SampleObject obj = SampleObject.GetInstance();
Console.WriteLine(obj.Reference_String);

System.Console.WriteLine("Press the enter key to exit...");
System.Console.ReadLine();
return 0;
}
}
}


##########################################

Remote Object:
############################################

using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;
using WMEncoderLib;

namespace CodeGuru.Remoting
{
/// <remarks>
/// Sample object to demonstrate the use of .NET Remoting.
/// </remarks>
public class SampleObject : MarshalByRefObject
{
/// <summary>
/// Constructor
/// </summary>
///
private static SampleObject instance;
private static int numOfReference;
// private string code;

private SampleObject()
{
numOfReference = 20;

// Create WMEncoderApp and WMEncoder objects.
WMEncoderApp EncoderApp = new WMEncoderApp();
IWMEncoder Encoder = EncoderApp.Encoder;

// Display the predefined Encoder UI.
EncoderApp.Visible = true;
}

public static SampleObject GetInstance()
{
if(instance == null)
{
instance = new SampleObject();
}
numOfReference++;
return instance;
}

public static int Reference
{
get { return numOfReference; }
}

public string Reference_String
{
get { return numOfReference.ToString(); }
}
}
}


############################################

Remote Client
############################################

using System;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Channels;
using System.Runtime.Remoting.Channels.Tcp;

namespace CodeGuru.Remoting
{
/// <remarks>
/// Sample client to demonstrate the use of .NET Remoting.
/// </remarks>
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(CodeGuru.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.Reference_String);
System.Console.WriteLine("Press the enter key to exit...");
System.Console.ReadLine();
}
return 0;
}
}
}

darwen
October 25th, 2005, 03:29 AM
Singleton access isn't one-client-at-a-time access. Each client exists in its own thread, and so access to the singleton object is multi-threaded.

If you want only one instance of Media Encoder running you'll have to control this case : i.e. first access start it up, and from that point onwards any additional start ups will recognise the fact that there's an instance running and use that instead of creating a new one.

Personally I'd limit the access to one client at a time : it'll make your life a lot easier.

Darwen.

jhammer
October 25th, 2005, 08:34 AM
If the class is a singleton, use singleton instead singlecall. You don't have to actully implement the remote object as singleton, but implement it as you would any other non-singleton class, and declare it as "singleton" instead of "singlecall".

Now, the following class is placed in the client, and is used to get the remote singleton object generically:

public class RemotingHelper
{
internal static bool _isInit = false;
private static IDictionary _wellKnownTypes;

public static object GetObject(Type type)
{
if (!_isInit)
InitTypeCache();
WellKnownClientTypeEntry entr = (WellKnownClientTypeEntry) _wellKnownTypes[type];
if (entr==null)
throw new RemotingException("Type not found");
return Activator.GetObject(entr.ObjectType,entr.ObjectUrl);
}
private static void InitTypeCache()
{
_wellKnownTypes = new Hashtable();
foreach(WellKnownClientTypeEntry entr in RemotingConfiguration.
GetRegisteredWellKnownClientTypes())
_wellKnownTypes.Add(entr.ObjectType,entr);
_isInit = true;
}
}


use RemotingConfiguration.GetObject(typeof(whatever)) to get the remote object.

In the server if you try to instanciate the class regularily you will get a new instance, instead of the singleton object that the client is using. So basically you need to do something very similar to the thing I did before, only in the server:

//Call this from the Main method before the app is running.
private static void InitRemoting()
{
foreach (WellKnownServiceTypeEntry entry in RemotingConfiguration.GetRegisteredWellKnownServiceTypes())
{
MarshalByRefObject pxy = (MarshalByRefObject) Activator.GetObject(entry.ObjectType, "tcp://localhost:port/MyServer/" + entry.ObjectUri);
pxy.CreateObjRef(entry.ObjectType);
RemoteSingletonObjectsList.Instance. RegisterRemoteSingletonObject(pxy);
}
}

public class RemoteSingletonObjectsList
{
private static RemoteSingletonObjectsList _instance = new RemoteSingletonObjectsList();
public static RemoteSingletonObjectsList Instance
{
get
{
return _instance;
}
}
private RemoteSingletonObjectsList()
{
}

private Hashtable _objects = new Hashtable();
public void RegisterRemoteSingletonObject(object obj)
{
_objects[obj.GetType()] = obj;
}
public object GetRemoteSingletonObject(Type type)
{
return _objects[type];
}
}


When the server starts it initializes the remoting objects before any client initialize them using CreateObjRef function. Then you put all the objects in a Hashtable. When you need the object in the server you call RemoteSingletonObjectsList.GetRemoteSingletonObject(typeof(whatever)).
Hope it is clear

Richard210363
October 26th, 2005, 11:41 AM
Thanks jhammer,
Yes I did change the singlecall to singleton but not in the code I posted.

I ended up doing a similar thing to what you say.

The server uses the same technique as the client to instantiate the object using the channel set up by the server and so any subsequent client calls the same object.

Your method looks a bit more generic so I shall try it out for my other projects.

Cheers
Richard Byrne