Search code examples
c#asp.netsignalrsignalr-hubsignalr.client

Storing the connection id of a specific client in SignalR hub


Below you can see a simplified version of my SignalR self hosted hub on a windows service:

public static class SubscriptionHandler
{
    public static int PriceFeedMembersCount = 0;
}

public class PriceHub : Hub
{
    public Task SubscribeToPriceFeed()
    {
        IHubContext context = GlobalHost.ConnectionManager.GetHubContext<PriceHub>();
        if (SubscriptionHandler.PriceFeedMembersCount == 0)
        {
            context.Clients.All.updatePriceSubscriptionStatus(true);
        }
        SubscriptionHandler.PriceFeedMembersCount++;
        return context.Groups.Add(Context.ConnectionId, "PriceFeed");
    }

    public Task UnsubscribeFromPriceFeed()
    {
        IHubContext context = GlobalHost.ConnectionManager.GetHubContext<PriceHub>();
        SubscriptionHandler.PriceFeedMembersCount--;
        if (SubscriptionHandler.PriceFeedMembersCount == 0)
        {
            context.Clients.All.updatePriceSubscriptionStatus(false);
        }
        return context.Groups.Remove(Context.ConnectionId, "PriceFeed");
    }

    public void NotifySubscribers(Price price)
    {
        IHubContext context = GlobalHost.ConnectionManager.GetHubContext<PriceHub>();
        context.Clients.Group("PriceFeed").updatePrice(price);
    }
}

And I have two types of clients for that hub: One of them is web applications and the other one is windows services. Here you can see a demo implementation for my windows service as a signalr client:

public partial class WinSer45 : ServiceBase
{
    private HubConnection hubConnection;
    private IHubProxy priceProxy;
    private Timer timer = new Timer();
    private bool hasSubscribers = false;

    public WinSer45()
    {
        InitializeComponent();
    }

    protected override void OnStart(string[] args)
    {
        timer.Interval = 1000; // saniyede bir
        timer.Elapsed += timer_Elapsed;
        timer.Enabled = true;

        hubConnection = new HubConnection("http://localhost:8080/signalr", useDefaultUrl: false);
        priceProxy = hubConnection.CreateHubProxy("PriceHub");
        hubConnection.Start().Wait();
        priceProxy.On<bool>("UpdatePriceSubscriptionStatus", hasSubscribers =>
        {
            this.hasSubscribers = hasSubscribers;
        });
    }

    void timer_Elapsed(object sender, ElapsedEventArgs e)
    {   
        if (hasSubscribers)
        {
            TestPrice testPrice = new TestPrice() { Id = 1, Buy = 1.2345, Sell = 9.8765, Symbol = "EURUSD" };
            priceProxy.Invoke("NotifySubscribers", testPrice).Wait();    
        }
    }   

    protected override void OnStop()
    {
    }
}

As you see I use the hasSubscribers flag to minimize the messages between hub and clients. And hasSubscribers flag is changed by SubscribeToPriceFeed and UnsubscribeFromPriceFeed methods.

If you look carefully you see the line below in SubscribeToPriceFeed:

context.Clients.All.updatePriceSubscriptionStatus(true);

I don't want to send the message to all clients but my client windows service. How can I store the connection Id of a specific client in my hub? If I can do that, I know I can send message to a specific connectionId as in the line below:

context.Clients.Client(connectionId).updatePriceSubscriptionStatus(true);

Thanks in advance,


Solution

  • pass source during connection

    like this

    hubConnection = new HubConnection("http://localhost:8080/signalr","source=windows",useDefaultUrl: false);
    

    HUB

    public override Task OnConnected()
    {
        var source= Context.QueryString['source'];
        return base.OnConnected();
    }
    

    create a class which will hold the user with source

    public class user {
      public string ConnectionID {set;get;}
      public string Source {set;get;}
    }
    

    declare a list in the hub

    List<user> userList=new List<user>();
    

    Then push the user during OnConnected

    public override Task OnConnected()
    {
        var us=new user();
        us.Source = Context.QueryString['source'];
        us.ConnectionID=Context.ConnectionId;
        userList.Add(us);
        return base.OnConnected();
    }
    

    and during broadcast just filter it by source

    var windowsUser=userList.Where(o=>o.Source == "windows").ToList(); // you'll get the windows user list