views:

1008

answers:

3

Hi,

Given this MarshalByRef class:

public class MyRemotedClass : MarshalByRef
{
  public void DoThis()
  {
     ...
  }
  public void DoThat()
  {
     ...
  }
}

Client side code:

MyRemotedClass m = GetSomehowMyRemotedClass();
m.DoThis();
m.DoThat();

I can have several clients doing the same thing at a the same time. I would like to distinct the clients. How can I identify inside the remotely accessed methods, by whom the remoting invocation is executed? For example, I could log who did what. (Actually, I do not need to trace back the true client info, I just want to be able to group invocations by clients.)

[Edited to add more background info]

I have enormous amount of code to cover, including properties. Therefore extending the input parameter list is not an option.

+1  A: 

pass client reference in each function? (or some identifier)

PoweRoy
+4  A: 

One of the things you can do is identify a client by IP address by implementing an IServerChannelSinkProvider.

Add this class to your remoting host project:

ClientIPServerSinkProvider.cs

using System;
using System.Collections;
using System.IO;
using System.Runtime.Remoting;
using System.Runtime.Remoting.Messaging;
using System.Runtime.Remoting.Channels;
using System.Threading;
using System.Net;

namespace MyRemotingEnvironment
{
    public class ClientIPServerSinkProvider : 
     IServerChannelSinkProvider
    {
     private IServerChannelSinkProvider _nextProvider = null;

     public ClientIPServerSinkProvider()
     {
     }

     public ClientIPServerSinkProvider(
      IDictionary properties, 
      ICollection providerData)
     {
     }

     public IServerChannelSinkProvider Next
     {
      get { return _nextProvider; }
      set { _nextProvider = value; }
     }

     public IServerChannelSink CreateSink(IChannelReceiver channel)
     {
      IServerChannelSink nextSink = null;

      if (_nextProvider != null)
      {
       nextSink = _nextProvider.CreateSink(channel);
      }
      return new ClientIPServerSink(nextSink);
     }

     public void GetChannelData(IChannelDataStore channelData)
     {
     }
    }



    public class ClientIPServerSink : 
     BaseChannelObjectWithProperties, 
     IServerChannelSink, 
     IChannelSinkBase
    {

     private IServerChannelSink _nextSink;

     public ClientIPServerSink(IServerChannelSink next)
     {
      _nextSink = next;
     }

     public IServerChannelSink NextChannelSink
     {
      get { return _nextSink; }
      set { _nextSink = value; }
     }

     public void AsyncProcessResponse(
      IServerResponseChannelSinkStack sinkStack, 
      Object state, 
      IMessage message, 
      ITransportHeaders headers, 
      Stream stream)
     {
      IPAddress ip = headers[CommonTransportKeys.IPAddress] as IPAddress;
      CallContext.SetData("ClientIPAddress", ip);
      sinkStack.AsyncProcessResponse(message, headers, stream);
     }

     public Stream GetResponseStream(
      IServerResponseChannelSinkStack sinkStack, 
      Object state, 
      IMessage message, 
      ITransportHeaders headers)
     {

      return null;

     }


     public ServerProcessing ProcessMessage(
      IServerChannelSinkStack sinkStack, 
      IMessage requestMsg, 
      ITransportHeaders requestHeaders, 
      Stream requestStream, 
      out IMessage responseMsg, 
      out ITransportHeaders responseHeaders, 
      out Stream responseStream)
     {
      if (_nextSink != null)
      {
       IPAddress ip = 
        requestHeaders[CommonTransportKeys.IPAddress] as IPAddress;
       CallContext.SetData("ClientIPAddress", ip);
       ServerProcessing spres = _nextSink.ProcessMessage(
        sinkStack, 
        requestMsg, 
        requestHeaders, 
        requestStream, 
        out responseMsg, 
        out responseHeaders, 
        out responseStream);
       return spres;
      }
      else
      {
       responseMsg = null;
       responseHeaders = null;
       responseStream = null;
       return new ServerProcessing();
      }
     }


    }
}

Then when you start your remoting host do something like the following:

BinaryServerFormatterSinkProvider bp = new BinaryServerFormatterSinkProvider();
ClientIPServerSinkProvider csp = new ClientIPServerSinkProvider();
csp.Next = bp;
Hashtable ht = new Hashtable();
ht.Add("port", "1234"); // Your remoting port number
TcpChannel channel = new TcpChannel(ht, null, csp);
ChannelServices.RegisterChannel(channel, false);

RemotingConfiguration.RegisterWellKnownServiceType(
    typeof(MyRemotedClass), 
    "MyRemotedClass.rem", 
    WellKnownObjectMode.SingleCall);

In your method calls you can access the IP address of the client by doing:

public class MyRemotedClass : MarshalByref
{
    public void DoThis()
    {
     string clientIP = CallContext.GetData("ClientIPAddress").ToString();
    }
}
Kev
Is it really this complicated just to get a client IP address?
dboarman
@dboarman - it's the only way. But if you've gotten used to writing your own channel sink classes then it becomes a normal part of remoting life.
Kev
Yeah, unfortunately for me this is really my first foray into Remoting...which I'm setting aside for now. I'm not keen on using Remoting when I could implement Marc Gravell's protobuf-net :) I'm going in a slightly different direction for now...thanks for you help, tho.
dboarman
@dboarman - isn't protobuf-net just a serialiser? i.e. you build into your chosen service infrastructure such as remoting or wcf? I don't think that's going to solve problems such as getting an IP address of a client.
Kev
@Kev: The way I'm understanding it (from Marc Gravell), I can use protobuf-net over a standard Tcp Socket. A Tcp socket server is easily made aware of client ID by IP address.
dboarman
@dboarman - Yeah that would work, but then you're in the zone of building your own server infrastructure and building all the good things that Remoting and WCF give you for free (NIH). I couldn't trust myself to do that.
Kev
@dboarman - just to clarify, I'm not suggesting that you shouldn't use the protobuf-net bits, but I'd be using them with WCF/Remoting and not building my own server infrastructure.
Kev
@Kev - :) understood, but there are two, no 3 considerations: (a) WCF is way more than we need, (b) Remoting is legacy therefore it's days are numbered whether its 5 months or 5 years, and (c) we already have a base socket server infrastructure.
dboarman
A: 

@Kev: Could you please provide a working sample project? That would be helpful.

Priyanka