首页 文章

将SignalR 2.0 .NET客户端重新连接到服务器中心的最佳实践

提问于
浏览
67

我在移动应用程序中使用SignalR 2.0和.NET客户端,因此我需要处理各种类型的断开连接 . 有时SignalR客户端在没有我帮助的情况下重新连接 - 有时我必须通过再次调用HubConnection.Start()来重新连接 . (因为SignalR在某些时候神奇地自动重新连接,我想知道我是否缺少功能或配置设置)

设置自动重新连接的客户端的最佳方法是什么?我已经看过处理Closed()事件的javascript示例,然后在n秒后连接 . 有没有推荐的方法?

我已经阅读了documentation以及有关SignalR连接生命周期的几篇文章,但我仍然不清楚如何处理客户端重新连接 .

谢谢!

4 回答

  • 60

    我终于想通了 . 以下是自从开始这个问题以来我学到的东西:

    Background: 我们're building an iOS app using Xamarin / Monotouch and the .NET SignalR 2.0.3 client. We'使用默认的SignalR协议 - 它似乎使用SSE而不是Web套接字 . 我可以使用带有Xamarin / Monotouch的网络套接字 . 一切都使用Azure网站托管 .

    我们需要应用程序快速重新连接到我们的SignalR服务器,但是我们一直遇到连接没有自行重新连接的问题 - 或者重新连接花费了30秒(由于底层协议超时) .

    我们最终测试了三种情况:

    Scenario A - connecting the first time the app was loaded. 从第一天开始就完美无瑕 . 即使通过3G移动连接,连接也可在不到0.25秒的时间内完成 . (假设收音机已经开启)

    Scenario B - reconnecting to the SignalR server after the app was idle/closed for 30 seconds. 在这种情况下,SignalR客户端最终将自行重新连接到服务器而无需任何特殊工作 - 但它似乎在尝试重新连接之前等待了30秒 . (我们的应用程序太慢了)

    在这30秒的等待期间,我们尝试调用无效的HubConnection.Start() . 并且调用HubConnection.Stop()也需要30秒 . 我找到a related bug on the SignalR site that appears to be resolved,但我们在v2.0.3中仍然遇到同样的问题 .

    Scenario C - reconnecting to the SignalR server after the app was idle/closed for 120 seconds or longer. 在这种情况下,SignalR传输协议已经超时,因此客户端永远不会自动重新连接 . 这解释了为什么客户有时但并不总是自己重新连接 . 好消息是,调用HubConnection.Start()几乎可以像方案A一样工作 .

    因此我花了一段时间才意识到重新连接条件因应用程序是否关闭30秒而不是120秒而有所不同 . 虽然SignalR跟踪日志说明了底层协议正在发生的事情,但我认为没有办法处理代码中的传输级事件 . (Closed()事件在方案B中30秒后触发,在方案C中立即触发; State属性在这些重新连接等待期间显示“已连接”;没有其他相关事件或方法)

    Solution: 解决方案很明显 . 我们're not waiting for SignalR to do its reconnection magic. Instead, when the app is activated or when the phone'的网络连接已恢复,我们're simply cleaning up the events and de-referencing the HubConnection (can' t处理它,因为它需要30秒,希望垃圾收集将处理它)并创建一个新实例 . 现在一切都很好 . 出于某种原因,我认为我们应该重用持久连接并重新连接,而不是仅仅创建一个新实例 .

  • -2

    由于OP要求 .NET client (下面是一个winform实现),

    private async Task<bool> ConnectToSignalRServer()
    {
        bool connected = false;
        try
        {
            Connection = new HubConnection("server url");
            Hub = Connection.CreateHubProxy("MyHub");
            await Connection.Start();
    
            //See @Oran Dennison's comment on @KingOfHypocrites's answer
            if (Connection.State == ConnectionState.Connected)
            {
                connected = true;
                Connection.Closed += Connection_Closed;
            }
            return connected;
        }
        catch (Exception ex)
        {
            Console.WriteLine("Error");
            return false;
        }
    }
    
    private async void Connection_Closed()
    {
        if(!IsFormClosed) // A global variable being set in "Form_closing" event of Form, check if form not closed explicitly to prevent a possible deadlock.
        {
            // specify a retry duration
            TimeSpan retryDuration = TimeSpan.FromSeconds(30);
            DateTime retryTill = DateTime.UtcNow.Add(retryDuration);
    
            while (DateTime.UtcNow < retryTill)
            {
                bool connected = await ConnectToSignalRServer();
                if (connected)
                    return;
            }
            Console.WriteLine("Connection closed")
        }
    }
    
  • 10

    在断开连接的事件上设置计时器以自动尝试重新连接是我所知道的唯一方法 .

    在javascript中它是这样完成的:

    $.connection.hub.disconnected(function() {
       setTimeout(function() {
           $.connection.hub.start();
       }, 5000); // Restart connection after 5 seconds.
    });
    

    这是文档中推荐的方法:

    http://www.asp.net/signalr/overview/signalr-20/hubs-api/handling-connection-lifetime-events#clientdisconnect

  • 36

    你可能会尝试在重新连接状态启动之前从你的android调用服务器方法,以防止魔法重新连接问题 .

    SignalR Hub C#

    public class MyHub : Hub
        {
            public void Ping()
            {
                //ping for android long polling
            }
     }
    

    In Android

    private final int PING_INTERVAL = 10 * 1000;
    
    private boolean isConnected = false;
    private HubConnection connection;
    private ClientTransport transport;
    private HubProxy hubProxy;
    
    private Handler handler = new Handler();
    private Runnable ping = new Runnable() {
        @Override
        public void run() {
            if (isConnected) {
                hubProxy.invoke("ping");
                handler.postDelayed(ping, PING_INTERVAL);
            }
        }
    };
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        System.setProperty("http.keepAlive", "false");
    
        .....
        .....
    
        connection.connected(new Runnable() {
            @Override
            public void run() {
                System.out.println("Connected");
                handler.postDelayed(ping, PING_INTERVAL);
        });
    }
    

相关问题