【问题标题】:When is the connection closed with SignalR from browser何时从浏览器使用 SignalR 关闭连接
【发布时间】:2014-01-28 15:35:24
【问题描述】:

我正在使用 SignalR 2.0 制作一个小聊天应用程序(就像所有人一样)。

我有一个 win8.1 应用程序,当应用程序关闭时,集线器收到 OnDisconnected 事件并将用户从集线器的列表中删除。 集线器向每个客户端发送用户已离开聊天的信息,因此我们可以看到用户已离开。

但是当我在网页中使用 SignalR 和 Javascript 并且页面关闭时,集线器不会收到有关选项卡/浏览器已关闭的通知...

有人知道如何关闭连接吗?

我编码的内容:

创业中心

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

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

集线器

[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 客户端

    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
                )
        );
    }

当我关闭选项卡/浏览器/导航到其他站点时,我的集线器不会触发 OnDisconnected 该网站是一个单页网站(目前)

我用的是什么浏览器?
Chrome:版本 32.0.1700.68 beta-m
Internet Explorer 11

【问题讨论】:

    标签: javascript signalr


    【解决方案1】:

    我猜你的问题是如何检测特定用户是离开还是关闭了浏览器。当用户进入或离开时,向其他用户发送通知。

    我认为问题在于您没有正确处理OnConnectedOnDisconnected。 要使其工作,首先您需要知道连接到集线器的每个客户端都传递一个唯一的连接 ID。您可以在中心上下文的 Context.ConnectionId 属性中检索此值。每个客户进来时,他们通过OnConnected,当他们离开时,他们通过OnDisconnected。下面是一个工作示例:

    中心类

    [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();
            }
        }
    

    查看

    <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>
    }
    

    【讨论】:

    • 问题是当浏览器关闭/用户导航到另一个页面或用户关闭选项卡时,集线器不会触发 OnDisconnected。即使我打电话给 $.connection.hub.stop(); Ps:我用一些代码更新了我的问题。
    • 嗨@Lin,如果我执行你的代码,我会得到这些错误:“未捕获的类型错误:对象#没有方法'连接'”。当我记录客户端对象时-> console.log($.connection.ChatHub.client);结果:对象{}。如果我使用带有小写 c 的 ChatHub,chathub 是未定义的
    • 嗨@brouckaertd,我想问题是您的集线器类中没有 OnConnected 函数。我为你创建了一个类似的聊天功能,你看一下。
    • 回答有点晚,但 onConnected 事件是可覆盖的。所以据我所知,这意味着它总是被调用。如果我使用自定义代码实现 onConnected 事件。我认为那不会解决我的问题。但我会尝试你的代码,并希望它有效。
    • 我在您的客户那里使用了我的集线器。它有效!谢谢!现在只确定我们的代码有什么区别
    【解决方案2】:

    您可能会遇到此处描述的问题:https://github.com/SignalR/SignalR/issues/2719

    tl;dr:Chrome 31 中引入了一个错误,该错误已在 Chrome Canary 中修复,这会阻止 SignalR 立即触发 OnDisconnected。

    如果是,您可以使用我在问题中建议的解决方法,将以下内容添加到您的 JS 代码中。

    window.onbeforeunload = function (e) {
        $.connection.hub.stop();
    };
    

    即使有这个错误并且没有解决方法,我希望 SignalR 在浏览器退出托管 SignalR 的页面后大约 35 秒后引发 OnDisconnected。

    您能否提供有关您在哪些浏览器上遇到此问题以及在什么情况下遇到此问题的更多详细信息? (例如关闭标签页、关闭窗口、刷新、导航到同一站点的另一个页面、导航到不同的站点等)

    【讨论】:

    • 我在我的问题中添加了一些代码和信息。当我使用 $.connection.hub.stop();集线器不会触发 OnDisconnected。即使我等待 35 秒或更长时间。
    猜你喜欢
    • 2012-07-30
    • 2012-09-25
    • 1970-01-01
    • 2018-11-26
    • 1970-01-01
    • 2018-02-06
    • 2016-07-22
    • 1970-01-01
    • 2011-10-24
    相关资源
    最近更新 更多