Introduction
This tutorial will introduce the concepts of publishing and
subscribing data over a network using the C# programming language and will
hopefully provide not only an understanding of how publish and subscribe
applications work but should provide the reader with the tools needed to
develop a publish and subscribe style application of their own. The demo code
is split into five separate projects. To start with we will look at the
mechanics of how the project works and then move on to the details of each of
the projects components.
The projects were developed with Microsoft Development Environment
version 7 on Windows XP.


Timers
The project is actually run through the use of timers that at set
intervals will in the case of the Publisher project the timer fires an instance
that will publish anything that has registered an interest in subscribing to
the information. It should be noted that in a real application there would be
some kind of identifier so that the Publisher would only send certain
information to certain subscribers. So at the moment the publisher will send
all the subscribed information to all the subscribers. The subscriber/s have a
timer that once fired redraws all the subscribed items that it has information
on and then waits for the timer to fire again. At this point I am deliberately
avoiding how the information gets from the publisher to the subscriber but we
will get to that, first we need to see how to set up the timers correctly.
In order to set up a timer you declare it like any other C# object,
private Timer timer;
timer = new Timer();
timer.Tick += new EventHandler( TimerEvent );
timer.Interval = 5000;
timer.Start();
The Timer is declared as a private member of the class which is then
instantiated during the setting up of the project ( The above code is from the
Publish project. ) The Timer has an Interval value that is set at the number of
milliseconds between the timer firing and a Tick function that is an
EventHandler
object that indicates the function that is to be called when the
timer is fired. Once these have been set the timer start can be called and the
function will be called whenever the timer fires.
An EventHandler
is a special function type that is called whenever an event is
fired be it through the use of a timer or as we will see later a callback
function. In the case of the Timer class the Tick member is an event that takes
a an EventHandler
function to call when the Tick event is fired every
Timer.Interval
milliseconds. The function for the event handler is delegated to
the function defined with the following properties,
private void TimerEvent( object PassedObject, EventArgs TimerArgs )
In the function declaration above both the class specification of private and
the name of the function are up to the developer but the function must have a
return type of void and take the two parameters defined above. The object
variable PassedObject
is the sender or the source of the event, because this is
used to implement the calling of a timer function this will be the timer object
that is defined in the class although to use it to say change the frequency of
the call you would be required to cast it back to the type of a Timer. e.g.
Timer tempTimer = ( Timer )PassedObject;
tempTimer.Interval = ( tempTimer.Interval - 1000 );
The EventArgs
parameter is the event argument. That is, it is the object that
the event is fired for. The EventArgs
class is stateless which means that by
itself it will hold no data. If we wish to pass data to an EventHandler
function, a class must be created that inherits from EventArgs
. This is exactly
what will be happening with the FakeTicker
class later. But for now that is how
the timer works first by declaring a Timer object and a function that follows
the EventHandler
definition of having a void return type and takes an object as
its first parameter and a EventArgs
object as its second parameter. All that is
needed then is to set up the function so that it is called when the event is
fired by declaring a new EventHandler
for the Timer.Tick
event and passing the
name of the declared function as a parameter.
Crossing Boundaries
Having gotten an idea of events we now want to to do something a bit more
interesting than fire them within projects. That's all well and good but what
if we could use these things to get programs to talk to each other? I'm sure it
would be even better if we could do it in such a way that the programs could be
running on different computers at the same time, but what exactly would that
involve? The answer is basically Channels. Get to love them as from now on
they'll be part of every Windows release.
Crossing The Channel
The sample code uses two TcpChannel
objects, both of which are used in the
Windows Form classes. One is used to advertise the Publisher object and one is
used to get the Publisher object. This means that if the code is run on two
computers the Publish object can be fired up on one and the when the client is
ready it can get the Publisher object. As long as the Publish application is
running and the client knows where to find it there will be no problems.
Naturally when publishing an object on one computer an receiving it on another
the code is going to be slightly different. So let's look at Publishing an
object first.
private TcpChannel Channel;
Channel = new TcpChannel( 8090 );
ChannelServices.RegisterChannel( Channel );
RemotingConfiguration.RegisterWellKnownServiceType( typeof( Publisher ),
"PublishSubscribe", WellKnownObjectMode.Singleton );
To start with the TcpChannel
is created as a private member of the class and
when it is constructed using new. the constructor is passed the value of 8090
which is the Tcp Channel that the code will make the Publisher object available
on. Once the TcpChannel
is created it needs to be registered with the .NET
runtime ChannelServices
using the ChannelServices.RegisterChannel
which will
then allow the channel to be broadcast or to receive or even do both at the
same time. Then all that has to be done is to either make the class object
available to other computers or processes on the same machine. This is done
using the RemotingConfiguration
class that allows the registration of the
object using either of the RegisterWellKnownType
functions which come in two
flavors the Service and the Client. To publish an object, the
RegisterWellKnownServiceType
is used. This takes the type of the object to be
published, in this case the Publisher object, the Uri name of the published
object and the publication type. The publication type can be either the
WellKnownObjectMode.Singleton
or the
WellKnownObjectMode.SingleCall
. The
WellKnownObjectMode
is an enumeration that contains only these two options, the
Singleton means that every client that accesses the Publisher object will receive
the same Publisher object and the
SingleCall
option means that for
every client that calls into the service a new object of type Publisher will be
created. For this project the Publisher Object is published as a Singleton as
the Publish project that broadcasts the Publisher object later gets the object
that it publishes itself so that it can call into the Publisher object when the
timer is fired. Admittedly this is a fudge to make the project work and in the
real world you would expect more than one object to be involved in the
publication of data and the receiving of the information to be published.
Once the object is published there needs to be a mechanism to use it. Ignoring
for a moment that the publishing project subscribes to the object it publishes,
lets look at the client code in the Subscribe project.
ClientChannel = new TcpChannel( 0 );
ChannelServices.RegisterChannel( ClientChannel );
try
{
NewPublisher = ( Publisher )Activator.GetObject( typeof( Publisher ),
"tcp://localhost:8090/PublishSubscribe" );
}
catch( NullReferenceException nullExp )
{
MessageBox.Show( "The url for the object is invalid "
+ nullExp.Message );
}
catch( RemotingException remExp )
{
MessageBox.Show( "The object type is not defined properly, " +
"it needs to be derived for a remoting class " +
remExp.Message );
}
Aws with publishing an object it is necessary to first of all create a
TcpChannel
object that can be used to receive information. The important thing
to note about the objects creation here is that the channel is created on a
port of 0 because if at this point you try to register the Channel on the port
8090 the application will throw an exception that says that the channel is
already in use. Using 0 allows the client to listen on any channel the
actual channel that the client uses is passed when we get the object from the
publisher.
"tcp://localhost:8090/PublishSubscribe"
Before we can do this though we need to register the client channel in exactly
the same way that the publisher is registered by calling
ChannelServices.RegisterChannel( ClientChannel );
Once the client is registered the publisher object is obtained by using the
Activator.GetObject function.
NewPublisher = ( Publisher )Activator.GetObject( typeof( Publisher ),
"tcp://localhost:8090/PublishSubscribe" );
The Activator class is effectively a helper class for creating objects
remotely. The GetObject
function returns an object that is of the type
specified by the first parameter and the second parameter is the url of the
object in this case the full url for the object is
"tcp://localhost:8090/PublishSubscribe" where "tcp" is the channel protocol,
"localhost" is the current machine address this can be Localhost as it is here
or an actual address such as 192.2.2.2. The "PublishSubscribe" is the name of
the project that the publisher is publishing on. Finally the object that is
returned is cast back to its object type and the Publisher
object NewPublisher
is made equal to it. Well its made equal to the proxy of the object and not the
actual object itself but as far as the NewPublisher
object is concerned it is
all the same.
Publishing And Subscribing
A publish and subscribe set up is based around two functions.
public delegate void PublishedTickerFunction( object SourceOfEvent,
FakeTicker FakeTickerArg );
public event PublishedTickerFunction OnUpdatedFakeTicker;
There are two keywords to take notice of here the first is a delegate and the
second is the event keyword. A delegate is a template to a function. By
declaring something as a delegate you are telling the compiler that at some
point you are going to be using a function with this signature. This is used in
much the same way as you would use the timer only in this case the delegate
function that will be used will be in a separate project, across process
boundaries and even across computers. As with the timer two parameters are
declared for the function the first is the object that represents the source of
the event . In this case whenever a function of the PublishedTickerFunction
type is called the object parameter SourceOfEvent
is equal to the Singleton
Publisher object declared when the Publisher is published. If the call to
RemotingConfiguration.RegisterWellKnownServiceType
had taken the
WellKnownObjectMode.SingleCall
as its third parameter then the
SourceOfEvent
object would be equal to the individual Publisher object that would have been
created whenever a client made a connection to the object being broadcast. The
second parameter for the function is of the
FakeTicker
type this is an object
that is derived from the
EventArgs
class that is used in the timer. Being a
delegate the
PublishedTickerFunction
is not called in the way that you would
expect if you are new to the concept of delegates. The function is not called
this way
Publisher.PublishedTickerFunction( object SourceOfEvent,
FakeTicker FakeTickerArg );
It is however called this way.
NewPublisher.OnUpdatedFakeTicker +=
new Publisher.PublishedTickerFunction( NewSubscriber.OnSubscriberUpdate );
The code line above from the Subscribe project shows that the Singleton
NewPublisher
object adds the the required function to be called back from the
publisher to its event definition in the same way that the timer adds an event
handler to its
timer.Tick
EventHandler. What is happening here is exactly the
same as with the timer only it will go across process boundaries. The function
to be called back when and an event is triggered is called naturally enough a
callback function and this is declared as,
public void OnSubscriberUpdate( object SourceOfEvent,
FakeTicker fakeTickerArg )
It can be seen from the above line that the function above declared in the
Subscriber class takes exactly the same parameters that the
PublishedTickerFunction
specifies but the call to the
PublishedTickerFunction
which you will notice is called as though it were a static function, takes as
its parameters not the arguments it specifies but a function that itself has
the same declaration signature as declared by the
PublishedTickerFunction
.
Which in practice means that whenever the code,
foreach( FakeTicker fake in FakeTickerList )
{
fake.Calculate();
OnUpdatedFakeTicker( this, fake );
}
is executed all the clients that have registered with the Publisher application
will be notified of the updates to each FakeTicker
in the FakeTickerList
. Which
means that every time that the loop is run not just one but all clients will be
called as the OnUpdatedFakeTicker
is an event handler list and not a single
call as it is with the time. Though seeing as the timer.Tick
function uses the
same += way of adding the function that is called by the timer it is theoretically
possible at least to have more than one function being called
whenever the timer event is triggered.
The Components
FakeTicker
The FakeTicker
project is a dll that is referenced by all the other projects.
The reason for this is that the file could either be included in every project
which would lead to circular references where the compiler would never be able
to release any resources allocated to the FakeTicker
class or only included in
some of the projects which wouldn't have the desired result for the program.
The FakeTicker
class is declared as,
[Serializable]
public class FakeTicker : EventArgs
{
The reason for this is that to be included in the event firing function the
FakeTicker
class must be derived from the
EventArgs
class. This is a class
which has the Serializable attribute which means it can be passed across the
wire, which is just a away of saying that it can be passed between processes wherever
those processes are running. This in any case is what we are using
the Serializable attribute for in this project, though it can be used for
writing the object to any medium that can receive it.
Publish
The Publish project contains the PublishService
class that controls the Publish
form. The class sets up a timer that fires every five seconds and publishes any
events that the clients have subscribed to. This class publishes the Publisher
object as a Singleton as described above and then subscribes to it itself
using,
NewPublisher = ( Publisher )Activator.GetObject( typeof( Publisher ),
"tcp://localhost:8090/PublishSubscribe" );
Publisher
The Publisher Project is the main part of the application that contains
the declarations of the delegate and the event functions that drive the
application. This class also contains the PublishNewEvents
function that
controls calls the OnUpdatedTicker
function which ensures that all the
subscribed clients are called back with the latest data. This class inherits
from MarshalByRefObject
which is a framework class that will set up the proxies
or code that will allow the process boundaries to be crossed while as far as
the local code is concerned there are no process boundaries being crossed.
Subscribe
The Subscribe project contains the SubscibeClient
class that runs the Subscribe
form and sets up the callback function with the publisher so that the
OnSubscriberUpdate
function will be called whenever the publisher fires an
event. The
SubscribeClient
class subscribes to the published Publisher object
and sets up a timer that runs every three seconds to check if the data has
been updated. This is set to a shorter time than the class that publishes
the data so that the clients always have the most relevant data but it is set at
not too short a time so that if you have more than one Subscribe project
running you can see the clients update at slightly different times.
Subscriber
The Subscriber project contains the Subscriber class that also inherits from the
MarshalByRefObject
class that allows the callbacks that are set up in the code
to be called when a new event is published. The class is quite simple in that
it mainly contains the callback function that is triggered from the publisher.
All this class does once the function is called is save the data that the
function receives so that the Subscribe form can display it when it's own timer
is fired.
Running The Project
To run the project press F5 and the Publish application will start in debug
mode. Then right click on the Subscribe Project go to Debug and left click on
start new instance. Then type in a name and give it a starting value. No
attempt is made for numeric accuracy so it doesn't really matter what number
you set. Note there is no set limit on how many Subscribe projects you can run.
Conclusion
With any luck this will enlighten more people than it confuses but as with many
things in programming sometimes you just don't get them until you try them so
try it. Remoting with C# can open up whole new ways of programming with server
banks containing ready to use classes that only have to be picked up. Of course
this probably wont happen like that as the requirements will always change
slightly from project to project but think about it for a minute using this
technology you can run a security database from a separate server that checks
people's security access in realtime by simply getting a security object from
the server and questioning it or if you're running from a large firm where a
lot of things are standardised. you can programmatically setup computers just
by running a small program that gets everything from the server. And the most
important thing of all is that once you understand the concepts writing the
code is really easy.
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.