Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

A simplified non topic based Event Notification Server in wcf with multiple protocol support.

4.67/5 (33 votes)
19 Sep 2008CPOL7 min read 1   1.9K  
To be notified when an alert is generated in a system without filtering for what type of event you are registered.

Contents

Introduction

Part I - Basic

What is Event Notification Server(ENS)?
Multiple Protocol Support
How does it work?
Procedure to use ENS and test clients.
Limitation

Part II - Internal.

Data Structure and Algorithm
Architecture of Event Notification Server.

Publisher & Subscriber Model

Part III - Documentation of Code

Introduction

A simplified Event Notification Server allows you to be notified when an alert is generated in a system without filtering for what type of event you are registered.In various scenarios we need such type of publish subscribe scheme .Here a simplified Event Notification Server is implemented in wcf using pub/sub scheme with multiple protocol support.In this implementation if a client subscribes with a server, the client will get all the events sent by the publisher.

Sources

I read the Juval Lowy Article on msdn magazine but the article is based on topic based pub/sub scheme .But for many cases a simple pub/sub scheme is needed without topic scheme .So i made many changes to the code and finally made a simplified version,i hope that this simplified implementation with multiple protocol support will be helpful to many men like me in their project.The code used in this article is based on the juva's lovy article, but some parts are not changed.

Part I - Basic

What is Event Notification Server?

Event Notification Server is an “Event Notification” system. It connects programs that provide information changes over time (publishers) with programs that want to receive notifications of those changes (subscribers). It is the middle ware that deals with publishers and subscribers.

Multiple Protocol Support

There are essentially two main groups applications that communicate through networks or the Internet - client server and the and peer-to-peer. Event Notification Server employ client server architectures for better performance.

Here the Event Notification Server expose different protocols based connectivity. A client
based on his requirement will choose protocol to connect.

Image 1

Diagram 1 - Several clients connected to a Event Notification Server with multiple protocol.

If a client is in the same machine with Event Notification Server (ENS) then it will connect with ENS thru Named Pipe protocol Because Named Pipe protocol is the fastest for same machine boundary.


If a client is in the same Local Area Network (LAN) with ENS it will connect with ENS thru TCP protocol Because TCP is a connection oriented reliable protocol.


If a client is in the same Virtual Private Network (VPN) with ENS then it will connect with ENS thru TCP protocol Because TCP is a connection oriented reliable protocol.

If a client is in the Internet with ENS then it will connect with ENS thru HTTP protocol Because HTTP is a firewall friendly protocol.It is unidirectional by default but using wsdualhttp binding i get bidirectional behavior.

How does it Work?

Here each client will register himself to ENS then when an event comes to ENS then ENS will send it to clients that are registered.
For Example we have 10 clients and all 10 clients would register to the ENS to receive notifications (client programs are individual programs) and then whenever the event occurs from the publisher, the ENS notifies all those clients.

Procedure to use ENS and test clients:

How to start the total System:

In the VS2008 just press F5 to launch total system and then it will be ready to go.

Event Notification Server:

Image 2

Nothing will need to be configured.


It will start with machine IP on which it is started.


For event receiving purpose(publisher service)
it will open port as follows:
8000 For HTTP.
8001 for TCP.
Mypipe1 for NamedPipe

For event broadcasting purpose(subscription service)
it will open port as follows:

8003 For HTTP.
8002 for TCP.
Mypipe2 for NamedPipe

How to send event:

1. For sending a single event clicks on button “Fire a single Event”.


2. For sending auto event set the interval then click the button
“Fire Auto Event”.


Image 3

Image 4

Image 5

How to receive event:


1. Click the “Subscribe” button of test client.


2. Then it will start to receive the events.

Image 6

Image 7

Image 8

Limitation

Does not work under NAT.
NO authentication, encryption
No Fault Tolerance Support .
No provision to prevent DOS (Denial of Service attack) attack.

Part II - Internal.

Data Structure:

A simple List contains the list of subscribers.

Algorithm:

Decouple the publishers from the subscribers by introducing a dedicated subscription service and a dedicated publishing service.

Subscribers that want to subscribe to events will register with the subscription service.

Subscription service will manages the lists of subscribers.

When there is an event in publisher

o The publisher will want subscriber list form subscriber service.

o Then Publisher will send the event to each subscriber of the list.

Architecture of Event Notification Server:

architecture.PNG


Publisher & Subscriber Model

Publish/subscribe (or pub/sub) is an asynchronous messaging paradigm where senders (publishers) of messages are not programmed to send their messages to specific receivers (subscribers). Rather, published messages are characterized without knowledge of what (if any) subscribers there may be. Subscribers express interest and receive messages , without knowledge of what (if any) publishers there are. This decoupling of publishers and subscribers can allow for greater scalability and a more dynamic network topology.Much the same way, the publisher can only notify subscribers it knows about. There is no way for the publisher to deliver an event to whomever wishes to receive it, nor is there an ability to broadcast an event. In addition, all publishers must repeatedly have the necessary code to manage the list of the subscribers and the publishing act itself.There is no way for a subscriber to ask that if an event is fired, the application should create an instance of the subscriber and let it handle the event.
Setting up subscriptions has to be done programmatically.

Part III - Documentation of Code

DataContract

[DataContract]
public class AlertData
 {
   private string _SeqNo;
  private string _Description;
 
   [DataMember]
   public string SeqNo { get { return _SeqNo; } set { _SeqNo = value; } }

  [DataMember]
   public string Description { get { return _Description; } set { _Description    = value; } }
   }

Event Publishing


The publishing service exposes the events contract in an endpoint, you need to mark the events contract as a service contract.

/// <summary>
/// This is the service contract of Publish Service

[ServiceContract]
interface IEvent
{
[OperationContract(IsOneWay = true)]
void OnEvent(AlertData e);
}

To provide your own publishing service, derive from IEvent and use method to deliver the event to all subscribers.

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
class Publishing : IEvent
{
 
   /// This method is called from the publisher client to send the event.
   public void OnEvent(AlertData e)
   {

       IEvent[] subscribers = Subscription.GetClientList(); 
       Type type = typeof(IEvent);
       MethodInfo methodInfo = type.GetMethod("OnEvent");
       foreach ( IEvent subscriber in subscribers)
       {
           try
           {
               methodInfo.Invoke(subscriber, new object[] { e });
           }
           catch 
           {
       
              }
       }
   }
}

Managing Registration(subscriptions):

For managing registration, I defined IRegistration interface shown in Example 1.

/// <summary>
   /// This is the Service contract for SubscriptionService
   /// </summary>
   [ServiceContract(CallbackContract = typeof(IEvent))]
   public interface IRegistration
   {
   [OperationContract]
   void Register( );

  [OperationContract]
   void UnRegister();

   [OperationContract]
   string GetDateTime();
   }

The callback interface is

   /// <summary>
   /// This is the service contract of Publish Service
   /// </summary>
   [ServiceContract]
   interface IEvent
   {
   [OperationContract(IsOneWay = true)]
   void OnEvent(AlertData e);
}

The application needs to expose its own subscription service in the form of an endpoint that supports its specific interface of IRegistration. To do so, the application needs to provide a service class that derives from IRegistration specify the callback contract as a type parameter,.

   /// This is the class for Subscription Service that is deployed to listen.
   
 [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
   class Subscription : IRegistration
   {
 
   /// This the data structue that is used for pub/sub scheme. 
   /// Here list is used to hold the subscribers.
 
   static List<IEvent> m_ClientList;
 
  
   /// This is constructor of the class.It is used here to create the instance
   /// of the pub/sub data structure 
   static Subscription()
   {
   m_ClientList = new List<IEvent>();
   }
  
   /// This method return the complete subscriber list to publisher service.
   
   internal static IEvent[] GetClientList()
   {
   lock (typeof(Subscription))
   {
   return m_ClientList.ToArray();
   }
   }
 
   /// This method is called by subscriber to register itself with Server.
   /// It register the client with pub/sub service. 
   public void Register( )
   {
   lock (typeof(Subscription))
   {
   IEvent subscriber = OperationContext.Current.GetCallbackChannel<IEvent>();
   if (m_ClientList.Contains(subscriber))
   {
   return;
   }
   m_ClientList.Add(subscriber);
}
   }

  
   /// This method is called by subscriber to Unsubscribe itself from Server.
   /// It Unsubscribe the client with pub/sub service.  
   public void UnRegister()
   {
   lock (typeof(Subscription))
   {
   IEvent subscriber = OperationContext.Current.GetCallbackChannel<IEvent>();
   m_ClientList.Remove(subscriber);
    }
   }

   public string GetDateTime()
   {
   return System.DateTime.Now.ToString();
   }

}

stores subscribers in a generic static list.

/// This the data structue that is used for pub/sub scheme. 
/// Here list is used to hold the subscribers. 


 
     static List<IEvent> m_ClientList;

The Register ( ) method extracts the callback reference from the operation call context. Then retrieves the list of subscribers for the event from the store. If the list does not contain the subscriber, it adds it in. UnRegister() operates in a similar manner.

Hosting

For hosting you have to expose Publishing service multiple times for multiple protocols.

   eventServiceHost = new ServiceHost(typeof(Publishing)); 

   ///Here diferent binding is created for different protocol.
   /// For example NetTcpBinding is created for TCP protocol.
   /// For example WSDualHttpBinding is created for HTTP protocol.
   // For example NetNamedPipeBinding is created for Named Pipe protocol.

   System.ServiceModel.Channels.Binding wsDualBindingpublish = new WSDualHttpBinding();
   System.ServiceModel.Channels.Binding tcpBindingpublish = new NetTcpBinding();
   System.ServiceModel.Channels.Binding namedPipeBindingpublish = new NetNamedPipeBinding();
      
///By the following line i add the address of PublishService to eventServiceHost for
///differnt protocol
                eventServiceHost.AddServiceEndpoint(typeof(IEvent), wsDualBindingpublish,
                                        "http://localhost:8000/PublishingService/");
                eventServiceHost.AddServiceEndpoint(typeof(IEvent), tcpBindingpublish,
                                        "net.tcp://localhost:8001/PublishingService");
                eventServiceHost.AddServiceEndpoint(typeof(IEvent), namedPipeBindingpublish,
                                        "net.pipe://localhost/MyPipe1");

//This line is used to open pub service to listen.
   eventServiceHost.Open();

For hosting you have to expose Subscription service multiple times for multiple protocols

subscriptionManagerHost = new ServiceHost(typeof(Subscription));

  System.ServiceModel.Channels.Binding wsDualBinding = new WSDualHttpBinding(
      WSDualHttpSecurityMode.None);
  System.ServiceModel.Channels.Binding tcpBinding = new NetTcpBinding(SecurityMode.None);
  System.ServiceModel.Channels.Binding namedPipeBinding = new NetNamedPipeBinding();


 ///By the following line i add the address of Subscription Service
 ///to subscriptionManagerHost for differnt protocol
 ///subscriptionManagerHost.AddServiceEndpoint(typeof(IRegistration), wsDualBinding,

                subscriptionManagerHost.AddServiceEndpoint(typeof(IRegistration),
                                       wsDualBinding,
                                       "http://localhost:8003/SubscriptionServie/");
               subscriptionManagerHost.AddServiceEndpoint(typeof(IRegistration),
                                   tcpBinding,
                                   "net.tcp://localhost:8002/SubscriptionServie");
               subscriptionManagerHost.AddServiceEndpoint(typeof(IRegistration),
                                       namedPipeBinding,
                                       "net.pipe://localhost/MyPipe2");



  //This line is used to open sub service to listen.
  subscriptionManagerHost.Open();

Http Client Connection:

We know that http is unidirectional. To make it bidirectional we have to use WSDualHttpBinding and have to open two sockets connection in client and server. so here ClientBaseAddress is used to set client,s address to listen in client.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <appSettings>
        <add key ="EndpointAddress" value ="http://localhost:8003/SubscriptionServie/"/>
    </appSettings>
 
</configuration>


   WSDualHttpBinding namedpipebinding = new WSDualHttpBinding(WSDualHttpSecurityMode.None);
   EndpointAddress endpointAddress = new EndpointAddress(EndpoindAddress);
   InstanceContext context = new InstanceContext(callbackinstance);
   m_Proxy = new RegistrationProxy(context, namedpipebinding, endpointAddress);  
   string strHostName = Dns.GetHostName();
   IPHostEntry ipEntry = Dns.GetHostByName(strHostName);
   IPAddress[] addr = ipEntry.AddressList;
   namedpipebinding.ClientBaseAddress = new Uri("http://" + 
           addr[0].ToString()    + ":" + "4000" + "/");

Tcp Client Connection:

SecurityMode.None is used here so that it can communicate over cross domain and over vpn.

<?xml version="1.0" encoding="utf-8" ?>
   <configuration>
   <appSettings>
   <add key ="EndpointAddress" value ="net.tcp://localhost:8002/SubscriptionServie"/>
   </appSettings>
   </configuration>
           NetTcpBinding NetTcpbinding = new NetTcpBinding(SecurityMode.None);
            EndpointAddress endpointAddress = new EndpointAddress(EndpoindAddress);
            InstanceContext context = new InstanceContext(callbackinstance);
            m_Proxy = new RegistrationProxy(context, NetTcpbinding, endpointAddress);

IPC Client Connection:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <appSettings>
           <add key ="EndpointAddress" value ="net.pipe://localhost/MyPipe2"/>
    </appSettings>    
</configuration>
           NetNamedPipeBinding namedpipebinding = new NetNamedPipeBinding();
            EndpointAddress endpointAddress = new EndpointAddress(EndpoindAddress);
            InstanceContext context = new InstanceContext(callbackinstance);
            m_Proxy = new RegistrationProxy(context, namedpipebinding, endpointAddress);

Http Publisher Connection:

We know that http is unidirectional. To make it bidirectional we have to use WSDualHttpBinding and have to open two sockets connection in client and server. so here ClientBaseAddress is used to set client,s address to listen in client.

WSDualHttpBinding wsDualBindingpublish = new WSDualHttpBinding();
EndpointAddress endpointAddress = 
  new EndpointAddress("http://localhost:8000/PublishingService/");
string strHostName = Dns.GetHostName();

  IPHostEntry ipEntry = Dns.GetHostByName(strHostName);

  IPAddress[] addr = ipEntry.AddressList;

  wsDualBindingpublish.ClientBaseAddress = new Uri("http://" + addr[0].ToString() 
  + ":" + "3000" + "/");
proxy = new PublisherProxy(wsDualBindingpublish, endpointAddress);

Conclusion

Here the code implement most simple scenario of pub/sub scheme without topic based which is helpful in many scenarios.

In the future post i will give fault tolerance implementation of this.

and then i will show how windows and certificate authentication can be implemented using this implementation.

Referance

What You Need To Know About One-Way Calls, Callbacks, And Events

Programming WCF Services - Written by Microsoft software legend Juval Lowy

wiki

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)