Обнаружение отключения клиента async в ASP.NET MVC
Для асинхронного контроллера:
public class MyController : AsyncController
{
[NoAsyncTimeout]
public void MyActionAsync() { ... }
public void MyActionCompleted() { ... }
}
Предположим, что MyActionAsync
запускает процесс, который занимает несколько минут. Если теперь пользователь переходит к действию MyAction
, браузер будет ждать с открытием соединения. Если пользователь закрывает свой браузер, соединение закрывается. Можно ли обнаружить, когда это происходит на сервере (желательно внутри контроллера)? Если да, то как? Я попытался переопределить OnException
, но это никогда не срабатывает в этом сценарии.
Примечание.. Я ценю полезные ответы ниже, но ключевым аспектом этого вопроса является то, что я использую AsyncController
. Это означает, что HTTP-запросы все еще открыты (они долговечны, как COMET или BOSH), что означает его соединение в прямом соке. Почему сервер не может быть уведомлен о завершении этого прямого соединения (т.е. "Соединение reset через одноранговую сеть", пакет TCP RST)?
Ответы
Ответ 1
Я понимаю, что этот вопрос старый, но он часто проявлялся в моем поиске того же ответа.
Детали ниже применимы только к .Net 4.5
HttpContext.Response.ClientDisconnectedToken
- это то, что вы хотите. Это даст вам CancellationToken
, который вы можете передать своим асинхронным/ждущим вызовам.
public async Task<ActionResult> Index()
{
//The Connected Client 'manages' this token.
//HttpContext.Response.ClientDisconnectedToken.IsCancellationRequested will be set to true if the client disconnects
try
{
using (var client = new System.Net.Http.HttpClient())
{
var url = "http://google.com";
var html = await client.GetAsync(url, HttpContext.Response.ClientDisconnectedToken);
}
}
catch (TaskCanceledException e)
{
//The Client has gone
//you can handle this and the request will keep on being processed, but no one is there to see the resonse
}
return View();
}
Вы можете протестировать фрагмент выше, поставив точку останова в начале функции, затем закрыв окно браузера.
И еще один фрагмент, не имеющий прямого отношения к вашему вопросу, но полезного все-таки...
Вы также можете ограничить количество времени, которое может выполнить действие, используя атрибут AsyncTimeout
. Чтобы использовать это использование, добавьте дополнительный параметр типа CancellationToken
. Этот токен позволит ASP.Net тайм-аут запроса, если выполнение занимает слишком много времени.
[AsyncTimeout(500)] //500ms
public async Task<ActionResult> Index(CancellationToken cancel)
{
//ASP.Net manages the cancel token.
//cancel.IsCancellationRequested will be set to true after 500ms
try
{
using (var client = new System.Net.Http.HttpClient())
{
var url = "http://google.com";
var html = await client.GetAsync(url, cancel);
}
}
catch (TaskCanceledException e)
{
//ASP.Net has killed the request
//Yellow Screen Of Death with System.TimeoutException
//the return View() below wont render
}
return View();
}
Вы можете протестировать это, поставив точку останова в начале функции (таким образом, чтобы запрос был занят более 500 мс при ударе точки останова), после чего он истекает.
Ответ 2
Не работает ли Response.IsClientConnected для этого? Я только что попробовал в моем случае отменить большие загрузки файлов. Под этим я подразумеваю, что если клиент прерывает свои (в моем случае Ajax) запросы, я могу видеть это в своем действии. Я не говорю, что он на 100% точнее, но мое небольшое тестирование показывает, что клиентский браузер прерывает запрос и что действие получает правильный ответ от IsClientConnected.
Ответ 3
Это так же, как говорит @Darin. HTTP - это протокол без учета состояния, что означает, что нет способа (с помощью HTTP) обнаружить, что клиент все еще существует или нет. HTTP 1.0 закрывает сокет после каждого запроса, в то время как HTTP/1.1 может держать его открытым какое-то время (тайм-аут keep alive можно установить как заголовок). То, что клиент HTTP/1.1 закрывает сокет (или сервер, если на то пошло), не означает, что клиент ушел, только что сокет не использовался какое-то время.
Есть что-то называемое COMET-серверы, которые используются, чтобы позволить клиенту/серверу продолжать "общаться" через HTTP. Найдите комету здесь в SO или в сети, доступно несколько реализаций.
Ответ 4
По очевидным причинам сервер не может быть уведомлен о том, что клиент закрыл свой браузер. Или, что он пошел в туалет:-) Что вы можете сделать, так это то, что клиент постоянно проводит опрос сервера с запросами AJAX на регулярном интервале (window.setInterval
), и если сервер обнаруживает, что он больше не опрошен, значит, клиент отсутствует дольше.