Home > Software design >  How can I send SignalR message to a single client?
How can I send SignalR message to a single client?

Time:11-06

For the life of me, I can't figure out how to send the SignalR message only to the client that hit the "Go" button, and no one else. It all works when I send it to "All", but I can't figure out how to get the connectionID on the server, and then just send the message to the initiating client. This is what I have working so far. I'd appreciate it if you could tell me how to make this work with just the calling client -

This is my Default.aspx file

<%@ Page Title="Home Page" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="SignalR5._Default" %>

<asp:Content ID="BodyContent" ContentPlaceHolderID="MainContent" runat="server">

    <asp:UpdatePanel ID="UpdatePanel1" runat="server">
        <ContentTemplate>
            <div><br /><br />
                <asp:Button ID="btnGo" runat="server" Text="GO!!!" OnClick="btnGo_Click" OnClientClick="this.disabled=true;" UseSubmitBehavior="false" /><br /><br />
                <asp:Label ID="lblLog" class="#log" runat="server" Text="Begin"></asp:Label> <br /> <br />
                <asp:Label ID="lblConnectionID" runat="server" Text=""></asp:Label>
            </div>
        </ContentTemplate>
    </asp:UpdatePanel>

    <script src="Scripts/jquery-3.4.1.js"></script>
    <script src="Scripts/jquery.signalR-2.4.2.js"></script>
    <script src="signalr/hubs"></script>

    <script type="text/javascript">
        $(function() {   

            var logger = $.connection.logHub;

            logger.client.logMessage = function(msg) {

                $("#MainContent_lblLog").html(msg);
            };

            $.connection.hub.start().done(function () {
                var cid = $.connection.hub.id;
                $("#MainContent_lblConnectionID").text("ConnectionID: "   cid);
                //alert("connection Id:"   cid);
            });
        });

    </script>
</asp:Content>

This is my Default.aspx.cs file

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Threading;
using System.Diagnostics;

namespace SignalR5
{
    public partial class _Default : Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {

        }

        protected void btnGo_Click(object sender, EventArgs e)
        {
            //The timer is just to display how long each task takes.
            Stopwatch stopWatch = new Stopwatch();
            stopWatch.Start();
            
            string strMessage = "Hello ";
            LogHub.SendMessage(strMessage);
            Thread.Sleep(2000);
            strMessage  = "("   stopWatch.Elapsed.Seconds   " Seconds)<br>what's up bro? ";

            stopWatch.Restart();
            LogHub.SendMessage(strMessage);
            Thread.Sleep(2500);
            strMessage  = "("   stopWatch.Elapsed.Seconds   " Seconds)<br>Call me soon ";

            stopWatch.Restart();
            LogHub.SendMessage(strMessage);
            Thread.Sleep(3200);
            strMessage  = "("   stopWatch.Elapsed.Seconds   " Seconds)<br>See you later ";

            stopWatch.Restart();
            LogHub.SendMessage(strMessage);
            Thread.Sleep(1800);
            strMessage  = "("   stopWatch.Elapsed.Seconds   " Seconds)<br>Okay man. See you ";

            stopWatch.Restart();
            LogHub.SendMessage(strMessage);
            Thread.Sleep(2900);
            strMessage  = "("   stopWatch.Elapsed.Seconds   " Seconds)<br>We're all Done!";
            lblLog.Text = strMessage;
            
            stopWatch.Stop();
        }
    }
}

This is my Startup1.cs file

using Microsoft.Owin;
using Owin;
using System;
using System.Threading.Tasks;

[assembly: OwinStartup(typeof(SignalR5.Startup1))]

namespace SignalR5
{
    public class Startup1
    {
        public void Configuration(IAppBuilder app)
        {
            app.MapSignalR();
        }
    }
}

And this is my LogHub.cs file

using Microsoft.AspNet.SignalR;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace SignalR5
{
    public class LogHub : Hub
    {
        // This is the only one working, but I don't want to send to all clients. 
        public static void SendMessage(string strMessage)
        {
            var hub = GlobalHost.ConnectionManager.GetHubContext("LogHub");
            hub.Clients.All.logMessage(strMessage);
   
        }
    }
}

As I mentioned, I'm having a real hard time figuring out how to just send this to the "caller" client. It works fine for "All".

CodePudding user response:

Each client connecting to a SignalR hub has a unique connection id. We can retrieve this using the Context.ConnectionId property of the hub context. Using this, we can send messages just to that particular client. Learn more about this here.

So for sending messages to a specific client, you're gonna need their connection Id to identify which client you are going to send the message to.

I have made a demo here for sending messages to a specific user. This is the hub class. This send method will send messages to all connected clients like usual, but the extra thing I did is, register their connection id and user name at the beginning so that I can access them later.

public static class UserHandler
    {
        // just remember when you will restart the app, object will get reset
        public static HashSet<string> ConnectedIds = new HashSet<string>();
        public static HashSet<string> ConnectedUsers = new HashSet<string>();
    }

 public void Send(string name, string message)
        {
            // registering the users
            UserHandler.ConnectedIds.Add(Context.ConnectionId);
            UserHandler.ConnectedUsers.Add(name);

            // Call the broadcastMessage method to update clients.
            Clients.All.broadcastMessage(name, message);
        }

This SendToSingleUser method is used for sending messages to a specific user, as for the dummy purpose I am just randomly taking the first client from the list. If you already know to which user you are going to send it, then you can pass the id to the method and look for the Id in your connection list just to make sure it's a valid connection.

public void SendToSingleUser(string userName, string message)
        {

            string fromUserId = Context.ConnectionId;
            //removing the currrent user ID & name
            UserHandler.ConnectedIds.Remove(fromUserId);
            UserHandler.ConnectedUsers.Remove(userName);

            //taking a random user for sending message
            string toUserId = UserHandler.ConnectedIds.First();
            string toUserName = UserHandler.ConnectedUsers.First();

            if (toUserId != null && fromUserId != null)
            {
                // send to 
                Clients.Client(toUserId).sendPrivateMessage(userName, message);

                // send to caller user
                Clients.Caller.sendPrivateMessage(userName, message);
            }

        } 

The aspx file:

    <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Chat.aspx.cs" Inherits="SignalRChat.Chat" %>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>SignalR Chat : Chat Page</title>

    <link href="Content/bootstrap.css" rel="stylesheet" />
    <link href="Content/style.css" rel="stylesheet" />
    <link href="Content/font-awesome.css" rel="stylesheet" />

    <script src="Scripts/jQuery-1.9.1.min.js"></script>
    <script src="Scripts/jquery.signalR-2.2.2.min.js"></script>
    <script src="Scripts/date.format.js"></script>
    <!--Reference the autogenerated SignalR hub script. -->
    <script src="signalr/hubs"></script>


    <script type="text/javascript">

        $(function () {

            // Declare a proxy to reference the hub. 
            var chat = $.connection.chatHub;
            // Create a function that the hub can call to broadcast messages.
            chat.client.broadcastMessage = function (name, message) {
                // Html encode display name and message. 
                var encodedName = $('<div />').text(name).html();
                var encodedMsg = $('<div />').text(message).html();
                // Add the message to the page. 
                $('#discussion').append('<li><strong>'   encodedName
                      '</strong>:&nbsp;&nbsp;'   encodedMsg   '</li>');
            };

            // Create a function that the hub can call to broadcast private messages
            chat.client.sendPrivateMessage = function (name, message) {
                // Html encode display name and message. 
                var encodedName = $('<div />').text(name).html();
                var encodedMsg = $('<div />').text(message).html();
                // Add the message to the page. 
                $('#discussion').append('<li><strong>'   encodedName
                      '</strong>:&nbsp;&nbsp;'   encodedMsg   '</li>');
            };


            // Get the user name and store it to prepend to messages.
            $('#displayname').val(prompt('Enter your name:', ''));
            $('#CurrentUser').append($('#displayname').val());
            // Set initial focus to message input box.  
            $('#message').focus();
            // Start the connection.
            $.connection.hub.start().done(function () {

                //send to all user
                $('#sendmessage').click(function () {

                    var msg = $("#message").val();

                    if (msg.length > 0) {
                        debugger;
                        var userName = $('#displayname').val();
                        chat.server.send(userName, msg);
                        $('#message').val('').focus();
                    }
                });

                //send to a sepcific user
                $('#sendToSingleUser').click(function () {

                    var msg = $("#message").val();

                    if (msg.length > 0) {
                        debugger;
                        var userName = $('#displayname').val();
                        chat.server.sendToSingleUser(userName, msg);
                        $('#message').val('').focus();
                    }
                });
            });
        });

    </script>

</head>
<body>
    <form id="form1" runat="server">
        <asp:ScriptManager ID="ScriptManager1" runat="server" ></asp:ScriptManager>
        <div class="content-wrapper">
            <div class="row">
                 <div class="container">
                    <label id="CurrentUser"> </label>
                    <input type="text" id="message" />
                    <input type="button" id="sendmessage" value="Send" />
                    <input type="button" id="sendToSingleUser" value="Send Single User" />
                    <input type="hidden" id="displayname" />
                    <ul id="discussion">
                    </ul>
                </div>
            </div>
        </div>

         <script src="Scripts/bootstrap.min.js"></script>

    </form>
</body>
</html>

The full demo will be found here

Note: This demo is different than the example that you have posted, I just wanted to gave you an idea how you can achieve the communication.

CodePudding user response:

When invoked your "Go" button you should invoke the "Go" method in the server, and then you just can send data to the Caller like:

await hub.Clients.Caller.logMessage(message);
  • Related