article

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

Email
Submitted on: 1/1/2015 9:24:00 PM
By: Daniel Ang C. M. (from psc cd)  
Level: Beginner
User Rating: By 22 Users
Compatibility: VB.NET
Views: 2791
 
     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 this few steps.

This article has accompanying files

 
				

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 this few steps.

Background

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 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)

 

(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 charged 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 multilined textbox 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.
5. 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)

 



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 side (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 of 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: http://www.dotnet247.com/247reference/msgs/12/63594.aspx

I might want to come out with a good walkaround that later on, but till then, all the best in remoting! Spend a sec' to rate me, will ya? Thanks

Etc

  • For those who've done COM before, "what 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 (eg. 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 (eg. 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 (eg. ProtocolScheme://ComputerName:Port/PossibleApplicationName
      ) or the well-known object URL (eg. 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, 2003: Uploaded first version.
Saturday 14, 2003: Uploaded second version (due to great ratings).
Monday 16, 2003: Added the Etc section

winzip iconDownload article

Note: Due to the size or complexity of this submission, the author has submitted it as a .zip file to shorten your download time. Afterdownloading it, you will need a program like Winzip to decompress it.Virus note:All files are scanned once-a-day by Planet Source Code for viruses, but new viruses come out every day, so no prevention program can catch 100% of them. For your own safety, please:
  1. Re-scan downloaded files using your personal virus checker before using it.
  2. NEVER, EVER run compiled files (.exe's, .ocx's, .dll's etc.)--only run source code.

If you don't have a virus scanner, you can get one at many places on the net including:McAfee.com


Report Bad Submission
Use this form to tell us if this entry should be deleted (i.e contains no code, is a virus, etc.).
This submission should be removed because:

Your Vote

What do you think of this article (in the Beginner category)?
(The article with your highest vote will win this month's coding contest!)
Excellent  Good  Average  Below Average  Poor (See voting log ...)
 

Other User Comments


 There are no comments on this submission.
 

Add Your Feedback
Your feedback will be posted below and an email sent to the author. Please remember that the author was kind enough to share this with you, so any criticisms must be stated politely, or they will be deleted. (For feedback not related to this particular article, please click here instead.)
 

To post feedback, first please login.