Click here to Skip to main content
Email Password   helpLost your password?

Contents

Introduction

The modern application architecture requires a transparent mapping of the Distributed Object Oriented Design model to the physical deployed model on the Intranet and Internet Networks. The standard .NET Remoting allows to consume a remoting object using only one standard channel such as the tcp or http in the physical peer-to-peer design pattern. The .NET Remoting infrastructure doesn't have a direct mechanism to chain and re-route channels between the consumer and remote object.

This article show you how to design and implement the message sink (Router) to forward the Remoting Message (IMessage) to the properly channel. The connectivity to the well-known remote object described by its url (uniform resource locator) address can be mapped to the logical url address and administrated in the config file or programmatically changed during the runtime. Using the logical url addresses allows to virtualize a distributed business model based on the remoting interface contracts.

Before than we will go to its implementation details, let's start it with its concept and usage. I am assuming that you have a knowledge of the .NET Remoting.

Concept

To create a proxy for the well-known remote object driven by interface contract requires to know the following:

The Interface contract is an abstract definition between the consumer and remote object located in the shareable assembly installed in the GAC, which allows to build a distributed model based on the loosely coupled design pattern.

The second requirement, the url address represents the physical description of the peer-to-peer connectivity. This string can be hard coded or programmatically retrieved from the custom config file using the extra coding for that.

Basically, the url address consists of the information related to the remoting channel and remoting object. It's information about the message dispatching. For instance:

tcp://localhost:9090/endpointB

where:

The url address is stored in the IMessage (property Uri) based on the client request. It's a read-writeable property allows to be modified and more abstracted. In the properly channel we need a custom message sink to check its format and mapping to the physical url address required by the remoting infrastructure.

The following sinks describes these features:

Client Router

The concept of the Router Client Message Sink is based on the mapping the physical url address to the unique logical name, for instance, see the following mapping:

tcp://localhost:9090/endpointB     =>   tcp://testobject

where: testobject = localhost:9090/endpointB  represents the logical url name on the client side

It's a responsibility of the router client sink to make this mapping based on the in memory knowledge base of the url addresses (urlKB). The urlKB located in the client sink provider and it can be configured during the registration service from the config file. Of course, its contents also can be updated during the runtime based on the application needs, which it will allow to reconfigure a distributed model on the fly.

The following config snippet shows a configuration of the client provider in the tcp channel included its urlKB (custom property lurl):

<clientProviders>
 <!-- Message Router -->
 <provider ref="router" name="TcpRouterC9092"
   lurl ="endpoint=localhost:9090/endpoint,
          endpointB=localhost:9090/endpointB,
          test=localhost:9092/; tcp://localhost:9090/endpoint,
          router9092=localhost:9092/"/> 
 <formatter ref="binary" />
</clientProviders>

Server Router

The position of the Router Server Message Sink in the logical channel is different from its client side. The server channel is processing the client's outgoing IMessage, therefore the router message sink can control the message workflow based on the url address.

Concept of the chaining remoting channels is shown in the following picture:

The router is driven by delimiter character ';' in the logical url address string. In this case, the IMessage is dispatching to the next channel instead of forwarding to the next sink. Like the above client provider, the server provider also contains the urlKB to obtain the physical url address.

The following config snippet shows that:

<serverProviders>
 <formatter ref="binary" />
 <provider ref="router" name="TcpRouterS9092" 
   lurl ="router9090=tcp://localhost:9090/, 
          router9092=tcp://localhost:9092/, 
          endpoint=tcp://localhost:9090/endpoint,
          endpointB=tcp://localhost:9090/endpointB,
          test=tcp://localhost:9092/; tcp://localhost:9090/endpoint"/> 
</serverProviders>

Based on the above concept, the connectivity to the remote object is transparent regardless of  how many channels have been chained. This connectivity can be described by the unique logical name and the routers of the each channel will take care it.

Usage

Using the Router Sink requires that you install the MessageRouter.dll and RouterLogicalCallContext.dll assemblies into the GAC and the following modification of the machine.config file in the remoting section:

<channelSinkProviders>
 <clientProviders>
  <formatter id="soap" 
type="System.Runtime.Remoting.Channels.SoapClientFormatterSinkProvider, 
      System.Runtime.Remoting,Version=1.0.3300.0, 
      Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
  <formatter id="binary" 
type="System.Runtime.Remoting.Channels.BinaryClientFormatterSinkProvider,
      System.Runtime.Remoting,Version=1.0.3300.0, Culture=neutral, 
      PublicKeyToken=b77a5c561934e089"/>
  <provider id="router" 
      type="RKiss.MessageRouter.RouterClientSinkProvider, 
      MessageRouter,Version=1.0.936.36529, 
      Culture=neutral, PublicKeyToken=47a36cf75249d9dc"/>
 </clientProviders>
 <serverProviders>
 <formatter id="soap" 
type="System.Runtime.Remoting.Channels.SoapServerFormatterSinkProvider,
      System.Runtime.Remoting,Version=1.0.3300.0, 
      Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
 <formatter id="binary" 
type="System.Runtime.Remoting.Channels.BinaryServerFormatterSinkProvider,
      System.Runtime.Remoting,Version=1.0.3300.0, 
      Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
 <provider id="wsdl" 
      type="System.Runtime.Remoting.MetadataServices.SdlChannelSinkProvider,
      System.Runtime.Remoting,Version=1.0.3300.0, 
      Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
 <provider id="router" type="RKiss.MessageRouter.RouterServerSinkProvider, 
      MessageRouter,Version=1.0.936.36529, Culture=neutral, 
      PublicKeyToken=47a36cf75249d9dc"/>
 </serverProviders>
</channelSinkProviders>

The MessageRouter contains two sink providers, one for the client and the other one for server. Both of them have the same id (router), which can be used to reference them in the config files.

The following features are built -in:

Note that the routers in the client/server channels are not tightly coupled, so they can be used separately based on the application needs.

After installation of the MessageRouter (GAC + machine.config), the router is ready to use in the .Net Remoting infrastructure like other standard providers such as soap and binary.

The following picture illustrates how to use a client router:

The remote object (published as endpointB) is fully transparent to the consumer side in the loosely coupled design pattern. The client is creating its proxy based on the unique logical url address (for instance: endpointB) in the tcp channel. The client router is mapping this address to the physical address (tcp://localhost:9090/endpointB) and forwarding to the next sink. Note that the logical url name doesn't need to be matched with the endpoint address, but it's better and readable to use it the same.

As you can see, using the client router is straightforward and configurable, special when the consumers are based on the Remoting Interface contract.

How about the situation when a deploying model needs to use more than one channels, for instances; asynchronous remoting call over internet using the Web Service and MSMQ, event driven architecture using the custom MSMQ channel, etc.?  Well, in situations like those, the chaining channels is a good solution to hide all connectivity issues from the business logic.

The following picture shows the configuration issues for the chaining channels using the Server Router:

The Server Router of the Channel X-1 found the router delimiter (';') in the Uri string, which indicates to forward the IMessage to the next channel (Channel X). The next channel in the Uri string is described by its unique logical url address, so the router will replace it by the physical one from its knowledge base (urlKB). After that, the IMessage can be re-routed to the properly channel.

Note that the chained channels can be used in any combination of the standard and custom channels. For illustration and evaluation purposes I used chaining of the standard tcp channels.

Updating Router

Earlier, I mentioned that the knowledge base of the router sink (urlKB) can be updated during the runtime. The concept is based on using the CallContext object which it travels with the Remoting message between the client and remote object. There is a very simple abstract definition (contract) located in the RouterLogicalCallContext.dll assembly:

namespace RKiss.MessageRouter
{
   [Serializable]
   public class RouterLogicalCallContext : ILogicalThreadAffinative
   {
      string strUrlKB; 
      public string UrlKB 
      { 
          get {return strUrlKB; } 
          set { strUrlKB = value; }
      }
   }
}

The following client's code snippet shows how to update the urlKB, for instance, in the TcpRouterS9092:

//update a local knowledge base of the url addresses

routerName = "TcpRouterS9092";
RouterLogicalCallContext urlkb = new RouterLogicalCallContext();
urlkb.UrlKB = "endpointB=tcp://localhost:1234/myObjectUri, endpoint=";
CallContext.SetData(routerName, urlkb);

The above code will perform updating of the endpointB entry and deleting of the endpoint entry from the urlKB of the TcpRouterS9092.

Configuring the Router Provider

The Router Provider uses the following standard and custom properties:

Design

The Router design is based on re-directing the IMessage to the first message sink of the next chained channel. The .NET Remoting infrastructure allows to chain the message sinks in the channel. Plug-in a custom message sink (Router) in the properly channel position (stack of the message sinks) and monitoring the url address is a design pattern of the chaining channels.

The following picture show that:

The incoming IMessage in the server channel is passed trough the first sink - formatter. After that, the next sink is the Router and the IMessage is passed to its method ProcessMessage.  There is all router logic in this method:

In the case of the router delimiter, the following router logic is going to be performed:

As the above picture shows, the chaining channels is straightforward way with a minimum performance overhead. The IMessage image has been already created by the consumer of the remote object, we just re-directed it to the other sink in the chained channel.

The behaviour of the message flow is like on the client channel, therefore the chained channel has to be registered in the same host process with the router channel. The host process, in this situation, represents a bridge process between the chained remoting channels. The chained channel doesn't need to be the same type, it can be any standard or custom channel, the .NET Remoting infrastructure will guarantee that the IMessage will flow properly through all of them.

Implementation

The implementation of the Router uses a standard message sink boilerplate (infrastructure). I will skip it this description and I am going to focus only on the parts which related to the router.

Server router

In the server router, there are two places where a router logic has to be inserted. The first place is the sink provider's constructor to initialize its knowledge base (located in the hashtable object) by  values from the config file. The following code snippet shows that:

public RouterServerSinkProvider(IDictionary properties, 
                              ICollection providerData)
{
   string strLURL = "";

   if(properties.Contains("name")) 
      m_strProviderName = Convert.ToString(properties["name"]);
   if(properties.Contains("lurl")) 
      strLURL = Convert.ToString(properties["lurl"]);

   if(strLURL != "") 
   {
      try 
      {
         string[] arrayUrl = strLURL.Split(new char[]{'=',','});
         for(int ii=0; ii<arrayUrl.Length; ii++) 
         {
            m_HT.Add(arrayUrl[ii].Trim(), arrayUrl[++ii].Trim());
         }
      }
      catch(Exception ex) 
      {
         string strWarning = 
            string.Format(
              "{0}.RouterServerSinkProvider has problem ({1}) in the {2}.", 
              m_strProviderName, ex.Message, strLURL);
         WriteEventLog(strWarning, EventLogEntryType.Warning);
      }
   }

   WriteEventLog(string.Format(
       "{0}.RouterServerSinkProvider has been initiated.", 
       m_strProviderName));
}

The second place is a sink's ProcessMessage method. There is a logic to update an internal knowledge base (hashtable) from the CallContext object targeted for the specified router. The rest of work is done in the private methods such as MessageDispatcher and MessageRouter.

public ServerProcessing ProcessMessage(
      IServerChannelSinkStack sinkStack, 
      IMessage requestMsg, ITransportHeaders requestHeaders, 
      Stream requestStream, out IMessage responseMsg,
      out ITransportHeaders responseHeaders, 
      out Stream responseStream)
{
   ServerProcessing servproc = ServerProcessing.Complete;
   responseHeaders = null;
   responseStream = null;

   //Are we in the business?

   if(m_Next != null) 
   {

      //check the Router Call Context 

      object objCC = requestMsg.Properties["__CallContext"];
      if(objCC != null && objCC is LogicalCallContext) 
      {
         LogicalCallContext lcc = objCC as LogicalCallContext;
         object objData = lcc.GetData(m_Provider.ProviderName);
         if(objData != null && objData is RouterLogicalCallContext) 
         {
            RouterLogicalCallContext rlcc = 
                  objData as RouterLogicalCallContext;
            if(rlcc.UrlKB == "") 
            {
               //clear the local KB

               m_Provider.ClearLogicalURL();
            }
            else
            if(rlcc.UrlKB == "?") 
            {
               //retrieve the local KB contens

               rlcc.UrlKB = m_Provider.GetLogicalURL();
               lcc.SetData(m_Provider.ProviderName, rlcc);
            }
            else 
            {
               //update local knowledge base

               string[] arrayUrl = 
                  rlcc.UrlKB.Split(new char[]{'=',','});
               for(int ii=0; ii<arrayUrl.Length; ii++) 
               {
                  m_Provider.SetLogicalURL(
                        arrayUrl[ii].Trim(), arrayUrl[++ii].Trim());
               }
            }
          }
      }

      //Dispatch message 

      responseMsg = MessageDispatcher(requestMsg);

      if(responseMsg == null) 
      {
         //processing message in the current channel 

         servproc = m_Next.ProcessMessage(sinkStack, requestMsg, 
            requestHeaders, requestStream, 
            out responseMsg, out responseHeaders, out responseStream);
      }
      else
      if(RemotingServices.IsOneWay((
            requestMsg as IMethodCallMessage).MethodBase) == true) 
      {
         servproc = ServerProcessing.OneWay;
      }
   } 
   else 
   {
      //---We have no active sink

      Trace.WriteLine(string.Format(
          "{0}:RouterServerSink ProcessMessage null", 
          m_Provider.ProviderName));

      responseMsg = null;
      responseHeaders = null;
      responseStream = null;
   }

   return servproc;
}

The MessageDispatcher method has a responsibility to validate a primary url address. In the case of the logical address, it will perform its mapping to the physical form using the provider's knowledge base (urlKB).

private IMessage MessageDispatcher(IMessage requestMsg)
{
   IMessage responseMsg = null;

   if(requestMsg.Properties["__Uri"] != null) 
   { 
      string strUrl = requestMsg.Properties["__Uri"].ToString();

      //try to split the url adresses and primary address

      string[] strArrayUrlPath = strUrl.Split(';'); 
      string[] strArrayPrimaryAddr = strArrayUrlPath[0].Split('/'); 

      //checking the endpoint?

      if(strArrayUrlPath.Length == 1 && 
          strArrayPrimaryAddr.Length == 2) 
      {
         //Yes, it is. The message is going to forward 

         //it to the StackBuilder.

         //do nothing, here (responseMsg is null). 

      }
      else 
      {
         //get the new primary address

         string strObjUrl = strUrl.Remove(0, 
            strArrayUrlPath[0].Length + 1).TrimStart(' ');
         string[] strArrayNewUrlPath = 
            strObjUrl.Split(new char[]{';'}, 2); 
         string lurl = strArrayNewUrlPath[0].Trim();

         //is this a logical Url

         if(lurl.IndexOf("://") < 0) 
         {
            //yes, it is. Replace this logical 

            //uri by its physical mapping

            string purl = m_Provider.GetLogicalURL(lurl);
            if(purl != "" && strArrayNewUrlPath.Length == 1) 
            {
               strObjUrl = purl; 
            }
            else
            if(purl != "" && strArrayNewUrlPath.Length == 2) 
            {
               strObjUrl = purl + ";" + strArrayNewUrlPath[1]; 
            }
         }

         //call router (forwarding the IMessage to the properly channel)

         responseMsg = MessageRouter(requestMsg, strObjUrl);
      }
   }
   else
   {
      //The url address can not by empty

      Exception exp = new Exception(
            string.Format(
            "{0}:RouterServerSink: The Uri address is null", 
            m_Provider.ProviderName));
      responseMsg = new ReturnMessage(exp, 
            (IMethodCallMessage)requestMsg);
   }

   return responseMsg;
}

Finally, the re-routing process is implemented in the following method. The logic is very simple. First of all, the valid channel is searching and creating its first message sink based on the chained physical url address. When we have a correct message sink, the IMessage can be passed into the sink invoking its method SyncProcessMessage resp. AsyncProcessMessage. The return value (respondMsg) is sent back to the original caller.

private IMessage MessageRouter(IMessage requestMsg, string strObjUrl) 
{
   IMessage responseMsg = null;

   //Redirect the IMessage to the properly outgoing channel 

   requestMsg.Properties["__Uri"] = strObjUrl;
   string strDummy = null;
   IMessageSink iMsgSink = null;

   //find the properly outgoing channel registered in this process

   foreach(IChannel channel in ChannelServices.RegisteredChannels)
   {
      if(channel is IChannelSender)
      {
         iMsgSink = (channel as IChannelSender).CreateMessageSink(
                        strObjUrl, null, out strDummy);
         if(iMsgSink != null) 
            break; 
      }
   }

   //check our result

   if(iMsgSink == null)
   {
      //Sorry we have no properly channel to the target object

      string strErr = string.Format(
        "{0}:RouterServerSink: A supported " +
        "channel could not be found for {1}", 
         m_Provider.ProviderName, strObjUrl);
      responseMsg = new ReturnMessage(new Exception(strErr), 
         (IMethodCallMessage)requestMsg);
   }
   else 
   {
      //Pass the IMessage to the following channel 

      //based on the method's attribute

      //The SyncProcessMessage can not be done on the 

      //OneWay attributed method (deadlock process)

      if(RemotingServices.IsOneWay((requestMsg 
            as IMethodCallMessage).MethodBase) == true) 
      {
         responseMsg = (IMessage)iMsgSink.AsyncProcessMessage(
            requestMsg, null);
      }
      else 
      {
         responseMsg = iMsgSink.SyncProcessMessage(requestMsg);
      }
   }

   return responseMsg;
}

Client Router

The client router implementation is much simpler than server one. Updating the router knowledge base and mapping the logical url address to the physical one is done the same way like in the server router. The different is only in the place where it's processing. For the client router, right place is the CreateSink method. Note that the final url address is passed to the message sink when proxy to the remote object has been created:

public IClientChannelSink CreateSink(IChannelSender channel, 
                  string url, object remoteChannelData)
{
   IClientChannelSink Sink = null;
   m_strChannelName = channel.ChannelName;
   StringBuilder sbUrl = new StringBuilder(m_strChannelName);
   sbUrl.Append("://");

   try 
   {
      //check the Router Call Context 

      object obj = CallContext.GetData(ProviderName);
      if(obj != null && obj is RouterLogicalCallContext) 
      {
         RouterLogicalCallContext rlcc =
            obj as RouterLogicalCallContext;
         if(rlcc.UrlKB == "") 
         {
            //clear the local KB

            ClearLogicalURL();
         }
         else
         if(rlcc.UrlKB == "?") 
         {
            //retrieve the local KB contens

            rlcc.UrlKB = GetLogicalURL();
            CallContext.SetData(ProviderName, rlcc);
         }
         else 
         {
            //update local knowledge base

            string[] arrayNewUrl = 
                  rlcc.UrlKB.Split(new char[]{'=',','});
            for(int ii=0; ii<arrayNewUrl.Length; ii++) 
            {
               SetLogicalURL(arrayNewUrl[ii].Trim(), 
                    arrayNewUrl[++ii].Trim());
            }
         }
      }

      //replace logical uri address by physical 

      //one from the local KB

      string[] arrayUrl = url.Split(new char[]{';'}, 2);
      string lurl = arrayUrl[0].Remove(0, sbUrl.Length).Trim();

      if(lurl.IndexOf('/') < 0) 
      {
         string purl = GetLogicalURL(lurl);

         if(purl != "") 
         {
            sbUrl.Append(purl);

            if(arrayUrl.Length == 2) 
            {
               sbUrl.Append(";");
               sbUrl.Append(arrayUrl[1]); 
            }

            url = sbUrl.ToString();
         }
      }

      //create a router sink object

      object ms = m_Next.CreateSink(channel, url, 
                  remoteChannelData);
      Sink = new RouterClientSink(this, url, ms);
   }
   catch(Exception ex) 
   {
      WriteEventLog(string.Format(
            "{0}/{1}.CreateSink catch {2}", m_strChannelName, 
            m_strProviderName, ex.Message), EventLogEntryType.Error); 
   }

   return Sink;
}

The following code snippet shows implementation of the message processing methods in the client message sink. The IMessage.Uri property is overwritten by value m_Uri, which represents the physical url address. The client sink just passing the IMessage to the next sink.

public IMessageCtrl AsyncProcessMessage(IMessage msgReq, 
            IMessageSink replySink)
{
   msgReq.Properties["__Uri"] = m_Url;
   IMessageCtrl iMsgCtrl = 
            m_NextMsgSink.AsyncProcessMessage(msgReq, 
            replySink);

   return iMsgCtrl;
}
public IMessage SyncProcessMessage(IMessage msgReq)
{
   msgReq.Properties["__Uri"] = m_Url;
   IMessage msgRsp = m_NextMsgSink.SyncProcessMessage(msgReq);

   return msgRsp;
}

Test

I created a test solution to test the client and server routers. There is a set of the following projects:

Note that the last 4 projects have been designed and implemented only for test purposes using the "Hello world" design pattern.

  1. Install the following assemblies into the GAC: MessageRouter, RouterLogicalCallContext, TestInterface and TestObject
  2. Modify your machine.config file as I mentioned early.
  3. Launch the ConsoleServer.exe program
  4. Launch the WindowsClient.exe program
  5. Select the url address on the top combo box (see, the following picture)
  6. Click on the SayHello
  7. Check the response on the ConsoleServer program box

The url combo box drop menu has many different url addresses, try them to verify the configuration of the client/server routers. You would to update the client/server routers knowledge base using the msg combo box to see the mapping and routing messages on the fly.

Conclusion

This article described a simple solution to chain channels in .NET Remoting. Using the logical url address to described the remoting connectivity in the deployed application allows to significant improve your implementation on the client side and administrated in the config file or programmatically on the fly. The great benefit is using the client router in the application model based on the remoting interface contracts design pattern. There is no need to implement extra code to retrieve a specified url address requested by GetObject method, the client router will take care of this mapping based on your configuration like in the case of using the new operator. Chaining channel (standard and custom) will make transparent connectivity to the remoting objects.

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
GeneralOnly for SAO ?
PEvans
19:55 28 Oct '03  
Great article Roman. I've been looking for a way to redirect remoting calls from one server to another, just like your router message sink does. However, I need to do this for client activated objects. Am I correct in saying that your implementation only works for server activated objects?

CAOs are harder because the first request to the server does not identify the requested object in the URL (instead it is identified in the body of the message). Also, once the server-side object has been created a dynamic URL is generated for this instance. This makes re-routing based on the URL difficult.

I would be very interested in any ideas you have about how this might be achieved for CAOs.

Thanks again for an excellent article.

Peter Evans
Generalgreat article
Brian Flood
17:27 4 Sep '03  
excellent content Roman!

Brian Flood

GeneralMissing config files and SP2 issue.
Roman Kiss
19:19 16 Mar '03  
I appology for missing the following config files:

------- WindowsClient.exe.config -------------------------------------

<configuration>
<system.runtime.remoting>
   <application>
   <channels>
      <channel ref="tcp" port="9092" >
      <clientProviders>
         <provider ref="router" name="TcpRouterC9092"
               lurl ="endpoint=localhost:9090/endpoint,
                           endpointB=localhost:9090/endpointB,
                    test=localhost:9092/; tcp://localhost:9090/endpoint,
                    router9092=localhost:9092/"/>           
         <formatter ref="binary" />
      </clientProviders>
      <serverProviders>
         <formatter ref="binary" />
         <provider ref="router" name="TcpRouterS9092"
               lurl ="router9090=tcp://localhost:9090/,
                        router9092=tcp://localhost:9092/,
                 endpoint=tcp://localhost:9090/endpoint,
                 endpointB=tcp://localhost:9090/endpointB,
                 test=tcp://localhost:9092/; tcp://localhost:9090/endpoint"/>  
         </serverProviders>
      </channel>
   </channels>
   </application>
</system.runtime.remoting>
</configuration>

------ ConsoleServer.exe.config ---------------------

<configuration>
<system.runtime.remoting>
   <application>
   <service>
   <wellknown mode="SingleCall" type="TestObject.ObjectA, TestObject,           
                  Version=1.0.941.28888, Culture=neutral, PublicKeyToken=144391ea9ae606ae"
                  objectUri="endpoint" />
   <wellknown mode="SingleCall" type="TestObject.ObjectB, TestObject,
                  Version=1.0.941.28888, Culture=neutral, PublicKeyToken=144391ea9ae606ae"
                  objectUri="endpointB" />
   </service>
   <channels>
   <channel ref="tcp" port="9090" >
   <serverProviders>
      <formatter ref="binary" />
      <provider ref="router" name="TcpRouterS9090"
            lurl ="router9090=tcp://localhost:9090/,
           router9092=tcp://localhost:9092/,
                  endpoint=tcp://localhost:9090/endpoint,
                  endpointB=tcp://localhost:9090/endpointB,
           test=tcp://localhost:9092/; tcp://localhost:9090/endpoint"/>  
      </serverProviders>
      <clientProviders>
      <provider ref="router" name="TcpRouterC9090"
            lurl ="endpoint=localhost:9090/endpoint,
              test=localhost:9090/; router9092; tcp://localhost:9090/endpoint"/>  
      <formatter ref="binary" />
      </clientProviders>
      </channel>
   </channels>
   </application>
</system.runtime.remoting>
</configuration>

---------------------------------------------------------------------------

The other notice:
- The article has been written before service pack 2 and it will need to be updated (only server sink - router) for the issue related with the binary formatter on the server side (error exception: Receiver is not registered). I have this update in my plan.

Thanks.

Roman
GeneralRe: Missing config files and SP2 issue.
solidstore
1:28 17 Dec '03  
I'm trying to get your sample to work with .NET 1.1 - I think I'm having the same problem you mentioned with the binary formatter. I've added the typeFilterLevel="Full" attribute to the server binary formatter. But still when using your test application any of the logical urls that require routing (contain Wink I just get Exception: Requested Service not found. Is this the same problem? Have you ever had this running under 1.1.

I'm hoping to use your ideas in my next project where I need to call objects on machine C from machine A where that arent directly accessible but can both see machine B, e.g. A --> B --> C
Do you think your ideas are valid in this application?
GeneralRe: Missing config files and SP2 issue.
Benoit Morneau
14:25 20 Oct '05  
I'm also trying to do something very similar; A --> B --> C and also C --> B --> A. B will be a machine a seperate DMZ.

Let me know what are your result and status on this.

I also do beleive that this article could also be useful for acheiving this.

Maybe we can join our force!

Benoit.
GeneralRe: Missing config files and SP2 issue.
Tobi Feller
7:08 29 Oct '05  
I'm also trying to communicate with .NET Remoting in such a chain.

A (Agent/Client) <-> ...internet... B (Webservice) ...internet... <-> C (RemoteObject) <-> ...lan... D (RemoteObject)

Does someone know a solution for the problem? Or just a few tips where to search.
GeneralHelp? Can't get this to work?
Nood!e
23:45 13 Mar '03  
I've followed exactly the steps included (registered the dlls, started the server, started the client, selected the url 'tcp://router9092; endpoint', pressed SayHello), and am using the source code downloadable from this article, but seem unable to get this to work. I am running both the server and the client on the same machine, although I have also tried them on seperate machines, and so have changed the port of the client to 9090 instead of the config file's 9092. Any suggestions?
GeneralRe: Help? Can't get this to work?
Nood!e
23:55 13 Mar '03  
ok - I've found the problem. In the ZIP file included there appear to be no config files for the server or client app. Can you post these also Roman?
Thanks
GeneralChannel Flexibility
Aaron Clauson
13:09 3 Mar '03  
Interesting and original article.

It seems as if Microsoft have sacrificed some flexibility in the way remoting has been implemented! It would be nice to be able to override the way remoting assigns a channel to a URL. One of the problems I have encountered is when I want to use some custom sinks with a http channel. I can create the channel and add my sinks but if another http channel is created without the sinks remoting uses the most recently created channel and the custom sinks get skipped.

The solution is to implement a custom channel URI but this seems to be a big chunk of work for a somewhat simple functionality gain...

Then there are custom remoting formatters!! Spent a long time and many calls to Microsoft before that one was sorted.

Still in my opinion remoting is ten thousand times better than DCOM so I shouldn't complain Smile .
GeneralCall me stupid...
Sean Winstead
14:39 24 Jan '03  
Due to a project I'm researching, this article really interests me. What I don't understand are the exact benefits the client-side and server-side routers give me. In what situation could I use them?

Sean Winstead
GeneralRe: Call me stupid...
Roman Kiss
15:06 24 Jan '03  
Client side:
Mapping the logical name of the connectivity to the physical url address of the remote object (for interface contract driven objects).

Server side:
re-routing the IMessage to the next physical channel. For instance: between the remote object and its consumer are different physical channels (WebService, MSMQ, tcp, http, pipe, etc.)

Roman
GeneralRe: Call me stupid...
Sean Winstead
16:06 24 Jan '03  
Client side:
Mapping the logical name of the connectivity to the physical url address of the remote object (for interface contract driven objects).

The article pointed this out. But why would I want a logical name when I can just change the physical name in the config file?

Server side:
re-routing the IMessage to the next physical channel. For instance: between the remote object and its consumer are different physical channels (WebService, MSMQ, tcp, http, pipe, etc.)

When would there be different, registered physical channels between the client & server?

Sean Winstead
GeneralRe: Call me stupid...
Roman Kiss
16:30 24 Jan '03  

>The article pointed this out. But why would I want a logical name when I
>can just change the physical name in the config file?

Yes, I mentioned that. You can NOT use the config file for the Activator.GetObject(type, url) to get the proxy, when the remote object is consummed by the interface contract (loosely coupled design pattern).


>When would there be different, registered physical channels between the >client & server?

For instance: MSMQCustomChannel -> WebServiceCustomChannel -> tcp/endpoint



Last Updated 6 Aug 2002 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010