Click here to Skip to main content
15,881,709 members
Articles / Web Development / HTML

HTML5: Real-Time Push Notifications from .NET Application

Rate me:
Please Sign up or sign in to vote.
4.76/5 (32 votes)
28 Jun 2015CPOL3 min read 98.9K   3.4K   92   36
Simple example for publish-subscribe scenario between HTML5 JavaScript and .NET using Websockets.

Introduction

The example bellow demonstrates a publish-subscribe communication scenario between JavaScript and a .NET application. The HTML5 JavaScript client needs to subscribe for an event (or more events) in the .NET application. When the event occurs the .NET application notifies subscribed clients.

The communication is realized via WebSockets (full-duplex single socket connection) and therefore it does not use a polling or long-polling mechanism which would be used in case of the HTTP protocol.

Notification messages are pushed from the service to all subscribed clients instantly when the event occurs.

Example Code

The example application is based on <a href="http://www.eneter.net/ProductInfo.htm">Eneter.Messaging.Framework</a> the free lightweight cross-platform framework for the interprocess communication which is very easy to use.
The example also uses the Chart.js library. A nice free library for drawing charts.

To Run Example

  • Download and unzip this example.
  • Download Eneter for .NET from http://www.eneter.net/ProductDownload.htm.
  • Download Eneter for Javascript from http://www.eneter.net/ProductDownload.htm.
  • Copy Eneter for Javascript into CpuUsageClient directory.
  • Open index.html file in an editor and ensure it uses the same version of eneter-messaging as you downloaded.
  • Open the example project in Visual Studio and add the reference to Eneter.Messaging.Framework.dll that you downloaded.
  • Build the application and execute it.
  • Open index.html (from CpuUsageClient directory) in an internet browser.
  • Press 'Open Connection' button and then 'Subscribe' button.
  • Web-page starts to get notifications and the chart is displayed.

In order to demonstrate the publish-subscribe scenario between JavaScript and .NET the example bellow implements a simple .NET console application and a simple HTML5 web-page.
The console application regularly checks the CPU usage and notifies its value.
The HTML5 web-page subscribes to be notified about CPU usage. When it receives the notification it updates the value and displays the chart.

768473/PublishSubscribeBetweenJSNET.png

Using Duplex Broker

The main idea of the example is using the duplex broker component from Eneter Messaging Framework. It provides functionality for sending notification events (publishing) as well as for subscribing to desired events.
When the broker receives a notification message it forwards it all subscribers which are interested in this type of notification.
The cross-platform aspect of the Eneter framework ensures messages between JavaScript and .NET are understood (e.g. it takes care about UTF16 vs UTF8 or little-endian vs big-endian).

.NET Service Application

The service application is a simple .NET console application that regularly checks the CPU usage. Then it uses the broker component to publish the value. The broker searches which clients are subscribed for this type of event and forwards them the message.

Also because JavaScript uses the JSON serializer the service uses the JSON serializer too. It also sets the duplex broker to use the JSON serializer.

The whole implementation is very simple:

C#
using System;
using System.Diagnostics;
using System.Threading;
using Eneter.Messaging.DataProcessing.Serializing;
using Eneter.Messaging.MessagingSystems.MessagingSystemBase;
using Eneter.Messaging.MessagingSystems.WebSocketMessagingSystem;
using Eneter.Messaging.Nodes.Broker;

namespace CpuUsageService
{
    // Message that will be notified.
    public class CpuUpdateMessage
    {
        public float Usage { get; set; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            // JavaScript uses JSON serializer so set using JSON.
            ISerializer aSerializer = new DataContractJsonStringSerializer();

            // Create broker.
            IDuplexBrokerFactory aBrokerFactory = new DuplexBrokerFactory();
            IDuplexBroker aBroker = aBrokerFactory.CreateBroker();

            // Communicate using WebSockets.
            IMessagingSystemFactory aMessaging = new WebSocketMessagingSystemFactory();
            IDuplexInputChannel anInputChannel = 
               aMessaging.CreateDuplexInputChannel("ws://127.0.0.1:8843/CpuUsage/");

            anInputChannel.ResponseReceiverConnected += (x, y) =>
            {
                Console.WriteLine("Connected client: " + y.ResponseReceiverId);
            };
            anInputChannel.ResponseReceiverDisconnected += (x, y) =>
            {
                Console.WriteLine("Disconnected client: " + y.ResponseReceiverId);
            };

            // Attach input channel and start listeing.
            aBroker.AttachDuplexInputChannel(anInputChannel);

            // Start working thread monitoring the CPU usage.
            bool aStopWorkingThreadFlag = false;
            Thread aWorkingThread = new Thread(() =>
                {
                    PerformanceCounter aCpuCounter =
                      new PerformanceCounter("Processor", "% Processor Time", "_Total");

                    while (!aStopWorkingThreadFlag)
                    {
                        CpuUpdateMessage aMessage = new CpuUpdateMessage();
                        aMessage.Usage = aCpuCounter.NextValue();

                        //Console.WriteLine(aMessage.Usage);

                        // Serialize the message.
                        object aSerializedMessage =
                           aSerializer.Serialize<CpuUpdateMessage>(aMessage);

                        // Notify subscribers via the broker.
                        // Note: The broker will forward the message to subscribed clients.
                        aBroker.SendMessage("MyCpuUpdate", aSerializedMessage);

                        Thread.Sleep(500);
                    }
                });
            aWorkingThread.Start();

            Console.WriteLine("CpuUsageService is running press ENTER to stop.");
            Console.ReadLine();

            // Wait until the working thread stops.
            aStopWorkingThreadFlag = true;
            aWorkingThread.Join(3000);

            // Detach the input channel and stop listening.
            aBroker.DetachDuplexInputChannel();
        }
    }
}

JavaScript Client

The JavaScript client is a simple HTML 5 web-page using the duplex broker client component for subscribing to CPU usage notifications. When the notification message is received it updates the chart displaying values. The chart is moving as new notification values are received.

The whole implementation is very simple:

HTML
<!DOCTYPE html>
<html>
    <head>
        <title>CPU Usage Client</title>
        
        <script src="Chart.js"></script>

        <!- ENSURE HERE IS THE SAME ENETER VERSION AS YOU DOWNLOADED -->
        <script src="eneter-messaging-6.5.0.js"></script>

        <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    </head>
    <body onunload="closeConnection();">
        <div>
            <button type="button" onclick="openConnection();">1. Open Connection</button>
        </div>
        <div>
            <button type="button" onclick="subscribe();">2. Subscribe</button>
            <button type="button" onclick="unsubscribe();">3. Unsubscribe</button>
        </div>
        <div>
            <button type="button" onclick="closeConnection();">4. Close Connection</button>
        </div>
        <div>
            <canvas id="canvas" height="300" width="300"></canvas>
        </div>
        
        <script>
            // Initialize chart.
            var myChartConfig = {
                        animation : false,
                        scaleOverlay : true,
                        scaleOverride : true,
                    scaleSteps : 10,
                        scaleStepWidth : 10,
                        scaleStartValue : 0
            };
            var myLineChartData = {
            labels : ["", "", "", "", "", "", "", "", "", ""],
            datasets : [
                {
                    fillColor : "rgba(220,220,220,0.5)",
                    strokeColor : "rgba(220,220,220,1)",
                    pointColor : "rgba(220,220,220,1)",
                    pointStrokeColor : "#fff",
                    data : [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
                }
            ]
            
        };
            var myChart = new Chart(document.getElementById("canvas").getContext("2d"));
            
            // Create the duplex output channel.
            var myOutputChannel =
              new WebSocketDuplexOutputChannel("ws://127.0.0.1:8843/CpuUsage/", null);

            // Create BrokerClient
            var myBrokerClient = new DuplexBrokerClient();
            
            // Subscribe to notifications.
            myBrokerClient.onBrokerMessageReceived = onBrokerMessageReceived;
            
            function openConnection() {
                // Attach output channel and be able to send messages and receive responses.
                myBrokerClient.attachDuplexOutputChannel(myOutputChannel);
            };

            function closeConnection() {
                // Detach output channel and stop listening to responses.
                myBrokerClient.detachDuplexOutputChannel();
            };

            function subscribe() {
                myBrokerClient.subscribe("MyCpuUpdate");
            };

            function unsubscribe() {
                myBrokerClient.unsubscribe("MyCpuUpdate");
            };
            
            function onBrokerMessageReceived(brokerMessageReceivedEventArgs) {
                // If the notified message is the CPU status update.
                if (brokerMessageReceivedEventArgs.MessageTypeId === "MyCpuUpdate")
                {
                    // Deserialize notified message content.
                    var aValue = JSON.parse(brokerMessageReceivedEventArgs.Message);
                    
                    // Update data and draw the chart.
                    myLineChartData.datasets[0].data.shift();
                    myLineChartData.datasets[0].data.push(aValue.Usage);
                    myChart.Line(myLineChartData, myChartConfig);
                }
            };
        </script>
    </body>
</html><font color="#111111" face="Segoe UI, Arial, sans-serif"><span style="font-size: 14px; white-space: normal;">
</span></font>

License

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


Written By
Architect
Slovakia Slovakia
My programming path started in 1987 when I got my first computer Sharp MZ-800.
It came with 8 bit CPU Z80, 64Kb RAM and the tape recorder. It was a great machine. I think I still have it somewhere.
I was fascinated and I started to write first programs. Eventually I became developer and software architect. I like innovations and clean nice solutions.

Comments and Discussions

 
Suggestionnotes Pin
Alrikabi_13-May-16 6:23
Alrikabi_13-May-16 6:23 
QuestionI don't understand this kindly elaborate Pin
Ayesha_K4-Nov-15 7:26
Ayesha_K4-Nov-15 7:26 
AnswerRe: I don't understand this kindly elaborate Pin
Ondrej_Uzovic4-Nov-15 7:50
Ondrej_Uzovic4-Nov-15 7:50 
When you download Eneter for Javascript the name of the library file contain the version number.
E.g. the current version is Eneter 6.5.0 so the file name is: eneter-messaging-6.5.0.js.

When e.g. Eneter 7.0.0 is released the file name of the library will be eneter-messaging-7.0.0.js.

Therefore the comment in the html file means not to forget to update the library name according to the version you have downloaded.

GeneralRe: I don't understand this kindly elaborate Pin
Ayesha_K4-Nov-15 10:05
Ayesha_K4-Nov-15 10:05 
GeneralRe: I don't understand this kindly elaborate Pin
Ondrej_Uzovic4-Nov-15 19:37
Ondrej_Uzovic4-Nov-15 19:37 
GeneralRe: I don't understand this kindly elaborate Pin
Ayesha_K5-Nov-15 4:11
Ayesha_K5-Nov-15 4:11 
GeneralRe: I don't understand this kindly elaborate Pin
Ondrej_Uzovic5-Nov-15 11:06
Ondrej_Uzovic5-Nov-15 11:06 
QuestionI'm unable to open it Pin
Ayesha_K4-Nov-15 6:26
Ayesha_K4-Nov-15 6:26 
AnswerRe: I'm unable to open it Pin
Ondrej_Uzovic4-Nov-15 7:56
Ondrej_Uzovic4-Nov-15 7:56 
GeneralRe: I'm unable to open it Pin
Ayesha_K4-Nov-15 10:30
Ayesha_K4-Nov-15 10:30 
QuestionEneter.Messaging.Framework.dll not found Pin
Jusharra30-Jun-15 18:21
Jusharra30-Jun-15 18:21 
AnswerRe: Eneter.Messaging.Framework.dll not found Pin
Ondrej_Uzovic30-Jun-15 19:01
Ondrej_Uzovic30-Jun-15 19:01 
GeneralRe: Eneter.Messaging.Framework.dll not found Pin
Jusharra1-Jul-15 3:25
Jusharra1-Jul-15 3:25 
GeneralMy vote of 5 Pin
Prasad Khandekar29-Jun-15 4:43
professionalPrasad Khandekar29-Jun-15 4:43 
GeneralRe: My vote of 5 Pin
Ayesha_K4-Nov-15 7:47
Ayesha_K4-Nov-15 7:47 
Questionwhat are prerequisites to run this application Pin
sha_server5-Jun-15 20:42
sha_server5-Jun-15 20:42 
AnswerRe: what are prerequisites to run this application Pin
Ondrej_Uzovic8-Jun-15 5:44
Ondrej_Uzovic8-Jun-15 5:44 
BugI found Error.Please help me. Pin
manan0shah24-May-15 19:36
manan0shah24-May-15 19:36 
GeneralRe: I found Error.Please help me. Pin
Ondrej_Uzovic26-May-15 9:08
Ondrej_Uzovic26-May-15 9:08 
QuestionHow to config IIS Pin
Mekki Ahmedi7-May-14 9:36
Mekki Ahmedi7-May-14 9:36 
AnswerRe: How to config IIS Pin
Mekki Ahmedi7-May-14 10:07
Mekki Ahmedi7-May-14 10:07 
GeneralRe: How to config IIS Pin
Ondrej_Uzovic7-May-14 10:43
Ondrej_Uzovic7-May-14 10:43 
GeneralVery nice Pin
Mekki Ahmedi7-May-14 8:32
Mekki Ahmedi7-May-14 8:32 
GeneralRe: Very nice Pin
Ondrej_Uzovic7-May-14 9:27
Ondrej_Uzovic7-May-14 9:27 
GeneralRe: Very nice Pin
Mekki Ahmedi7-May-14 10:07
Mekki Ahmedi7-May-14 10:07 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.