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.
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:
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”.
How to receive event:
1. Click the “Subscribe” button of test client.
2. Then it will start to receive the events.
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:
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.
[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
{
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.
[ServiceContract(CallbackContract = typeof(IEvent))]
public interface IRegistration
{
[OperationContract]
void Register( );
[OperationContract]
void UnRegister();
[OperationContract]
string GetDateTime();
}
The callback interface is
[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,.
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
class Subscription : IRegistration
{
static List<IEvent> m_ClientList;
static Subscription()
{
m_ClientList = new List<IEvent>();
}
internal static IEvent[] GetClientList()
{
lock (typeof(Subscription))
{
return m_ClientList.ToArray();
}
}
public void Register( )
{
lock (typeof(Subscription))
{
IEvent subscriber = OperationContext.Current.GetCallbackChannel<IEvent>();
if (m_ClientList.Contains(subscriber))
{
return;
}
m_ClientList.Add(subscriber);
}
}
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.
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));
System.ServiceModel.Channels.Binding wsDualBindingpublish = new WSDualHttpBinding();
System.ServiceModel.Channels.Binding tcpBindingpublish = new NetTcpBinding();
System.ServiceModel.Channels.Binding namedPipeBindingpublish = new NetNamedPipeBinding();
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");
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();
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");
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