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

Обнаружение отключения клиента 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)?

4b9b3361

Ответ 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), и если сервер обнаруживает, что он больше не опрошен, значит, клиент отсутствует дольше.