Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

.NET Remoting in Simple English (Really, it's that simple)

0.00/5 (No votes)
26 Jun 2003 2  
This article is meant to be a step by step guide to give its reader a gist of .NET Remoting. This tutorial guides the reader to set up a fully functional chat program.

Sample Image - RemotingSimpleEng.gif

Introduction

This article is meant to be a step by step guide to give its reader a gist of .NET Remoting. I've tried to search good tutorials, but it was rather hard to find one that could work on my PC! So here's my first shot at a .NET Remoting tutorial in VB.NET. It shouldn't be hard to get it running, if you'd follow these few steps. After going through this article, be sure to read the continuation of this article here.

Q. What is an application boundary or domain?

A. Application domain is a logical boundary, and may be process or machine boundaries. Usually, no direct access exists to objects across this imaginary boundary. This is where Remoting services come into the picture, since it is able to access an object across a domain or boundary.

Q. By Value? By Object? Huh?

A. Now that you understand what an application domain is, we ask ourselves, how do we want to request the instance of a particular class to be accessible outside the original Application Domain. We have two choices, we either request a copy of the object or request a reference (or in techie words, proxy) to the original object.

Q. Let's use by Value... but how?

A. If you want your object, during runtime, to request a copy of the remote object (marshal-by-value), we have to do either one of these on the remote object:

  • Add the SerializableAttribute to the class

    Example:

    <Serializable()> Public Class XYZ
  • Implement the ISerializable interface (for more details, consult: .NET Framework Class Library, ISerializable Interface)

    Example:

    Public Class XYZ
        Implements ISerializable

Q. What about by Ref...?

A. If you want your object, during runtime, to request a reference to the remote object (marshal-by-reference [MBR]), we have to ensure that the remotable object's class derives, directly or indirectly, from MarshalByRefObject. This allows the runtime to create a proxy to the object.

Q. How do I begin?

A. Generally, two things need to happen. A server must publish a remotable object (or service). The service may be on a TCP channel or HTTP channel, or even both. We give a name to the service, like "RemotingChat". The client, on the other hand, connects to a specific server or host name, specifies the service name using a supported channel, and either says, "I want an (value) or this (reference) instance of the object called RemotingChat". Remember all this can be done either programmatically or by using an app.config file.

Q. Did I just forget something...? Oh yes, you ALSO need to decide whether you want the object to be client or server activated. What are they about and how do you do it?

A. Server-activated objects are published at a URL, like so:

ProtocolScheme://ComputerName:Port/AppName/ObjectUri

The URL is so called as "well-known" to the client, even John Jones can use it! So it's called a well-known type. Even the URL is termed as the the well-known object URL.

On the other hand, the client activation creates one object for each, and its object's lifetime extends until either of this happens:

  • Client drops dead... and loses reference to object
  • The object's lease expires (can't live any longer...)

Your client URL would look like:

ProtocolScheme://ComputerName:Port/AppName

Q. So you've chosen to go for Server Activated? There's a choice between SingleCall and Single-what? Singleton,...

A. Another choice?? Well you would SingleCall remote objects when you want each object to service one and only one request. This is useful when you want it to be created on-demand as method calls arrive. Oh yes, don't forget, that after the method call, it dies... Therefore, its lifetime is limited to the method call. You might want to use it in stateless applications (in simplicity: applications that forget you ever called them...). Some say, it's the choice for load-balanced applications.

Another pound of the tonne? Ahh, yes it's singleton. This is useful, if you need all the clients to access one and only one remote object. Here, the server's remoting layer creates one Singleton object. This lone-ranger object handles method calls from all clients. Sad huh? But it lives up to a 100 years, -it lives as long as the server's lifetime. Pretty much the opposite of the SingleCall fellow, so it's useful in stateful applications. A good choice is when the overhead of creating and maintaining objects is substantial.

To activate the remote objects programmatically on the host, you might code it as:

new WellKnownServiceTypeEntry(typeof(InBetween.Manager), _ 
       "Manager", WellKnownObjectMode.Singleton);

or

new WellKnownServiceTypeEntry(typeof(InBetween.Manager), _ 
  "Manager", WellKnownObjectMode.Singlecall); 

Q. You mentioned that there are 2 ways to configure my remotable object's creation: Programmatically and by using a config file. What do you mean?

A. Bearing in mind, that you want to do a server activation; it means you either do this:

<configuration>

  <system.runtime.remoting>

      <application name="WiseOwlMathService">

          <service>

              <wellknown mode="SingleCall" type="InBetween.Manager,Manager"

                  objectUri = "MYOBJECT1" />

             <wellknown mode="Singleton" type="InBetween.Manager,Manager"

                  objectUri = "MYOBJECT2" />

         </service>

      <channels>

                  <channel port="9999" ref="http" />

                  <channel port="8888" ref="tcp" />

      </channels>

      </application>

  </system.runtime.remoting>

  </configuration>

or this:

using System;

  using System.Reflection;

  using System.Runtime.Remoting;
 class RemotingHost {

  static void Main(string[] args) {

  String s = Assembly.GetExecutingAssembly().Location;

  RemotingConfiguration.Configure ("Yourserver.exe.config");

  Console.ReadLine();

  }

  }

Q. What about Client Activation codes?

A. Here you are again, you can either do it programmatically: The code below shows what you need to place on your server (host) portion of your code to specify the type of the object being registered

using System.Runtime.Remoting;

      //. . .blah blah blah


      ActivatedServiceTypeEntry as =

      new ActivatedServiceTypeEntry(typeof(InBetween.Manager));

RemotingConfiguration.RegisterActivatedServiceType(as);

On the client,

using System;

  using System.Runtime.Remoting;

  using System.Runtime.Remoting.Channels;

  using System.Runtime.Remoting.Channels.Http;

  using System.Runtime.Remoting.Channels.Tcp;


class RemotingHost {
static void Main(string[] args) {
RemotingConfiguration.ApplicationName = "YOURSERVICENAME";
ActivatedServiceTypeEntry as =
new ActivatedServiceTypeEntry(typeof(InBetween.Manager));
RemotingConfiguration.RegisterActivatedServiceType(as
ChannelServices.RegisterChannel(new HttpChannel(9999));
ChannelServices.RegisterChannel(new TcpChannel(8888));
//etc etc

}
}

or:

<configuration>

  <system.runtime.remoting>

  <application name="YOURSERVICENAME">

  <service>

  <activated type="InBetween.Manager,Manager" />

  </service>

  <channels>

  <channel port="9999" ref="http" />

  <channel port="8888" ref="tcp" />

  </channels>

  </application>

  </system.runtime.remoting>

  </configuration>

Q. So, now you have your client. But how does it activate the remote object?

A. If you're referring to a Server activated or Well-known object that exists at a URL, you would use:

  • Client obtains proxy using Activator.GetObject
  • Client can also obtain proxy using new

Otherwise, you would use the Client to request the object factory (that exists on the URL) to instantiate object and return a proxy to it using

Activator.CreateInstance

Or use, our simple

new

Q. What are the differences between all the object activation codes?

A. To summarise, here's a table

GetObject This returns a proxy for the well-known type served at the specified location. Interestingly, there is no network traffic until first method call. This will cause the object Proxy to be built on the client from metadata of the server. The server activates object on first method call.
GetObject via System.Runtime.Remoting. RemotingServices There is a GetObject wrapper around System.Runtime.Remoting. RemotingServices.Connect too:
Using System.Runtime.Remoting;
   static object o RemotingServices.Connect
   (Type classToProxy, string url);
Activator.CreateInstance

This is a bit of a bad guy as this client activation requires a network round trip. It sends an activation message to the server. But the goody thing is that, you can include constructor parameters (unlike, just new objectName()). The server then creates the object using specified constructor by building an ObjRef about new instance. The then server then sends a message with ObjRef to client. Finally, (after being under the mercies of network traffic, etc.), the client creates proxy from ObjRef...

Dim activationAttributes() As Object = { New _
   Activation.UrlAttribute _
   ("http://localhost:9999/RemotingChat")
   }
Dim o As Object = _
 Activator.CreateInstance_
 (GetType(InBetween.Manager),_
  Nothing, activationAttributes)
   InBetween.Manager c = _
     CType (o, InBetween.Manager)

Q. Anything else?

A. Oh yes... You would need to consider some things when using this code in v1.1 of the framework... You need to add a property called typeFilterLevel to the formatter tag and set it to Full to relax security, or else, you'll get an exception:

System.Security.SecurityException.
 Type System.DelegateSerializationHolder and the types derived from it (such
 as System.DelegateSerializationHolder) are not permitted to be deserialized
 at this security level.
 System.Runtime.Serialization.SerializationException
 Because of security restrictions,
 the type System.Runtime.Remoting.ObjRef cannot
 be accessed.

Be sure to change your config file (on the server):

<configuration>
<system.runtime.remoting>
<channel ref="http" port="7777">
<serverProviders>
<provider ref="wsdl" />
<formatter ref="soap" typeFilterLevel="Full" />
<formatter ref="binary" typeFilterLevel="Full" />
</serverProviders>
<clientProviders>
<formatter ref="binary" />
</clientProviders>
</channel>
<service>
<!-- ... Add your services here ... -->
</service>
</system.runtime.remoting>
</configuration>

and client:

<configuration>
<system.runtime.remoting>
<channel ref="http" port="0">
<clientProviders>
<formatter ref="binary" />
</clientProviders>
<serverProviders>
<formatter ref="binary" typeFilterLevel="Full" />
</serverProviders>
</channel>
<client>
<!-- ... Add your classes here ... -->
</client>
</system.runtime.remoting>
</configuration>

For more details on how to do it programmatically, please refer to this link.

Q. Can you give us a diagram that explains all this?

A. Sure...

Diagram showing all the sinks involved

Background of the Server Activated Chat Program

It's important for you to understand what's going on before we dive into the code (don't worry, it's not really hard). First off, we will have a remotable object called Manager, which will be a singleton object (defined later). Clients will send their messages by calling a method in the one and only one (singleton) Manager object called SendText, which in turn will raise an event, evtReceiveText. This event will be handled by all the chat clients, which then will display the received message on the textbox, txtReceivedMsgs.

OK, let's conceptualize it, we have one and only one remotable object, called the Manager, from the namespace InBetween (which conceptually, sits in between the server and client, serializing messages to and fro). Next, we have one Server, which will register or create a new instance of our own well known service or application, called ChatApplication. Next, we will have the clients themselves, who will implement the Manager's event called, evtReceiveText, which is an event raised to all clients when anyone sends any message.

Now, for those who've done some remoting, I'm not going to use a config file (an XML file to configure the application's name and port, etc), but rather I'll be using these two:

On the client side, to get the Manager object (more comments on as we go on):

theManager = CType(Activator.GetObject(Type.GetType("InBetween.Manager, _
  InBetween"), "http://UP:7777/ChatApplication"),
  InBetween.Manager)

On the server side, to register the well known service (more comments on as we go on):

System.Runtime.Remoting.RemotingConfiguration.RegisterWellKnownServiceType( _
  Type.GetType("InBetween.Manager, InBetween"), _
  "ChatApplication", WellKnownObjectMode.Singleton)

Using the code (Manager.vb)

This is what your solution should look like

(Note: I've included the .config files, just in case you want to try using them)

  1. To start, create an empty solution.
  2. Add a new class library project called InBetween.
  3. Change the default .vb name to Manager.vb.
  4. Copy the code below (Be sure to read the comments as we go on)

    This guy down here is the remotable object.

      Imports System
    
     'We are going to declare an event delegate
    
    
     'Event delegates sound really big... but what this is doing is that:
    
    
     ' "I want to be able, from this class,
    
     ' to raise a type of an event called
    
     'ReceiveText..."
    
    
     ' "And, I want those who use Manager class to handle that event"
    
    
     ' "...Now, I'm going to pass username As String and text As String."
    
    
     ' "It's gonna be up to you, how you're going to handle it"
    
    
     Public Delegate Sub ReceiveText_
     (ByVal username As String, ByVal text As String)
      Public Class Manager _
     Inherits MarshalByRefObject
     
     'Why inherit MarshalByRefObject?
    
    
     'Enables access to objects across application domain boundaries
    
    
     'in applications that support remoting.
    
    
     'Src: .NET Framework Class Library
    
     ' MarshalByRefObject Class [Visual Basic]
    
    
     'Let's break it down (in simple english)...
    
    
     'MarshalByRefObject is the means which allows
    
     ' objects like this class here, to
    
    
     'communicate across boundaries, via remoting.
    
    
     'An application domain is a partition in an
    
     'operating system process where one
    
     'or more applications reside.
    
     'What's this? I thought we already declared an event handler?
    
    
     'Here's where we need to declare the event itself.
    
    
     'Delegates, as its name suggests
    
     'are 'ambassadors' who advertises this event
    
    
     'That's why in our Client.vb,
    
     'we say "theManager.evtReceiveText",
    
     'and not 'theManager.ReceiveText'
    
    
     Public Event evtReceiveText As ReceiveText
     Public Overrides Function _
       InitializeLifetimeService() As Object
    
     'This function, if overriden,
    
     ' allows us to tell the Manager object how long
    
    
     'it should live. If by any reason we'd
    
     'like to have it stay a 'lil longer,
    
     'we renew it and so forth
    
    
      'We won't do anything here. So what happens is that
    
      'the Manager object is governed by a default lifetime.
    
    
      Return Nothing
    
      End Function
      
      Public Function SendText(ByVal username _
        As String, ByVal text As String)
    
      'Later in the client.vb code,
    
      'you would see that chat clients (like John Doe),
    
      'will raise thise event by calling SendText
    
      'with the appropriate paramaters.
    
    
      'This event is then propagated to ALL clients,
    
      ' like Jones and so forth.
    
    
      'On Jones' PC (for example), Client.vb
    
      'would handle this event by displaying something like
    
    
      '"John: yada yada" on the txtReceivedMsgs.
    
    
      'Of course John's client window sould also show the same
    
    
      RaiseEvent evtReceiveText(username, text)
    
      End Function
      
      Public Function getHash() As String
    
      'this is just a handy function to reaffirm
    
      ' that all your clients are communicating
    
    
      'with a ONE and only Manager object
    
    
      'Which means (in simple English),
    
    
      'John and the Jones will see the very same
    
      'hash code which was assigned to the
    
      'Manager object
    
    
      Return Me.GetType.GetHashCode().ToString
    
      End Function
      
      End Class

Using the code (Server.vb)

  1. Add a new Console Application project called Server, or whatever you want to call it.
  2. Change the default .vb name to Server.vb.
  3. Don't forget to add a reference to InBetween, 'cause we're going to make the first ever call to it and calling its getHash method.
  4. Copy the code below (Be sure to read the comments as we go on)

    This is the guy that's in charge of registering our service, the ChatApplication. We notice that we create a singleton as shown below:

      Imports System.Runtime.Remoting.Channels
    
     Imports System.Runtime.Remoting.Channels.Http
    
     Imports System.Runtime.Remoting
    
     Imports System
    
     Imports InBetween
      
    
     public class Server
      
      Public Shared Sub Main()
    
     Dim server1 As Server
    
     server1 = New Server()
    
     End Sub
      
     Public Sub New()
       'Create a HTTP channel for our use
    
    
     'We'll 'talk' on this port
    
    
     'IMPORTANT: Make sure you don't have
    
     'anything running on this channel!
    
    
     Dim chan As IChannel = New HttpChannel(7777)
        'Register it
    
    
     ChannelServices.RegisterChannel(chan)
       'I could have read the config from an xml file with :
    
    
     'System.Runtime.Remoting.RemotingConfiguration.
    
     'Configure(your.config.file)
    
    
     '(XML format)
    
    
     'Refer .NET Framework Class Library
    
     'RemotingConfiguration.Configure Method
    
     '[Visual Basic]
    
    
     'BUT somehow, I just couldn't make it work!
    
     'So I went for this great 1 line code shown below:
    
     'Notice these things:
    
    
     '1. We are registering a service: RegisterWellKnownServiceType
    
    
     '2. It's of type: Type.GetType("InBetween.Manager, InBetween")
    
    
     ' InBetween is the namespace, Manager is the class
    
    
     '3. We're calling that application, ChatApplication
    
    
     '4. It's of type: Singleton
    
    
     ' Why Singleton and not singlecall? If u chose singlecall,
    
    
     ' everyone client (John and the Jones)
    
     'would be creating their own Manager objects
    
    
     ' which would mean no message ever gets across to anyone
    
    
     System.Runtime.Remoting.RemotingConfiguration._
       RegisterWellKnownServiceType(_
       Type.GetType("InBetween.Manager, InBetween"), _
       "ChatApplication", WellKnownObjectMode.Singleton)
     'I registered the Manager class and called getHash
    
    
     'Read Manager.vb for more details on getHash
    
    
     Dim Manager1 As New Manager()
    
     Console.WriteLine("The Manager object's ID:" _
        & Manager1.getHash())
    
     System.Console.WriteLine("Hit ENTER to exit...")
     'We don't want this object to die out too fast, so we just put a
    
     'ReadLine here to sustain the object's lifetime
    
    
     System.Console.ReadLine()
      
      End Sub
    
     End Class

Using the code (Client.vb)

Now, here's where we design the client.

  1. Add a new Windows Application project called Client.
  2. Change the default .vb name to Client.vb. If you're planning on copy and pasting, skip to 5.
  3. Add two multi-lined textboxes called txtReceivedMsgs and txtMsgToSend to handle received messages and to type messages into, respectively.
  4. Add a simple button, btnSend.
  5. Again, add a reference to InBetween and System.Runtime.Remoting. We need the latter too, because we're going to need to create a HTTPChannel object.
  6. Copy the code below, make sure you name the variables as above (if you've created the interface yourself). (Be sure to read the comments as we go on)

Sample Image - RemotingSimpleEng.gif

Imports System.Runtime.Remoting.Channels.Http
 Public Class Client Inherits System.Windows.Forms.Form
  Private theManager As InBetween.Manager
#Region " Windows Form Designer generated code "
Public Sub New()
MyBase.New()

'This call is required by the Windows Form Designer. InitializeComponent()

'Add any initialization after the InitializeComponent() call

Dim chan As HttpChannel

'Create a HTTP channel for our use

'IMPORTANT: Make sure you don't have anything running on this channel!

chan = New HttpChannel("8888")

'Registers a channel with the channel services.

System.Runtime.Remoting.Channels.ChannelServices.RegisterChannel(chan)

'Creates a proxy for a currently running remote object

'This remote object is the our InBetween.Manager's instance

'NOTE: Change 'UP' to your Chat server's name

'http://UP:7777/ChatApplication

theManager = CType(Activator.GetObject(Type.GetType("InBetween.Manager,_
  InBetween"), "http://UP:7777/ChatApplication"), InBetween.Manager)

'Add our event handler here

'In other words, tell this fellar,

'"when you receive an event called evtReceiveText

'(of type InBetween.Manager),

'then use the sub called HandleReceivedMsg

'to handle it

Try
 AddHandler Me.theManager.evtReceiveText, _
  AddressOf Me.HandleReceivedMsg
Catch
 e1 As Exception
 'Our simple exception handler

 MessageBox.Show(e1.Message)
End Try

'Cosmetic, I'm against it, but...

'(This displays a caption on your

'client window that says "Client on <PC NAME>")

Me.Text = "Client on " & _
 Windows.Forms.SystemInformation.ComputerName()

'Now, you would notice that the getHash(),

'will return a string that identifies

'the 'theManager's hash code.

'This Hash code will appear on ALL clients.

'Why? Simple, we are dealing with ONE and

'only ONE instance of InBetween's Manager class

'We specified singleton on the server (Module1.vb), remember?

'It's easy to remember, a 'single' 'ton', "SINGLE-TON"

MessageBox.Show(Me.theManager.getHash())
End Sub

'Form overrides dispose to clean up the component list.

Protected Overloads Overrides Sub _
  Dispose(ByVal disposing As Boolean)
 If disposing Then If Not (components Is Nothing) Then
   components.Dispose()
 End If
 End If
 MyBase.Dispose(disposing)
 End Sub
'Required by the Windows Form Designer

Private components As System.ComponentModel.IContainer

'NOTE: The following procedure is required by the Windows Form Designer

'It can be modified using the Windows Form Designer.

'Do not modify it using the code editor.

Friend WithEvents txtReceivedMsgs As System.Windows.Forms.TextBox
Friend WithEvents btnSend As System.Windows.Forms.Button
Friend WithEvents txtMsgToSend As System.Windows.Forms.TextBox
<System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
Me.btnSend = New System.Windows.Forms.Button()
Me.txtReceivedMsgs = New System.Windows.Forms.TextBox()
Me.txtMsgToSend = New System.Windows.Forms.TextBox() Me.SuspendLayout()
'

'btnSend

'

Me.btnSend.Location = New System.Drawing.Point(280, 160)
Me.btnSend.Name = "btnSend" Me.btnSend.TabIndex = 0
Me.btnSend.Text = "&Send"
'

'txtReceivedMsgs

'

Me.txtReceivedMsgs.Location = New System.Drawing.Point(0, 8)
Me.txtReceivedMsgs.Multiline = True
Me.txtReceivedMsgs.Name = "txtReceivedMsgs"
Me.txtReceivedMsgs.ReadOnly = True
Me.txtReceivedMsgs.Size = New System.Drawing.Size(360, 88)
Me.txtReceivedMsgs.TabIndex = 1 Me.txtReceivedMsgs.Text = ""
'

'txtMsgToSend

'

Me.txtMsgToSend.Location = New System.Drawing.Point(0, 104)
Me.txtMsgToSend.Multiline = True
Me.txtMsgToSend.Name = "txtMsgToSend"
Me.txtMsgToSend.Size = New System.Drawing.Size(360, 48)
Me.txtMsgToSend.TabIndex = 2
Me.txtMsgToSend.Text = ""
'

'Client

'

Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
Me.ClientSize = New System.Drawing.Size(360, 189)
Me.Controls.AddRange(New System.Windows.Forms.Control() _
   {Me.txtMsgToSend, Me.txtReceivedMsgs, Me.btnSend})
Me.MaximizeBox = False Me.Name = "Client"
Me.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen
Me.Text = "Client" Me.ResumeLayout(False)
End Sub
#End Region
Private Sub btnSend_Click(ByVal sender As System.Object, _
   ByVal e As System.EventArgs) Handles btnSend.Click
'What happenning here?

'Once we press our 'Send' button,

'we raise an event via SendText method of Manager (InBetween)

'Then just, erase our textbox - txtMsgToSend 'Easy isn't it?

'To follow the event, peek at InBetween's Manager's SendText method

Me.theManager.SendText(Windows.Forms.SystemInformation.ComputerName,_
   Me.txtMsgToSend.Text)
txtMsgToSend.Text = ""
End Sub

Sub HandleReceivedMsg(ByVal username As String, ByVal text As String)
'Ok, here's what happens...

'John Doe sends u a message, the Manager object raises an event,

'your client intercepts it, and execution drops down here...

'You then append the text here...

'"... and I thought chat programs were hard..."

'well anyway, here's the line that does it

Me.txtReceivedMsgs.AppendText(username & " : " & text & vbCrLf)
End Sub

Private Sub Client_Closing(ByVal sender As Object, _
  ByVal e As System.ComponentModel.CancelEventArgs)_
  Handles MyBase.Closing
Try
 'Ok, let's undo what we've done

 'We've added a handler, (remember?), so now we need to remove it

 'You basically do this, ...

 RemoveHandler theManager.evtReceiveText, _
    AddressOf Me.HandleReceivedMsg
Catch
 e1 As Exception
 
'Exception handling for... err, simple ppl like us...

MessageBox.Show(e1.Message)
End Try
End Sub

Private Sub Client_Load(ByVal sender As System.Object,_
 ByVal e As System.EventArgs) Handles MyBase.Load
End Sub
End Class

Run It! (Conclusion)

  1. Build the solution. You will be prompted to set the startup object, just do so.
  2. As you're waiting, note these points:
    • We could have used the .config files, but I prefer to code it to show you how to do without the .config.
    • As each client window loads up, you'll see the same hash code. It's the hash code of the singleton Manager object.
    • We're using the same HTTP port (7777) on both sides (client and server). We could use TCP if we wanted to. You'd better make sure John Doe's FTP server (or any other application) isn't running at that port too (at the same time)!
    • Our Well-known object was exposed on the server end.
  3. Just before you really run it, place the server.exe into the same directory as client.exe. Place other clients on other PCs.
  4. Run the server.exe first, then, all the clients.
  5. Chat!

So what's the conclusion?

  • The most important thing to note is: We've done a server activation
  • MSDN says "... Server-activated objects are objects whose lifetimes are directly controlled by the server. The server application domain creates these objects only when the client makes a method call on the object, not when the client calls new (New() in Visual Basic) or Activator.GetObject(); this saves a network round trip solely for the purpose of instance creation. Only a proxy is created in the client application domain when a client requests an instance of a server-activated type... There are two activation modes (or WellKnownObjectMode values) for server-activated objects, Singleton and SingleCall..." .NET Framework Developer's Guide Server Activation [Visual Basic]
  • Our first method call to the object was in Server.vb. We could also have made the first call from client.vb. Try this, remove the comment on theManager = New InBetween.Manager() in client.vb, and in server.vb, comment these:
    Dim Manager1 As New Manager()
      Console.WriteLine("The Manager object's ID:" & Manager1.getHash()) 

    So you see, it still works the same... I had to make the first call to getHash() in Server.vb because I wanted to get the hash code on server.vb to display. So let me recap: the reason why we call this server activated is because our object's lifetime was directly controlled by the server via Singleton activation. We didn't instantiate anything by saying Dim Manager1 As New Manager() , that just registered the object. The actual object existed only after we made the method call.

Points of interest

Strange thing to note: The first client to be executed, must be run from the same directory as the server.exe! Subsequent clients need not be started in the same directory as server.exe. Yes that's strange, otherwise, you'd get this silly error which makes no sense:

System.IO.FileNotFoundException
File or assembly name Client , or one of its dependencies, was not found.

Yes, it's a known problem, you might want to read more on this here.

NOTE: The reason for this problem is that the client needs the server's metadata to build the client's proxy object. The solution is shown in my 3rd article, here.

Etc

  • For those who've done COM before, "What's the difference between .NET remoting and remotely activated COM?". Unlike COM, remoting does not start the host or server application for you. This is an important difference between .NET remoting and remote activation in COM.
  • MSDN calls our Manager.vb a remotable type. Our Server.vb is the host application
  • Your host application domain is not limited to our simple chat program, but they may be Windows Services, console applications, Windows Forms applications, Internet Information Services (IIS) processes, or ASP.NET applications.
  • General Host Tasks (e.g. what you should think about when coding your own host)
    • What's my host application domain going to be like? (Windows Services, console applications, Windows Forms applications, Internet Information Services (IIS) processes, or ASP.NET applications)
    • Which activation model should I use? (Client/Server activation)
    • Choose a channel (HTTP or TCP) and a port. Register it using ChannelServices.RegisterChannel. Remember, you can't use the same port as Uncle Joe's FTP server...
  • General Client Tasks (e.g. what you should think about when coding your own client)
    • What's my client application domain going to be like? (Windows Services, console applications, Windows Forms applications, Internet Information Services (IIS) processes, or ASP.NET applications)
    • Which activation model should I use? (Client/Server activation)
    • Should I use the client activation URL (e.g. ProtocolScheme://ComputerName:Port/PossibleApplicationName) or the well-known object URL (e.g.. ProtocolScheme://ComputerName:Port/ PossibleApplicationName/ObjectUri) of the remote type.
    • Choose a channel (HTTP or TCP) and a port. Register it using ChannelServices.RegisterChannel.
  • If you want to try to use the .config file, which I don't like, bear in mind that most of the problems using .NET remoting occur because some of these settings are either incorrect or do not match the configuration settings for client applications. It is very easy to mistype a name, forget a port, or neglect an attribute. If you are having problems with your remoting application, check your configuration settings first.

History

  • Friday 13, June 2003: Uploaded first version.
  • Saturday 14, June 2003: Uploaded second version (due to great ratings).
  • Monday 16, June 2003: Added the Etc section.
  • Friday, 27 July 2003: Added a slightly better introduction.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here