HTML5:来自 .NET 应用程序的实时推送通知






4.76/5 (32投票s)
用于 HTML5 JavaScript 和 .NET 之间发布-订阅场景的简单示例,使用 Websockets。
引言
下面的示例演示了 JavaScript 和 .NET 应用程序之间的发布-订阅通信场景。 HTML5 JavaScript 客户端需要订阅 .NET 应用程序中的一个或多个事件。 当事件发生时,.NET 应用程序会通知已订阅的客户端。
该通信通过 WebSockets(全双工单套接字连接)实现,因此它不使用 HTTP 协议情况下的轮询或长轮询机制。
当事件发生时,通知消息会立即从服务推送到所有已订阅的客户端。
示例代码
示例应用程序基于 Eneter.Messaging.Framework
,这是一个免费的轻量级跨平台框架,用于进程间通信,非常易于使用。
该示例还使用了 Chart.js library。 一个用于绘制图表的优秀的免费库。
运行示例
- 下载并解压此示例。
- 从 http://www.eneter.net/ProductDownload.htm 下载适用于 .NET 的 Eneter。
- 从 http://www.eneter.net/ProductDownload.htm 下载适用于 Javascript 的 Eneter。
- 将适用于 Javascript 的 Eneter 复制到 CpuUsageClient 目录中。
- 在编辑器中打开 index.html 文件,并确保它使用的 eneter-messaging 版本与您下载的版本相同。
- 在 Visual Studio 中打开示例项目,并将对您下载的
Eneter.Messaging.Framework.dll
的引用添加到项目中。 - 构建应用程序并执行它。
- 在互联网浏览器中打开 index.html(来自 CpuUsageClient 目录)。
- 按“打开连接”按钮,然后按“订阅”按钮。
- 网页开始获取通知并显示图表。
为了演示 JavaScript 和 .NET 之间的发布-订阅场景,下面的示例实现了一个简单的 .NET 控制台应用程序和一个简单的 HTML5 网页。
控制台应用程序定期检查 CPU 使用率并通知其值。
HTML5 网页订阅以接收 CPU 使用率的通知。 收到通知后,它会更新该值并显示图表。
使用双工代理
该示例的主要思想是使用 Eneter Messaging Framework 中的双工代理组件。 它提供了发送通知事件(发布)以及订阅所需事件的功能。
当代理收到通知消息时,它会将该消息转发给所有对此类通知感兴趣的订阅者。
Eneter 框架的跨平台方面确保了 JavaScript 和 .NET 之间的消息能够被理解(例如,它处理 UTF16 与 UTF8 或小端与大端)。
.NET 服务应用程序
服务应用程序是一个简单的 .NET 控制台应用程序,它定期检查 CPU 使用率。 然后,它使用代理组件发布该值。 代理搜索哪些客户端订阅了这种类型的事件,并将消息转发给它们。
此外,由于 JavaScript 使用 JSON 序列化程序,因此该服务也使用 JSON 序列化程序。 它还将双工代理设置为使用 JSON 序列化程序。
整个实现非常简单
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 客户端
JavaScript 客户端是一个简单的 HTML 5 网页,它使用双工代理客户端组件来订阅 CPU 使用率通知。 收到通知消息后,它会更新显示值的图表。 随着收到新的通知值,图表会移动。
整个实现非常简单
<!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>