Подтвердить что ты не робот

Лучшая практика для повторного подключения клиента SignalR 2.0.NET к серверу

Я использую SignalR 2.0 с клиентом .NET в мобильном приложении, которое должно обрабатывать различные типы отключений. Иногда клиент SignalR переподключается автоматически, а иногда его необходимо подключить напрямую, снова вызвав HubConnection.Start().

Поскольку SignalR волшебным образом автоматически переподключается, мне интересно, не хватает ли мне функции или настройки конфигурации?

Какой лучший способ настроить клиент, который автоматически подключается?


Я видел примеры JavaScript, которые обрабатывают событие Closed() и затем Connect через n секунд. Есть ли рекомендуемый подход?

Я прочитал документацию и несколько статей о времени жизни соединений SignalR, но мне все еще неясно, как обрабатывать переподключение клиента.

4b9b3361

Ответ 1

Я, наконец, понял это. Вот что я узнал с начала этого вопроса:

Фон:. Мы создаем приложение iOS с помощью Xamarin/Monotouch и .NET SignalR 2.0.3. Мы используем протоколы SignalR по умолчанию - и, похоже, он использует SSE вместо веб-сокетов. Я еще не уверен, если можно использовать веб-сокеты с Xamarin/Monotouch. Все размещается на сайтах Azure.

Нам понадобилось приложение для быстрого подключения к нашему серверу SignalR, но у нас остались проблемы, когда соединение не пересоединялось самостоятельно - или повторное подключение заняло ровно 30 секунд (из-за основного тайм-аута протокола).

Было три сценария, в которые мы закончили тестирование:

Сценарий A - подключение при первом загрузке приложения. Это работало безупречно с первого дня. Соединение завершается менее чем через .25 секунд даже через мобильные соединения 3G. (если радио уже включено)

Сценарий B - повторное подключение к серверу SignalR после того, как приложение простаивало/закрывалось на 30 секунд. В этом случае клиент SignalR в конечном итоге снова подключится к серверу без какой-либо специальной работы - но он, кажется, ждет ровно 30 секунд, прежде чем пытаться снова подключиться. (слишком медленное для нашего приложения)

В течение этого 30-секундного периода ожидания мы попытались вызвать HubConnection.Start(), который не повлиял. И вызов HubConnection.Stop() также занимает 30 секунд. Я нашел связанную ошибку на сайте SignalR, которая, как представляется, будет устранена, но мы все еще имеем ту же проблему в версии 2.0.3.

Сценарий C - повторное подключение к серверу SignalR после того, как приложение простаивало/закрывалось в течение 120 секунд или дольше. В этом случае транспортный протокол SignalR уже отключен, поэтому клиент никогда не будет автоматически повторно подключаться. Это объясняет, почему клиент иногда, но не всегда перестраивался сам по себе. Хорошей новостью является то, что вызов HubConnection.Start() работает почти мгновенно, как сценарий A.

Поэтому мне потребовалось некоторое время, чтобы понять, что условия повторного подключения различаются в зависимости от того, было ли приложение закрыто на 30 секунд против 120 + секунд. И хотя журналы трассировки SignalR подсвечивают то, что происходит с базовым протоколом, я не верю, что есть способ обработать события транспортного уровня в коде. (событие Closed() срабатывает через 30 секунд в сценарии B, сразу же в сценарии C, свойство State говорит "Подключено" во время этих периодов ожидания повторного подключения, никаких других соответствующих событий или методов)

Решение: Решение очевидно. Мы не ждем, когда SignalR сделает магию повторного соединения. Вместо этого, когда приложение активируется или когда восстановление телефонной сети восстанавливается, мы просто очищаем события и удаляем ссылки на HubConnection (не можем распоряжаться им, потому что это занимает 30 секунд, надеюсь, сбор мусора позаботится об этом ) и создание нового экземпляра. Теперь все отлично работает. По какой-то причине я думал, что мы должны повторно использовать постоянное соединение и снова подключаться, а не просто создавать новый экземпляр.

Ответ 2

Установка таймера в отключенном событии для автоматической попытки повторного подключения является единственным методом, о котором я знаю.

В 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

Ответ 3

Поскольку ОП запрашивает .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 comment on @KingOfHypocrites 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")
    }
}

Ответ 5

Вы можете попытаться вызвать метод сервера из своего андроида перед тем, как восстановить состояние начала, чтобы предотвратить проблему магии повторного подключения.

SignalR Hub С#

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