C# 在ASP.NET MVC中检测异步客户端断开连接

C# 在ASP.NET MVC中检测异步客户端断开连接,c#,asp.net-mvc,asynchronous,C#,Asp.net Mvc,Asynchronous,给定异步控制器: public class MyController : AsyncController { [NoAsyncTimeout] public void MyActionAsync() { ... } public void MyActionCompleted() { ... } } 假设MyActionAsync启动了一个需要几分钟的过程。如果用户现在转到MyAction操作,浏览器将等待连接打开。如果用户关闭浏览器,则连接将关闭。是否可以在服务器上

给定异步控制器:

public class MyController : AsyncController 
{
    [NoAsyncTimeout]
    public void MyActionAsync() { ... }

    public void MyActionCompleted() { ... }
}
假设
MyActionAsync
启动了一个需要几分钟的过程。如果用户现在转到
MyAction
操作,浏览器将等待连接打开。如果用户关闭浏览器,则连接将关闭。是否可以在服务器上(最好在控制器内部)检测到这种情况?如果是,怎么做?我已经尝试过重写
OnException
,但在这种情况下,它永远不会触发


注意:我非常感谢下面的有用答案,但这个问题的关键是我使用的是
异步控制器。这意味着HTTP请求仍处于打开状态(它们像COMET或BOSH一样存在很长时间),这意味着它是一个活动套接字连接。当此实时连接终止时,为什么不能通知服务器(即“由对等方重置连接”,TCP RST数据包)?

由于明显的原因,无法通知服务器客户端已关闭其浏览器。或者他去了厕所:-)你能做的就是让客户端以固定的间隔(
window.setInterval
)不断地用AJAX请求轮询服务器,如果服务器检测到它不再被轮询,就意味着客户端不在了。

正如@Darin所说。HTTP是一种无状态协议,这意味着无法(通过使用HTTP)检测客户端是否仍然存在。HTTP 1.0在每次请求后关闭套接字,而HTTP/1.1可以将其保持打开一段时间(保持活动超时可以设置为标头)。HTTP/1.1客户端关闭套接字(或服务器)并不意味着客户端已经离开,只是套接字已经有一段时间没有使用了


有一种叫做的东西,用于让客户机/服务器继续通过HTTP“聊天”。在SO或网络上搜索comet,有几种可用的实现。

没有响应。iClientConnected在这方面工作得相当好吗?我刚刚尝试过取消大文件上传。我的意思是,如果客户机中止(在我的例子中是Ajax)请求,我可以在我的操作中看到这一点。我并不是说它是100%准确的,但我的小规模测试表明,客户端浏览器中止了请求,并且该操作从iClientConnected获得了正确的响应。

我意识到这个问题很老,但在我搜索相同答案的过程中,它经常出现。 以下详细信息仅适用于.Net 4.5

这就是你想要的。这将为您提供一个可以传递给异步/等待调用的消息

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

您可以通过在函数的开始处放置一个断点(这样,当遇到断点时,请求将花费500多毫秒),然后让它运行出来来测试这个问题。

我很欣赏响应,但我确实意识到HTTP不允许我知道用户在响应发出后何时关闭浏览器。然而,这不是我所要求的。
AsyncController
的一个重要部分是请求没有完成——它仍然处于打开状态。原则上,似乎可以知道没有客户端可以向其发送repsonse。@Kirk,就HTTP协议而言,
AsyncController
与普通的
控制器没有任何不同。协议始终保持无状态。它是无状态的,它的请求/响应也是无状态的。但这笔交易仍可能需要很长时间。这正是COMET和BOSH的工作原理(或者坦白说,当您启用聊天功能时,甚至Gmail),我不明白为什么无法识别实时连接的终止(这是一个实时连接,这就是为什么您可以在最后发送响应的原因)。-1 HTTP位于TCP之上,当用户关闭浏览器时,TCP连接已关闭。HTTP的“无状态”是在后续请求/响应对之间的,它不影响连接在服务响应中是否具有状态或其他状态(例如从TCP管道读取,任何到双CR LF都是头-在HTTP协议的中间有一个状态机右击)。。只要您具有TCP KeepAlive,或者服务器尝试开始写入响应,或者客户端的操作系统干净地关闭了连接,IIS就会知道不再有客户端。其他答案实际上描述了ASP.NET API对这些知识的披露。@jgauffin,感谢您的回复。有趣的是,你提到COMET,因为这正是我试图使用的东西。当使用
AsyncController
时,您正在进行长寿命的HTTP连接——就像COMET一样。因此,如果用户在连接仍然打开的情况下关闭浏览器,为什么服务器不能知道并通知我?@Kirk,当使用异步控制器时,客户端没有参与任何长期的连接。就HTTP协议而言,它与普通控制器完全相同。唯一的区别是ASP.NET将在一个工作线程上开始请求,并可以在另一个工作线程上完成请求,如果您的API支持,则在这两者之间可以使用IOCP。但从协议层面看,如果你没有使用异步控制器,那么exchange也是一样的东西。@Darin,我相信你错了。在firebug中,甚至直接在浏览器中,您可以看到连接已打开。您可以按escape键,它将终止连接。这就像一个需要很长时间才能处理的请求。(或者换句话说,你是对的——这就像一个正常的控制器需要很长时间来服务一个动作——这确实是一个长寿命的HTTP连接。)@Darin,TCP RST数据包呢?我知道这并不能保证,但我认为在这种情况下,大多数表现良好的套接字实现都会发送这种消息。@jgauffin——我想我看到了我现在缺少的东西。您和Darin显然是正确的,请求完成后,客户端无法发送任何数据包,RST或o
[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();
}