我在移动应用程序中使用SignalR 2.0与.NET客户端,需要处理各种类型的断开连接。有时SignalR客户端会自动重新连接 - 有时必须通过再次调用HubConnection.Start()
直接重新连接。
由于SignalR在某些时候神奇地自动重新连接,我想知道我是否缺少功能或配置设置?
设置自动重新连接的客户端的最佳方法是什么?
我已经看过处理Closed()
事件的javascript示例,然后在n秒后连接。有没有推荐的方法?
我已经阅读了documentation以及有关SignalR连接生命周期的几篇文章,但我仍然不清楚如何处理客户端重新连接。
我终于想通了。以下是我在开始这个问题后学到的东西:
背景:我们正在使用Xamarin / Monotouch和.NET SignalR 2.0.3客户端构建iOS应用程序。我们使用默认的SignalR协议 - 它似乎使用SSE而不是Web套接字。我不确定是否可以使用带有Xamarin / Monotouch的网络插座。一切都使用Azure网站托管。
我们需要应用程序快速重新连接到我们的SignalR服务器,但是我们一直遇到连接没有自行重新连接的问题 - 或者重新连接花费了30秒(由于底层协议超时)。
我们最终测试了三种情况:
场景A - 第一次加载应用程序时连接。从第一天开始就完美无瑕。即使通过3G移动连接,连接也可在不到0.25秒的时间内完成。 (假设收音机已经开启)
场景B - 在应用程序空闲/关闭30秒后重新连接到SignalR服务器。在这种情况下,SignalR客户端最终将自己重新连接到服务器而无需任何特殊工作 - 但它似乎在尝试重新连接之前等待了30秒。 (我们的应用程序太慢了)
在这30秒的等待期间,我们尝试调用无效的HubConnection.Start()。并且调用HubConnection.Stop()也需要30秒。我找到了a related bug on the SignalR site that appears to be resolved,但我们在v2.0.3中仍然遇到了同样的问题。
场景C - 在应用程序空闲/关闭120秒或更长时间后重新连接到SignalR服务器。在这种情况下,SignalR传输协议已经超时,因此客户端永远不会自动重新连接。这解释了为什么客户有时但并不总是自己重新连接。好消息是,调用HubConnection.Start()几乎就像场景A一样。
所以我花了一段时间才意识到重新连接条件是根据应用程序是否关闭30秒而不是120秒而不同。虽然SignalR跟踪日志说明了底层协议正在发生的事情,但我认为没有办法处理代码中的传输级事件。 (Closed()事件在方案B中30秒后触发,在方案C中立即触发; State属性在这些重新连接等待期间显示“已连接”;没有其他相关事件或方法)
解决方案:解决方案很明显。我们不是在等待SignalR重新连接魔法。相反,当应用程序被激活或手机的网络连接恢复时,我们只是清理事件并取消引用HubConnection(无法处理它,因为它需要30秒,希望垃圾收集将处理它)并创建一个新实例。现在一切都很好。出于某种原因,我认为我们应该重用持久连接并重新连接,而不是仅仅创建一个新实例。
在断开连接的事件上设置计时器以自动尝试重新连接是我所知道的唯一方法。
在javascript中它是这样完成的:
$.connection.hub.disconnected(function() {
setTimeout(function() {
$.connection.hub.start();
}, 5000); // Restart connection after 5 seconds.
});
这是文档中推荐的方法:
由于OP要求.NET客户端(下面是一个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;
}
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
return connected;
}
private async void Connection_Closed()
{ // A global variable being set in "Form_closing" event
// of Form, check if form not closed explicitly to prevent a possible deadlock.
if(!IsFormClosed)
{
// 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")
}
}
Multiple query string
self.hubConnection = $.hubConnection();
self.hubConnection.qs = { param1: value, param2: value };
从:
您可能会尝试在重新连接状态启动之前从您的android调用服务器方法以防止魔法重新连接问题。
SignalR Hub C#
public class MyHub : Hub
{
public void Ping()
{
//ping for android long polling
}
}
在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);
});
}