1

I have a MarshalByReferenceObject, whose methods are called via .net remoting.

public class MyServer : MarshalByReferenceObject
{
    public void DoSomething();
}

System.Runtime.Remoting.RemotingConfiguration.RegisterWellKnownServiceType(
            typeof(MyServer), "serverUrl", System.Runtime.Remoting.WellKnownObjectMode.SingleCall);

The clients are calling the server like this:

var server = Activator.GetObject(typeof(MyServer), "serverUrl");
server.DoSomething();

Now for debugging purposes, when a client calls the DoSomething() method, I want to log who the actual caller is.

How can I identify the calling method from within my server.DoSomething() function? Or from within my server constructor, which is called by Activator.GetObject?

Ideally I would want the whole stacktrace: which client method called the server, which parent method called that client method, and so on. But I would settle for some sort of caller identification. I control the clients, so perhaps I could give them names somehow? The interface specification is already pretty much fixed, so adding an otherwise superfluous string callerId parameter to each and every method is something I would like to avoid.

The local stack trace of course only identifies the System.Runtime.Remoting methods, not the actual remote caller.


example stacktrace on the server side:

    at MyServer.MyServer(), line 123
    at System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor, Boolean& bNeedSecurityCheck)
    at System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache)
    at System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean skipVisibilityChecks, Boolean skipCheckThis, Boolean fillCache)
    at System.Activator.CreateInstance(Type type, Boolean nonPublic)
    [...] 
    at System.Threading._IOCompletionCallback.PerformIOCompletionCallback(UInt32 errorCode, UInt32 numBytes, NativeOverlapped* pOVERLAP)

This obviously does not help me identify the calling method from the client.

HugoRune
  • 13,157
  • 7
  • 69
  • 144
  • 2
    If you control both sides, why not add that information as a parameter? – Thomas Weller Mar 08 '15 at 22:02
  • 1
    I control both sides, but the method signatures that are called are predetermined by an interface specification, which I would rather not change. And it is a lot of methods, called from a lot of places. – HugoRune Mar 08 '15 at 22:06
  • does that preclude something like an overloaded ctor? – Ňɏssa Pøngjǣrdenlarp Mar 08 '15 at 22:08
  • @Plutonix No, the constructor would be fine to overload, since it is not actually included in the spec. But the [Activator.GetInstance](https://msdn.microsoft.com/en-us/library/9yazthfw%28v=vs.110%29.aspx) method does not allow me to pass any parameters to the constructor. The only thing i can pass is some sort of "channel-state" object, which is only used internally. It does not seem to be possible to create a server object with a custom constructor. – HugoRune Mar 08 '15 at 22:14
  • @HugoRune - I haven't done much with remoting but I did a quick bit of reasearch... Can you implement IServerChannelSinkProvider and somehow make use of the information in there to get what you need about the client... https://msdn.microsoft.com/en-us/library/System.Runtime.Remoting.Channels.IServerChannelSink(v=vs.110).aspx – Steve Sheldon Mar 09 '15 at 11:34

1 Answers1

2

If it's an option for you to adjust the way your client and server work slightly, it is possible to pass parameters to your protocol class (MyServer) constructor.

On the server-side you can use RemotingConfiguration.RegisterActivatedServiceType to register your protocol class.

On the client-side you can call RemotingConfiguration.RegisterActivatedClientType to register that you want instances of the protocol class to be created on the server. Then you can just create a new instance and pass whatever client-identifying parameters you like to the constructor (in the normal way, without Activator.GetObject), and the object will be automatically created on the server and proxied locally.

Protocol Class

using System;

public class MyServer : MarshalByRefObject
{
    Guid clientID;

    public MyServer(Guid clientID)
    {
        this.clientID = clientID;
    }

    public void DoSomething()
    {
        Console.WriteLine("Logged operation from client {0}.", this.clientID);
    }
}

Server

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

class Server
{
    static void Main(string[] args)
    {
        ChannelServices.RegisterChannel(new TcpServerChannel(1234), false);
        RemotingConfiguration.ApplicationName = "Test";
        RemotingConfiguration.RegisterActivatedServiceType(typeof(MyServer));
        Console.WriteLine("Server listening ...");
        Console.ReadLine();
    }
}

Client

using System;
using System.Runtime.Remoting;

class Client
{
    static void Main(string[] args)
    {
        RemotingConfiguration.RegisterActivatedClientType(typeof(MyServer),
                                      "tcp://localhost:1234/Test");
        var id = Guid.NewGuid();
        Console.WriteLine("Creating object with id {0}.", id);
        var obj = new MyServer(id);
        obj.DoSomething();
    }
}
softwariness
  • 4,022
  • 4
  • 33
  • 41