所以我再次提出了另一个SignalR问题,但这一次它有点具体。
我正在建立一个CMS,这次我真的全力以赴,理想情况下希望SignalR充当API。在过去的两年里,我构建了许多SignalR应用程序并且所有工作都令人惊讶,但这次我想避免通过JavaScript连接到HUB / Self主机。
一旦您查看了系统的体系结构(下图),我将进一步解释一个更小的东西
在审核了这个啧啧之后:http://www.asp.net/signalr/overview/deployment/tutorial-signalr-self-host
它解释了如何设置自我主机,最重要的是如何访问集线器IE:
$.connection.hub.url = "http://localhost:8080/signalr";
// Declare a proxy to reference the hub.
var chat = $.connection.myHub;
基本上我想废弃它并避免在JavaScript中创建连接。是否可以从外部应用程序连接到C#中的集线器?请注意,所有应用都将位于同一个IIS实例下。
希望这不是要消化并提前感谢你!
此致
答案 0 :(得分:0)
好的,我迟到了,但它可能对其他人有用:
集线器:
public class MyHub : Hub
{
private static MyHub _instance;
public static MyHub GetInstance()
{
return _instance;
}
public MyHub()
{
_instance = this;
}
}
您需要的地方:
var hub = MyHub.GetInstance();
我不想使用静态变量来展示集线器,但是没有找到更好的方法来实现这一点。
答案 1 :(得分:0)
是的,您可以通过创建一个自托管的SignalR集线器来做到这一点,而不是像通常通过IIS那样托管SignalR。使用自托管解决方案,集线器和客户端都可以完全使用C#。
在此说明中,我的集线器将是Windows服务,但您也可以使用WPF桌面,Winforms桌面或控制台应用程序。
首先在Visual Studio中创建Windows服务,确保您使用的是.NET Framework 4.5或更高版本:
然后在包管理器控制台中键入以下内容:
PM> Install-Package Microsoft.AspNet.SignalR.SelfHost
PM> Install-Package ServiceProcess.Helpers
PM> Install-Package Microsoft.Owin.Cors
请注意,后者Microsoft.Owin.Cors
是跨域支持所必需的。接下来,将其添加到您的app.config文件中:
<runtime>
<loadFromRemoteSources enabled="true" />
</runtime>
然后将其添加到您的Program.cs
文件中:
using ServiceProcess.Helpers;
using System;
using System.Collections.Generic;
using System.Data;
using System.ServiceProcess;
namespace SignalRBroadcastServiceSample
{
static class Program
{
private static readonly List<ServiceBase> _servicesToRun = new List<ServiceBase>();
/// <summary>
/// The main entry point for the application.
/// </summary>
static void Main()
{
_servicesToRun.Add(CurrencyExchangeService.Instance);
if (Environment.UserInteractive)
{
_servicesToRun.ToArray().LoadServices();
}
else
{
ServiceBase.Run(_servicesToRun.ToArray());
}
}
}
}
接下来,添加一个将成为域的类库。现在,将包含此内容的Startup类添加到您的服务项目中:
使用系统; 使用System.Threading.Tasks; 使用Microsoft.Owin; 使用Owin;
[程序集:OwinStartup(typeof(SignalRBroadcastServiceSample.Startup))]
命名空间SignalRBroadcastServiceSample { 公共类创业 { 公共无效配置(IAppBuilder应用) { //在此处分支管道,以“ / signalr”开头的请求 app.Map(“ / signalr”,地图=> { //将CORS中间件设置为在SignalR之前运行。 //默认情况下,这将允许所有来源。您可以 //通过以下方式配置来源和/或http动词集 //为cors选项提供不同的政策。 map.UseCors(CorsOptions.AllowAll); var hubConfiguration =新的HubConfiguration { //您可以通过取消注释下面的行来启用JSONP。 // JSONP请求不安全,但是某些旧版浏览器(和某些 // IE版本)需要JSONP才能跨域工作 // EnableJSONP = true EnableDetailedErrors = true, EnableJSONP = true }; //运行SignalR管道。我们没有使用MapSignalR //,因为此分支已经在“ / signalr”下运行 //路径。 map.RunSignalR(hubConfiguration); }); } } }
否将此添加到您的域项目中:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SignalrDomain
{
public class Currency
{
private decimal _usdValue;
public string CurrencySign { get; set; }
public decimal Open { get; private set; }
public decimal Low { get; private set; }
public decimal High { get; private set; }
public decimal LastChange { get; private set; }
public decimal RateChange
{
get
{
return USDValue - Open;
}
}
public double PercentChange
{
get
{
return (double)Math.Round(RateChange / USDValue, 4);
}
}
public decimal USDValue
{
get
{
return _usdValue;
}
set
{
if (_usdValue == value)
{
return;
}
LastChange = value - _usdValue;
_usdValue = value;
if (Open == 0)
{
Open = _usdValue;
}
if (_usdValue < Low || Low == 0)
{
Low = _usdValue;
}
if (_usdValue > High)
{
High = _usdValue;
}
}
}
}
}
最后在此处创建SignalR
中心,其中WebApp.Start
调用是
SignalR self-hosting:
using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Hubs;
using Microsoft.Owin;
using SignalrDomain;
using System;
using System.Collections.Generic;
using System.Linq;
namespace SignalRBroadcastServiceSample
{
public class CurrencyExchangeHub : Hub
{
private readonly CurrencyExchangeService _currencyExchangeHub;
public CurrencyExchangeHub() :
this(CurrencyExchangeService.Instance)
{
}
public CurrencyExchangeHub(CurrencyExchangeService currencyExchange)
{
_currencyExchangeHub = currencyExchange;
}
public IEnumerable<Currency> GetAllCurrencies()
{
return _currencyExchangeHub.GetAllCurrencies();
}
public string GetMarketState()
{
return _currencyExchangeHub.MarketState.ToString();
}
public bool OpenMarket()
{
_currencyExchangeHub.OpenMarket();
return true;
}
public bool CloseMarket()
{
_currencyExchangeHub.CloseMarket();
return true;
}
public bool Reset()
{
_currencyExchangeHub.Reset();
return true;
}
}
}
还将其添加到您的CurrencyExchangeService.cs
文件中:
using Microsoft.AspNet.SignalR;
using Microsoft.AspNet.SignalR.Hubs;
using Microsoft.Owin.Hosting;
using SignalrDomain;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ServiceProcess;
using System.Threading;
namespace SignalRBroadcastServiceSample
{
public partial class CurrencyExchangeService : ServiceBase
{
private Thread mainThread;
private bool isRunning = true;
private Random random = new Random();
protected override void OnStart(string[] args)
{
WebApp.Start("http://localhost:8083"); // Must be
//@"http://+:8083" if you want to connect from other computers
LoadDefaultCurrencies();
// Start main thread
mainThread = new Thread(new ParameterizedThreadStart(this.RunService));
mainThread.Start(DateTime.MaxValue);
}
protected override void OnStop()
{
mainThread.Join();
}
public void RunService(object timeToComplete)
{
DateTime dtTimeToComplete = timeToComplete != null ?
Convert.ToDateTime(timeToComplete) : DateTime.MaxValue;
while (isRunning && DateTime.UtcNow < dtTimeToComplete)
{
Thread.Sleep(15000);
NotifyAllClients();
}
}
// This line is necessary to perform the broadcasting to all clients
private void NotifyAllClients()
{
Currency currency = new Currency();
currency.CurrencySign = "CAD";
currency.USDValue = random.Next();
BroadcastCurrencyRate(currency);
Clients.All.NotifyChange(currency);
}
#region "SignalR code"
// Singleton instance
private readonly static Lazy<CurrencyExchangeService>
_instance = new Lazy<CurrencyExchangeService>(
() => new CurrencyExchangeService
(GlobalHost.ConnectionManager.GetHubContext<CurrencyExchangeHub>().Clients));
private readonly object _marketStateLock = new object();
private readonly object _updateCurrencyRatesLock = new object();
private readonly ConcurrentDictionary<string,
Currency> _currencies = new ConcurrentDictionary<string, Currency>();
// Currency can go up or down by a percentage of this factor on each change
private readonly double _rangePercent = 0.002;
private readonly TimeSpan _updateInterval = TimeSpan.FromMilliseconds(250);
public TimeSpan UpdateInterval
{
get { return _updateInterval; }
}
private readonly Random _updateOrNotRandom = new Random();
private Timer _timer;
private volatile bool _updatingCurrencyRates;
private volatile MarketState _marketState;
public CurrencyExchangeService(IHubConnectionContext<dynamic> clients)
{
InitializeComponent();
Clients = clients;
}
public static CurrencyExchangeService Instance
{
get
{
return _instance.Value;
}
}
private IHubConnectionContext<dynamic> Clients
{
get;
set;
}
public MarketState MarketState
{
get { return _marketState; }
private set { _marketState = value; }
}
public IEnumerable<Currency> GetAllCurrencies()
{
return _currencies.Values;
}
public bool OpenMarket()
{
bool returnCode = false;
lock (_marketStateLock)
{
if (MarketState != MarketState.Open)
{
_timer = new Timer(UpdateCurrencyRates, null, _updateInterval, _updateInterval);
MarketState = MarketState.Open;
BroadcastMarketStateChange(MarketState.Open);
}
}
returnCode = true;
return returnCode;
}
public bool CloseMarket()
{
bool returnCode = false;
lock (_marketStateLock)
{
if (MarketState == MarketState.Open)
{
if (_timer != null)
{
_timer.Dispose();
}
MarketState = MarketState.Closed;
BroadcastMarketStateChange(MarketState.Closed);
}
}
returnCode = true;
return returnCode;
}
public bool Reset()
{
bool returnCode = false;
lock (_marketStateLock)
{
if (MarketState != MarketState.Closed)
{
throw new InvalidOperationException
("Market must be closed before it can be reset.");
}
LoadDefaultCurrencies();
BroadcastMarketReset();
}
returnCode = true;
return returnCode;
}
private void LoadDefaultCurrencies()
{
_currencies.Clear();
var currencies = new List<Currency>
{
new Currency { CurrencySign = "USD", USDValue = 1.00m },
new Currency { CurrencySign = "CAD", USDValue = 0.85m },
new Currency { CurrencySign = "EUR", USDValue = 1.25m }
};
currencies.ForEach(currency => _currencies.TryAdd(currency.CurrencySign, currency));
}
private void UpdateCurrencyRates(object state)
{
// This function must be re-entrant as it's running as a timer interval handler
lock (_updateCurrencyRatesLock)
{
if (!_updatingCurrencyRates)
{
_updatingCurrencyRates = true;
foreach (var currency in _currencies.Values)
{
if (TryUpdateCurrencyRate(currency))
{
BroadcastCurrencyRate(currency);
}
}
_updatingCurrencyRates = false;
}
}
}
private bool TryUpdateCurrencyRate(Currency currency)
{
// Randomly choose whether to update this currency or not
var r = _updateOrNotRandom.NextDouble();
if (r > 0.1)
{
return false;
}
// Update the currency price by a random factor of the range percent
var random = new Random((int)Math.Floor(currency.USDValue));
var percentChange = random.NextDouble() * _rangePercent;
var pos = random.NextDouble() > 0.51;
var change = Math.Round(currency.USDValue * (decimal)percentChange, 2);
change = pos ? change : -change;
currency.USDValue += change;
return true;
}
private void BroadcastMarketStateChange(MarketState marketState)
{
switch (marketState)
{
case MarketState.Open:
Clients.All.marketOpened();
break;
case MarketState.Closed:
Clients.All.marketClosed();
break;
default:
break;
}
}
private void BroadcastMarketReset()
{
Clients.All.marketReset();
}
private void BroadcastCurrencyRate(Currency currency)
{
Clients.All.updateCurrencyRate(currency);
}
}
public enum MarketState
{
Closed,
Open
}
#endregion
}
下一步,在您的客户端控制台应用程序中,添加以下NuGet软件包:
PM> Install-Package Microsoft.AspNet.SignalR.Client
还要添加一个CommmunicationHandler
类:
using Microsoft.AspNet.SignalR.Client;
using SignalrDomain;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Client
{
public static class CommunicationHandler
{
public static string ExecuteMethod(string method, string args, string serverUri, string hubName)
{
var hubConnection = new HubConnection("http://localhost:8083");
IHubProxy currencyExchangeHubProxy = hubConnection.CreateHubProxy("CurrencyExchangeHub");
// This line is necessary to subscribe for broadcasting messages
currencyExchangeHubProxy.On<Currency>("NotifyChange", HandleNotify);
// Start the connection
hubConnection.Start().Wait();
var result = currencyExchangeHubProxy.Invoke<string>(method).Result;
return result;
}
private static void HandleNotify(Currency currency)
{
Console.WriteLine("Currency " + currency.CurrencySign + ", Rate = " + currency.USDValue);
}
}
}
这是客户端中的Program类:
using System;
using System.Diagnostics;
using System.Net;
namespace Client
{
class Program
{
static void Main(string[] args)
{
var state = CommunicationHandler.ExecuteMethod("GetMarketState",
"", IPAddress.Any.ToString(), "CurrencyExchangeHub");
Console.WriteLine("Market State is " + state);
if (state == "Closed")
{
var returnCode = CommunicationHandler.ExecuteMethod
("OpenMarket", "", IPAddress.Any.ToString(), "CurrencyExchangeHub");
Debug.Assert(returnCode == "True");
Console.WriteLine("Market State is Open");
}
Console.ReadLine();
}
}
}
现在运行该服务并启动它,然后在运行控制台客户端应用程序时,您应该会看到货币汇率会定期更新。
原始文章在这里:https://www.codeproject.com/Articles/892634/Using-SignalR-Broadcasting-and-Notifications-with
您可以在此处下载完整的源代码:http://www.sandbox.ca/~rmoore/export/codeproject/CodeProjectSelfHostedBroadcastServiceSignalRSample.zip
此外,您可以使用有用的Topshelf NuGet软件包更新应用程序:https://github.com/Topshelf/Topshelf
,而不是使用上面显示的托管和安装Windows服务的旧方法。