Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When is the connection closed with SignalR from browser

I'm making a little chat application with SignalR 2.0 (just like everyone).

I have a win8.1 application and when the application is closed, the hub receives the OnDisconnected event and removes the user from the list on the hub. The hub sends to every client that the user has left the chat, so we can visualize that the user has left.

But when I'm using SignalR and Javascript in a webpage and the page gets closed, the hub doesn't get notified that the tab/browser is closed...

Anyone any idea how the connection can be closed?

What i've coded:

Startup Hub

[assembly: OwinStartup(typeof(ChatServer.Startup))]

namespace ChatServer
{
public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        // Map all hubs to "/signalr"
        app.MapSignalR();
    }
}
}

Hub

[HubName("ChatHub")]
public class ChatHub : Hub
{
    private static List<User> Users = new List<User>();
    public void Send(string name, string message)
    {
        // Call the broadcastMessage method to update clients.
        Clients.All.broadcastMessage(name, message, DateTime.Now);
    }

    public void UserConnected(string name)
    {
        Clients.Caller.connected(JsonConvert.SerializeObject(Users));
        User user = new User() { Name = name, ConnectionID = Context.ConnectionId };
        Clients.Others.userConnected(JsonConvert.SerializeObject(user));
        Users.Add(user);
    }

    public void UserLeft()
    {
        if(Users.Any(x=>x.ConnectionID == Context.ConnectionId))
        {
            User user = Users.First(x => x.ConnectionID == Context.ConnectionId);
            Users.Remove(user);
            Clients.Others.userLeft(JsonConvert.SerializeObject(user), Context.ConnectionId);
        }
    }

    public override System.Threading.Tasks.Task OnDisconnected()
    {
        // Only called when win8.1 app closes
        // Not called when browsers closes page
        UserLeft();
        return base.OnDisconnected();
    }
}

HTML - Javascript:

Javascript:

chat = new function () {

var ChatHub;
// Connecting to the hub
this.attachEvents = function () {
    if ($.connection != null) {
        ChatHub = $.connection.ChatHub;
       $.connection.hub.start({ transport: 'auto' }, function () {
            // Register client on hub
            ChatHub.server.userConnected("web name");
        });
    }

    this.send = function (name,message) {
        if ($.connection != null) {
                //Send chat message
                ChatHub.server.send(name, message).fail(function (e) {
                    alert(e);
                });
        }
    }
  }
};

 window.onbeforeunload = function (e) {
    //This is called when we close the page
    $.connection.hub.stop();
    return "You killed me! :'(";
};

Win8.1 client

    internal async void ConnectToHub(string userName)
    {
        try
        {
            HubConnection hubConnection = new HubConnection(SERVER_PROXY);
            chat = hubConnection.CreateHubProxy("ChatHub");
            Context = SynchronizationContext.Current;

            MakeHubFunctionsAvailableOnClient();

            await hubConnection.Start();
            // Register client on hub
            await chat.Invoke("UserConnected", userName);
        }
        catch (Exception)
        {
            throw;
        }
    }

    private void MakeHubFunctionsAvailableOnClient()
    {
        //Receive chat messages
        chat.On<string, string, DateTime>("broadcastMessage",
                (name, message, date) => Context.Post(
                    delegate {
                        Messages.Add(new UserMessage() { Message = message, User = name, Date = date }); 
                    }, null
                )
        );

        //Receive all online users
        chat.On<string>("connected",
                (users) => Context.Post(
                    delegate
                    {
                        List<User> userList = JsonConvert.DeserializeObject<List<User>>(users);
                        foreach (User user in userList)
                        {
                            Users.Add(user);
                        }
                        Messages.Add(new UserMessage() { Message = "CONNECTED", User = "System", Date = DateTime.Now }); 
                    }, null
                )
        );

        //New user connected
        chat.On<string>("userConnected",
                (user) => Context.Post(
                    delegate
                    {
                        User newUser = JsonConvert.DeserializeObject<User>(user);
                        Users.Add(newUser);
                        Messages.Add(new UserMessage() { Message = newUser.Name + " CONNECTED", User = "System", Date = DateTime.Now }); 
                    }, null
                )
        );

        //User left, remove user from list on client
        chat.On<string>("userLeft",
                (user) => Context.Post(
                    delegate
                    {
                        User newUser = JsonConvert.DeserializeObject<User>(user);
                        User y = Users.First(x=>x.ConnectionID == newUser.ConnectionID);
                        bool ux  = Users.Remove(y);
                        Messages.Add(new UserMessage() { Message = newUser.Name + " left the conversation", User = "System", Date = DateTime.Now });
                    }, null
                )
        );
    }

My hub doesn't trigger OnDisconnected, when i close tab / browser / navigating to other site The site is a singe page website (for the moment)

What browser am i using?
Chrome: Version 32.0.1700.68 beta-m
Internet Explorer 11

like image 280
brouckaertd Avatar asked Jan 09 '14 21:01

brouckaertd


1 Answers

I guess your question is how to detect whether a specific user left or closed the browser. When a user comes in or leaves, send a notification to other users.

I think the problem is you didn't handle OnConnected and OnDisconnected properly. To make it work, first you need to know each client connecting to a hub passes a unique connection id. You can retrieve this value in the Context.ConnectionId property of the hub context. When each client comes in, they go through OnConnected, when they leave, they go through OnDisconnected. Below is a working example:

Hub Class

[HubName("ChatHub")]
    public class ChatHub : Hub
    {
        private static List<User> Users = new List<User>();
        public void Send(string message)
        {
            var name = Context.User.Identity.Name;
            Clients.All.broadcastMessage(name, message, DateTime.Now.ToShortDateString());
        }
        public override Task OnConnected()
        {
            User user = new User() { Name = Context.User.Identity.Name, ConnectionID = Context.ConnectionId };
            Users.Add(user);
            Clients.Others.userConnected(user.Name);
            return base.OnConnected();
        }
        public override Task OnDisconnected()
        {
            if (Users.Any(x => x.ConnectionID == Context.ConnectionId))
            {
                User user = Users.First(x => x.ConnectionID == Context.ConnectionId);
                Clients.Others.userLeft(user.Name);   
                Users.Remove(user);
            }
            return base.OnDisconnected();
        }
    }

View

<input type="text" id="message" />
<button class="btn">Send</button>
<ul id="contents"></ul>

@section scripts
{
    <script src="~/Scripts/jquery-1.10.2.js"></script>
    <script src="~/Scripts/jquery.signalR-2.0.1.js"></script>
    <script src="/signalr/hubs"></script>

    <script>

        var chatHub = $.connection.ChatHub;
        chatHub.client.broadcastMessage = function (name, message, time) {
            var encodedName = $('<div />').text(name).html();
            var encodedMsg = $('<div />').text(message).html();
            var encodedTime = $('<div />').text(time).html();
            $('#contents').append('<li><strong>' + encodedName + '</strong>:&nbsp;&nbsp;' + encodedMsg + '</strong>:&nbsp;&nbsp;' + encodedTime + '</li>');
        };
        chatHub.client.userConnected = function (data) {          
            $('#contents').append('<li>Wellcome<strong>' + '</strong>:' + data + '</li>');
        };
        chatHub.client.userLeft = function (data) {
            $('#contents').append('<li>Bye<strong>' + '</strong>:' + data + '</li>');
        };
        $(".btn").on("click", function() {
            chatHub.server.send($("#message").val());
        });
        $.connection.hub.start().done(function() {
            console.log("connected...");
        });
    </script>
}
like image 164
Lin Avatar answered Oct 22 '22 18:10

Lin