[英]Connect to SignalR self host hub from another app in c#
所以我再次遇到另一个SignalR问题,但是这次更加具体了。
我正在构建CMS,这次我真的投入了很多精力,理想情况下希望SignalR充当API。 在过去的两年中,我已经构建了许多SignalR应用程序,并且所有应用程序都表现出色,但是这次我想避免通过JavaScript连接到HUB / Self主机。
在您查看了系统的体系结构之后,我将进一步解释(下图)
在审查此Tut之后: 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实例下。
希望这不要太多消化,并在此先感谢您!
问候,
好的,我来晚了,但是对其他人可能有用:
枢纽:
public class MyHub : Hub
{
private static MyHub _instance;
public static MyHub GetInstance()
{
return _instance;
}
public MyHub()
{
_instance = this;
}
}
在您需要的地方:
var hub = MyHub.GetInstance();
我不喜欢使用静态变量来公开集线器,但没有找到实现此目的的更好方法。
是的,您可以通过创建一个自托管的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
类添加到您的服务项目中:
using System;
using System.Threading.Tasks;
using Microsoft.Owin;
using Owin;
[assembly: OwinStartup(typeof(SignalRBroadcastServiceSample.Startup))]
namespace SignalRBroadcastServiceSample
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
// Branch the pipeline here for requests that start with "/signalr"
app.Map("/signalr", map =>
{
// Setup the CORS middleware to run before SignalR.
// By default this will allow all origins. You can
// configure the set of origins and/or http verbs by
// providing a cors options with a different policy.
map.UseCors(CorsOptions.AllowAll);
var hubConfiguration = new HubConfiguration
{
// You can enable JSONP by uncommenting line below.
// JSONP requests are insecure but some older browsers (and some
// versions of IE) require JSONP to work cross domain
// EnableJSONP = true
EnableDetailedErrors = true,
EnableJSONP = true
};
// Run the SignalR pipeline. We're not using MapSignalR
// since this branch already runs under the "/signalr"
// path.
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;
}
}
}
}
}
最后在此处创建Web应用程序WebApp.Start
调用是SignalR自托管的关键部分的SignalR集线器:
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软件包来更新应用程序,而不是使用上面显示的旧的托管和安装Windows服务的旧方法: https : //github.com/Topshelf/Topshelf
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.