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

Kir*_*oll 14 c# asp.net-mvc asynchronous

给定一个异步控制器:

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

    public void MyActionCompleted() { ... }
}
Run Code Online (Sandbox Code Playgroud)

假设MyActionAsync开始一个需要几分钟的过程.如果用户现在进入MyAction操作,浏览器将等待连接打开.如果用户关闭其浏览器,则关闭连接.是否有可能检测到服务器上何时发生这种情况(最好是在控制器内)?如果是这样,怎么样?我尝试过压倒,OnException但在这种情况下永远不会发生.

注意: 我非常感谢下面的有用答案,但这个问题的关键方面是我正在使用AsyncController.这意味着HTTP请求仍然是打开的(它们像COMET或BOSH一样长寿),这意味着它是一个实时套接字连接.为什么在此实时连接终止时(即"通过对等方重置连接",TCP RST数据包)不能通知服务器?

Wil*_*l D 25

我意识到这个问题已经过时了,但在我寻找相同的答案时经常出现这个问题.以下详细信息仅适用于.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();
}
Run Code Online (Sandbox Code Playgroud)

您可以通过在函数开头放置断点然后关闭浏览器窗口来测试上面的代码段.


另一个片段,与你的问题没有直接关系,但却有用...

您还可以使用该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();
}
Run Code Online (Sandbox Code Playgroud)

您可以通过在函数的开头放置一个断点来测试这个(因此在断点被命中时请求需要超过500毫秒)然后让它用完.


Kon*_*tin 7

Response.IsClientConnected对此工作不是很好吗?我刚刚尝试在我的情况下取消大文件上传.我的意思是,如果客户端中止他们(在我的情况下是Ajax)请求,我可以在我的Action中看到它.我并不是说它是100%准确但我的小规模测试显示客户端浏览器中止请求,并且Action从IsClientConnected获得正确的响应.


jga*_*fin 5

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

有一种称为COMET服务器的东西,用于让客户端/服务器继续通过HTTP"聊天".在SO或网上搜索彗星,有几种可用的实现.

  • @jgauffin,谢谢你的回复.你提到COMET很有意思,因为这基本上是我试图使用的.当使用`AsyncController`时,您正在使用长期的HTTP连接 - *就像COMET*一样.因此,如果用户在连接仍处于打开状态时关闭浏览器**,为什么服务器无法知道它并通知我? (3认同)
  • @Darin,我相信你错了.在firebug甚至直接浏览器中,您可以看到连接已打开.您可以点击转义键,它将终止连接.这就像一个需要很长时间才能处理的请求.(换句话说,你是*正确的* - 它就像一个普通的控制器*需要很长时间*才能为一个动作提供服务 - 这确实是一个长期的HTTP连接.) (2认同)