从c#

时间:2015-04-29 19:32:09

标签: javascript c# signalr

所以我再次提出了另一个SignalR问题,但这一次它有点具体。

我正在建立一个CMS,这次我真的全力以赴,理想情况下希望SignalR充当API。在过去的两年里,我构建了许多SignalR应用程序并且所有工作都令人惊讶,但这次我想避免通过JavaScript连接到HUB / Self主机。

一旦您查看了系统的体系结构(下图),我将进一步解释一个更小的东西

enter image description here

在审核了这个啧啧之后: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实例下。

希望这不是要消化并提前感谢你!

此致

2 个答案:

答案 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或更高版本:

enter image description here

然后在包管理器控制台中键入以下内容:

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服务的旧方法。