Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / HTML

HTML5: Real-Time Push Notifications from .NET Application

4.76/5 (32 votes)
28 Jun 2015CPOL3 min read 102.3K   3.4K  
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)