Общие сведения и обработка событий времени существования подключений в SignalR

Предупреждение

Эта документация не подходит для последней версии SignalR. Ознакомьтесь с ASP.NET Core SignalR.

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

В этой статье предполагается, что у вас уже есть некоторые знания о Событиях SignalR и времени существования подключения. Общие сведения о SignalR см. в статье Общие сведения о SignalR. Список событий времени существования подключения см. в следующих ресурсах:

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

Предыдущие версии этого раздела

Сведения о более ранних версиях SignalR см. в разделе Старые версии SignalR.

Вопросы и комментарии

Оставьте отзыв о том, как вам понравилось это руководство и что мы могли бы улучшить в комментариях в нижней части страницы. Если у вас есть вопросы, которые не связаны непосредственно с руководством, вы можете опубликовать их на форуме ASP.NET SignalR или StackOverflow.com.

Общие сведения

В этом разделе содержатся следующие подразделы:

Ссылки на справочные статьи по API относятся к версии API для .NET 4.5. Если вы используете .NET 4, ознакомьтесь с разделами api версии .NET 4.

Терминология и сценарии времени существования подключения

Обработчик OnReconnected событий в концентраторе SignalR может выполняться непосредственно после OnConnected , но не после OnDisconnected для данного клиента. Причина, по которой можно выполнить повторное подключение без отключения, заключается в том, что в SignalR используется слово "соединение".

Подключения SignalR, транспортные подключения и физические подключения

В этой статье рассматриваются подключения SignalR, подключения транспорта и физические подключения:

  • Подключение SignalR — это логическая связь между клиентом и URL-адресом сервера, поддерживаемая API SignalR и однозначно определяемая идентификатором подключения. Данные об этой связи поддерживаются SignalR и используются для установления транспортного соединения. Связь заканчивается, и SignalR удаляет данные, когда клиент вызывает Stop метод или достигается ограничение времени ожидания, пока SignalR пытается восстановить потерянное транспортное подключение.
  • Транспортное подключение — это логическая связь между клиентом и сервером, поддерживаемая одним из четырех ТРАНСПОРТНЫХ API: WebSockets, события, отправленные сервером, навсегда кадр или длинный опрос. SignalR использует ТРАНСПОРТНЫЙ API для создания транспортного подключения, а API транспорта зависит от наличия физического сетевого подключения для создания транспортного подключения. Транспортное подключение завершается, когда SignalR завершает его или когда API транспорта обнаруживает, что физическое соединение нарушено.
  • Физическое подключение относится к физическим сетевым каналам — проводам, беспроводным сигналам, маршрутизаторам и т. д. — упрощают обмен данными между клиентским компьютером и серверным компьютером. Чтобы установить транспортное соединение, должно быть установлено физическое соединение, а для установления соединения SignalR должно быть установлено транспортное соединение. Однако нарушение физического подключения не всегда немедленно завершает транспортное подключение или подключение SignalR, как будет описано далее в этом разделе.

На следующей схеме подключение SignalR представлено уровнями Api Концентраторов и Api PersistentConnection SignalR, транспортное подключение представлено уровнем Транспорта, а физическое соединение представлено линиями между сервером и клиентами.

Схема архитектуры SignalR

При вызове Start метода в клиенте SignalR вы предоставляете коду клиента SignalR все сведения, необходимые для установления физического подключения к серверу. Код клиента SignalR использует эти сведения для выполнения HTTP-запроса и установления физического подключения, использующего один из четырех методов транспорта. Если транспортное подключение завершается сбоем или происходит сбой сервера, подключение SignalR не удаляется сразу, так как клиент по-прежнему располагает сведениями, необходимыми для автоматического повторного создания нового транспортного подключения к тому же URL-адресу SignalR. В этом сценарии не требуется вмешательство со стороны пользовательского приложения, и когда код клиента SignalR устанавливает новое транспортное подключение, он не запускает новое подключение SignalR. Непрерывность подключения SignalR отражается в том факте, что идентификатор подключения, который создается при вызове Start метода, не изменяется.

Обработчик OnReconnected событий в концентраторе выполняется, когда транспортное подключение автоматически устанавливается после потери. Обработчик OnDisconnected событий выполняется в конце соединения SignalR. Подключение SignalR может завершиться любым из следующих способов:

  • Если клиент вызывает Stop метод , на сервер отправляется сообщение о остановке, и как клиент, так и сервер немедленно завершаются подключением SignalR.
  • После потери подключения между клиентом и сервером клиент пытается повторно подключиться, и сервер ожидает повторного подключения клиента. Если попытки повторного подключения не увенчались успехом и истекло время ожидания отключения, клиент и сервер завершают подключение SignalR. Клиент останавливает попытку повторного подключения, и сервер удаляет свое представление подключения SignalR.
  • Если клиент прекращает работу без возможности вызова Stop метода , сервер ожидает повторного подключения клиента, а затем прекращает подключение SignalR по истечении времени ожидания отключения.
  • Если сервер перестает работать, клиент пытается повторно подключиться (повторно создать транспортное подключение), а затем завершает подключение SignalR по истечении времени ожидания отключения.

Если нет проблем с подключением и пользовательское приложение завершает подключение SignalR путем вызова Stop метода , соединение SignalR и транспортное подключение начинаются и заканчиваются примерно в одно и то же время. В следующих разделах более подробно описаны другие сценарии.

Сценарии отключения транспорта

Физические подключения могут быть медленными или могут быть перерывы в подключении. В зависимости от таких факторов, как продолжительность прерывания, транспортное подключение может быть прервано. Затем SignalR пытается восстановить транспортное подключение. Иногда API транспортного подключения обнаруживает прерывание и разрывает транспортное подключение, а SignalR немедленно обнаруживает, что соединение потеряно. В других сценариях ни API подключения к транспорту, ни SignalR сразу же не узнают о том, что подключение потеряно. Для всех транспортов, кроме длительного опроса, клиент SignalR использует функцию keepalive для проверка потери подключения, которую API транспорта не может обнаружить. Сведения о длительных опросах подключений см. в разделе Timeout and keepalive settings далее в этом разделе.

Если подключение неактивно, сервер периодически отправляет клиенту пакет keepalive. На дату написания этой статьи частота по умолчанию — каждые 10 секунд. Прослушивая эти пакеты, клиенты могут определить, возникла ли проблема с подключением. Если пакет keepalive не получен, когда ожидалось, через некоторое время клиент предполагает наличие проблем с подключением, таких как медленная работа или прерывание работы. Если keepalive по-прежнему не получен через более длительное время, клиент предполагает, что подключение было разорвано, и начинает попытки повторного подключения.

На следующей схеме показаны события клиента и сервера, которые возникают в типичном сценарии, когда возникают проблемы с физическим подключением, которые не распознаются API транспорта сразу. Схема применяется к следующим обстоятельствам:

  • Транспортом является WebSockets, forever frame или события, отправленные сервером.
  • Существуют различные периоды прерывания физического сетевого подключения.
  • API транспорта не получает сведения о прерываниях, поэтому SignalR использует функциональность keepalive для их обнаружения.

Отключение транспорта

Если клиент переходит в режим повторного подключения, но не может установить транспортное подключение в течение времени ожидания отключения, сервер завершает подключение SignalR. В этом случае сервер выполняет метод концентратора OnDisconnected и помещает в очередь сообщение об отключении для отправки клиенту на случай, если клиенту удастся подключиться позже. Если клиент выполняет повторное подключение, он получает команду отключения и вызывает Stop метод . В этом сценарии не выполняется при OnReconnected повторном подключении клиента и OnDisconnected не выполняется при вызове Stopклиентом . Этот сценарий показан на следующей схеме.

Перебои в работе транспорта — время ожидания сервера

На клиенте могут возникать следующие события времени существования подключения SignalR:

  • ConnectionSlow событие клиента.

    Возникает, когда с момента получения последнего сообщения или проверки активности истекла предустановленная доля времени ожидания. Период предупреждения об истечении времени ожидания по умолчанию составляет 2/3 от времени ожидания keepalive. Время ожидания keepalive составляет 20 секунд, поэтому предупреждение возникает примерно через 13 секунд.

    По умолчанию сервер отправляет проверки связи по проверке проверки связи каждые 10 секунд, а клиент проверяет наличие проверки проверки связи примерно каждые 2 секунды (одна треть разницы между значением времени ожидания keepalive и значением предупреждения об истечении времени ожидания).

    Если API транспорта узнает об отключении, SignalR может быть проинформирован об отключении до истечения периода предупреждения о тайм-ауте keepalive. В этом случае ConnectionSlow событие не будет вызываться, и SignalR перейдет непосредственно к событию Reconnecting .

  • Reconnecting событие клиента.

    Возникает, когда (а) API транспорта обнаруживает, что подключение потеряно, или (б) время ожидания проверки активности прошло с момента получения последнего сообщения или проверки связи с сохранением. Код клиента SignalR начинает пытаться повторно подключиться. Это событие можно обработать, если требуется, чтобы приложение запустите какое-либо действие при потере транспортного подключения. Время ожидания по умолчанию составляет 20 секунд.

    Если клиентский код пытается вызвать метод концентратора, когда SignalR находится в режиме повторного подключения, SignalR попытается отправить команду. В большинстве случаев такие попытки завершаются неудачей, но в некоторых обстоятельствах они могут быть успешными. Для событий, отправляемых сервером, транспорта навсегда кадра и длительного опроса, SignalR использует два канала связи, один из них используется клиентом для отправки сообщений, а другой — для получения сообщений. Для получения используется постоянно открытый канал, который закрывается при прерывании физического подключения. Канал, используемый для отправки, остается доступным, поэтому при восстановлении физического подключения вызов метода от клиента к серверу может быть успешным до восстановления канала получения. Возвращаемое значение не будет получено до тех пор, пока SignalR не откроет канал, используемый для получения.

  • Reconnected событие клиента.

    Возникает при восстановлении транспортного соединения. Выполняется OnReconnected обработчик событий в концентраторе.

  • Closed client event (disconnected event in JavaScript).

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

Прерывания транспортного подключения, которые не обнаруживаются API транспорта и не задерживают получение проверки связи с сервером дольше, чем период предупреждения об истечении времени ожидания, может не вызывать никаких событий времени существования подключения.

Некоторые сетевые среды намеренно закрывают бездействующие подключения, а другая функция хранения пакетов заключается в том, чтобы предотвратить это, сообщая этим сетям, что используется подключение SignalR. В крайних случаях частоты проверки связи по умолчанию может быть недостаточно для предотвращения закрытых подключений. В этом случае вы можете настроить более частые отправки проверки проверки пинга. Дополнительные сведения см. в разделе Timeout and keepalive settings далее в этой статье.

Примечание

Важно! Последовательность событий, описанная здесь, не гарантируется. SignalR предпринимает все попытки вызвать события времени существования подключения предсказуемым образом в соответствии с этой схемой, но существует множество вариантов сетевых событий и много способов, с помощью которых базовые платформы связи, такие как API транспорта, обрабатывают их. Например, Reconnected событие может не возникать при повторном подключении клиента или OnConnected обработчик на сервере может быть запущен, когда попытка установить подключение неуспешна. В этом разделе описываются только те эффекты, которые обычно создаются определенными типичными обстоятельствами.

Сценарии отключения клиента

В клиенте браузера код клиента SignalR, поддерживающий подключение SignalR, выполняется в контексте JavaScript веб-страницы. Именно поэтому при переходе с одной страницы на другую необходимо завершить подключение SignalR, поэтому при подключении из нескольких окон или вкладок браузера у вас есть несколько подключений с несколькими идентификаторами подключений. Когда пользователь закрывает окно или вкладку браузера, переходит на новую страницу или обновляет страницу, подключение SignalR немедленно завершается, так как код клиента SignalR обрабатывает это событие браузера и вызывает Stop метод . В этих сценариях или на любой клиентской платформе, когда приложение вызывает Stop метод , OnDisconnected обработчик событий немедленно выполняется на сервере, и клиент вызывает Closed событие (событие называется disconnected в JavaScript).

Если клиентское приложение или компьютер, на котором оно работает, аварийно завершает работу или переходит в спящий режим (например, когда пользователь закрывает ноутбук), сервер не получает сведения о том, что произошло. Насколько известно серверу, потеря клиента может быть вызвана прерыванием подключения, и клиент может попытаться повторно подключиться. Таким образом, в этих сценариях сервер ожидает, чтобы клиент мог повторно подключиться, и OnDisconnected не выполняется, пока не истечет время ожидания отключения (по умолчанию около 30 секунд). Этот сценарий показан на следующей схеме.

Сбой клиентского компьютера

Сценарии отключения сервера

Когда сервер переходит в автономный режим — он перезагружается, завершается сбоем, домен приложения перезапускается и т. д. Результат может быть похож на потерянное подключение, или API транспорта и SignalR могут сразу узнать, что сервер удален, и SignalR может начать попытки повторного подключения без вызова ConnectionSlow события. Если клиент переходит в режим повторного подключения и если сервер восстанавливается или перезапускается или новый сервер подключается к сети до истечения времени ожидания отключения, клиент повторно подключается к восстановленным или новому серверу. В этом случае подключение SignalR продолжается на клиенте и Reconnected возникает событие . На первом сервере OnDisconnected никогда не выполняется, а на новом сервере выполняется, OnReconnected хотя OnConnected никогда не выполнялся для этого клиента на этом сервере ранее. (Результат будет таким же, если клиент повторно подключается к тому же серверу после перезагрузки или перезапуска домена приложения, так как при перезапуске сервера у него нет памяти о предыдущих действиях подключения.) На следующей схеме предполагается, что API транспорта немедленно узнает о потерянной связи, поэтому ConnectionSlow событие не вызывается.

Сбой сервера и повторное подключение

Если сервер не становится доступным в течение времени ожидания отключения, подключение SignalR завершается. В этом сценарии событие (disconnected в клиентах JavaScript) вызывается на клиенте, Closed но OnDisconnected никогда не вызывается на сервере. На следующей схеме предполагается, что API транспорта не получает сведения о потерянном соединении, поэтому оно обнаруживается функцией поддержки SignalR и ConnectionSlow вызывается событие.

Сбой сервера и время ожидания

Параметры времени ожидания и сохранения

Значения по умолчанию ConnectionTimeout, DisconnectTimeoutи KeepAlive подходят для большинства сценариев, но их можно изменить, если среда имеет особые потребности. Например, если сетевая среда закрывает бездействующие подключения в течение 5 секунд, может потребоваться уменьшить значение keepalive.

ConnectionTimeout

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

Этот параметр применяется только в том случае, если функциональность keepalive отключена, что обычно применяется только к длинному опрашиваемому транспорту. На следующей схеме показано влияние этого параметра на длинное опрашивающее транспортное соединение.

Длительное опрашивание транспортного подключения

DisconnectTimeout

Этот параметр представляет время ожидания после потери транспортного соединения перед возникновением Disconnected события. Значение по умолчанию - 30 секунды. При установке DisconnectTimeoutавтоматически KeepAlive устанавливается значение 1/3 значения DisconnectTimeout .

KeepAlive

Этот параметр представляет время ожидания перед отправкой пакета keepalive через неактивное подключение. Значение по умолчанию — 10 секунд. Это значение не должно превышать 1/3 значения DisconnectTimeout .

Если вы хотите задать и DisconnectTimeoutKeepAlive, установите после KeepAliveDisconnectTimeout. В противном случае параметр KeepAlive будет перезаписан, если DisconnectTimeout автоматически устанавливается KeepAlive значение 1/3 от значения времени ожидания.

Если вы хотите отключить функциональность keepalive, присвойте значение KeepAlive null. Функциональность Keepalive автоматически отключается для транспорта с длительным опросом.

Изменение параметров времени ожидания и сохранения

Чтобы изменить значения по умолчанию для этих параметров, задайте их в Application_Start файле Global.asax , как показано в следующем примере. Значения, показанные в примере кода, совпадают со значениями по умолчанию.

protected void Application_Start(object sender, EventArgs e)
{
    // Make long polling connections wait a maximum of 110 seconds for a
    // response. When that time expires, trigger a timeout command and
    // make the client reconnect.
    GlobalHost.Configuration.ConnectionTimeout = TimeSpan.FromSeconds(110);
    
    // Wait a maximum of 30 seconds after a transport connection is lost
    // before raising the Disconnected event to terminate the SignalR connection.
    GlobalHost.Configuration.DisconnectTimeout = TimeSpan.FromSeconds(30);
    
    // For transports other than long polling, send a keepalive packet every
    // 10 seconds. 
    // This value must be no more than 1/3 of the DisconnectTimeout value.
    GlobalHost.Configuration.KeepAlive = TimeSpan.FromSeconds(10);
    
    RouteTable.Routes.MapHubs();
}

Как уведомить пользователя об отключении

В некоторых приложениях может потребоваться отобразить сообщение для пользователя при возникновении проблем с подключением. У вас есть несколько вариантов того, как и когда это сделать. Приведенные ниже примеры кода предназначены для клиента JavaScript, использующий созданный прокси-сервер.

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

    $.connection.hub.connectionSlow(function() {
        notifyUserOfConnectionProblem(); // Your function to notify user.
    });
    
  • reconnecting Обработайте событие для отображения сообщения, когда SignalR знает об отключении и переходит в режим повторного подключения.

    $.connection.hub.reconnecting(function() {
        notifyUserOfTryingToReconnect(); // Your function to notify user.
    });
    
  • disconnected Обработайте событие для отображения сообщения, когда истекло время ожидания попытки повторного подключения. В этом сценарии единственный способ повторно установить соединение с сервером — перезапустить соединение SignalR, вызвав Start метод , который создаст новый идентификатор подключения. В следующем примере кода используется флаг , чтобы убедиться, что уведомление отправляется только после истечения времени ожидания повторного подключения, а не после нормального завершения подключения SignalR, вызванного Stop вызовом метода .

    var tryingToReconnect = false;
    
    $.connection.hub.reconnecting(function() {
        tryingToReconnect = true;
    });
    
    $.connection.hub.reconnected(function() {
        tryingToReconnect = false;
    });
    
    $.connection.hub.disconnected(function() {
        if(tryingToReconnect) {
            notifyUserOfDisconnect(); // Your function to notify user.
        }
    });
    

Непрерывное повторное подключение

В некоторых приложениях может потребоваться автоматически восстановить подключение после его потери и истечения времени ожидания попытки повторного подключения. Для этого можно вызвать метод из Closed обработчика Start событий (disconnectedобработчик событий на клиентах JavaScript). Вы можете подождать некоторое время перед вызовом Start , чтобы избежать этого слишком часто, когда сервер или физическое подключение недоступны. Следующий пример кода предназначен для клиента JavaScript, использующий созданный прокси-сервер.

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

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

Отключение клиента в серверном коде

SignalR версии 2 не имеет встроенного серверного API для отключения клиентов. Есть планы по добавлению этой функции в будущем. В текущем выпуске SignalR самый простой способ отключить клиент от сервера — реализовать метод отключения на клиенте и вызвать этот метод с сервера. В следующем примере кода показан метод отключения для клиента JavaScript с помощью созданного прокси-сервера.

var myHubProxy = $.connection.myHub
myHubProxy.client.stopClient = function() {
    $.connection.hub.stop();
};

Предупреждение

Безопасность. Ни этот метод отключения клиентов, ни предлагаемый встроенный API не будут учитывать сценарий взлома клиентов, выполняющих вредоносный код, так как клиенты могут повторно подключиться или взломанный код может удалить stopClient метод или изменить его действия. Подходящее место для реализации защиты от отказа в обслуживании (DOS) с отслеживанием состояния — не на уровне платформы или сервера, а в интерфейсной инфраструктуре.

Определение причины отключения

SignalR 2.1 добавляет перегрузку к событию сервера OnDisconnect , указывающее, был ли клиент намеренно отключен, а не истекает время ожидания. Параметр StopCalled имеет значение true, если клиент явно закрыл подключение. В JavaScript, если ошибка сервера привела к отключению клиента, сведения об ошибке будут переданы клиенту как $.connection.hub.lastError.

Код сервера C#: stopCalled параметр

public override System.Threading.Tasks.Task OnDisconnected(bool stopCalled)
{
    if (stopCalled)
    {
        Console.WriteLine(String.Format("Client {0} explicitly closed the connection.", Context.ConnectionId));
    }
    else
    {
        Console.WriteLine(String.Format("Client {0} timed out .", Context.ConnectionId));
    }
            
    return base.OnDisconnected(stopCalled);
}

Код клиента JavaScript: доступ к lastError событию disconnect .

$.connection.hub.disconnected(function () {
    if ($.connection.hub.lastError) 
        { alert("Disconnected. Reason: " +  $.connection.hub.lastError.message); }
});