【问题标题】:How to cancel HttpListenerContext.AcceptWebSocketAsync?如何取消 HttpListenerContext.AcceptWebSocketAsync?
【发布时间】:2017-06-18 00:32:15
【问题描述】:

它没有取消令牌参数。 HttpListenerContext 也没有相关的 (Begin/End)AcceptWebSocket 方法。

【问题讨论】:

    标签: c# .net http websocket


    【解决方案1】:

    也许以下解决方案更适合您的情况,它基于article

    一旦触发取消令牌,这将停止侦听,然后您可以实现自定义逻辑来取消操作。在我的情况下,它足以打破循环,但它真的可以是你想要的任何东西。

        public void Stop()
        {
            this.Status = ServerStatus.Stopping;
    
            this.listener.Stop();
            this.cancellationTokenSource.Cancel();
    
            this.Status = ServerStatus.Stopped;
        }
    
        private async void ListenForConnections(CancellationToken cancellationToken)
        {
            try
            {
                while (this.Status == ServerStatus.Running)
                {
                    var socketTask = this.listener.AcceptSocketAsync();
    
                    var tcs = new TaskCompletionSource<bool>();
                    using (cancellationToken.Register(s => ((TaskCompletionSource<bool>)s).TrySetResult(true), tcs))
                    {
                        if (socketTask != await Task.WhenAny(socketTask, tcs.Task).ConfigureAwait(false))
                            break;
                    }
    
                    var context = new TcpContext(socketTask.Result);
    
                    this.OnConnectionReceived(context);
                }
            }
            catch (ObjectDisposedException)
            {
                // Closed
            }
        }
    

    【讨论】:

    • 有什么办法可以避免ObjectDisposedException?我一直在寻找避免这种异常的适当方法,但似乎每个人都只是吃了它然后继续前进。
    • 目前不可能,但是这里有一个问题来跟踪请求github.com/dotnet/corefx/issues/5749
    【解决方案2】:

    嗯,您从正在侦听请求的HttpListener 获得上下文(上下文不会自行侦听,据我所知,它只为您包装请求/响应)。我想你应该打电话给HttpListener.Stop() 会成功吗?

    【讨论】:

    • 看起来像答案。我需要一些时间来检查这个。
    • @DenisGladkiy 确定,如果可能,请在检查时对此事发表评论。我的假设完全基于文档、我对HttpListener 的经验和常识^-^ 很高兴知道这是否是正确的方法(我没有使用网络套接字)。
    • 根据 Web 套接字 RFC,AcceptSocketAsync 唯一应该做的就是发送几个 HTTP 标头:“好的,从现在开始通过 WebSocket 进行讨论”。我已经挖掘了 HttpListener 的来源,而 AcceptSocketAsync 正是这样做的。看起来 API 设计很糟糕:内部只有一个异步调用,并且它有一个带有取消令牌的重载 :(
    • 我未能重现此“发送”操作阻塞的确切情况:即使客户端没有从套接字读取,服务器也有足够大的缓冲区来接受标头。所以我不能保证关闭 HttpListenerContext 会做这些事情。但是来自 MS 的源代码和常识说这是要走的路。我试图调用 HttpListenerContext.Response.Close();在调用 AcceptWebSocketAsync 之前:正如预期的那样,从 AcceptWebSocketAsync 调用 ObjectDisposedException。
    • 在我的情况下,需要取消才能关闭应用程序。所以我会投票给你的答案。但我需要时间来研究罗纳德的第二个答案,看看它是否适合一般情况:关闭不是取消,它不会允许未来的工作。要将您的答案标记为已接受,我需要解决问题。
    【解决方案3】:

    你能做的最好的是将监听部分包装在一个线程中,当你想取消时,在线程上执行 Abort。

    一定要捕获方法中可能发生的 ObjectDisposedException。 对较低级别的 TcpListener 做了同样的事情。

        public void Stop()
        {
            this.Status = ServerStatus.Stopping;
            this.listener.Stop();
    
            this.listeningThread.Abort();
    
            this.Status = ServerStatus.Stopped;
        }
    
        /// <summary>
        ///     Listens for connections.
        /// </summary>
        private async void ListenForConnections()
        {
            try
            {
                while (this.Status == ServerStatus.Running)
                {
                    var socket = await this.listener.AcceptSocketAsync();
                    var context = new TcpContext(socket);
    
                    this.OnConnectionReceived(context);
                }
            }
            catch (ObjectDisposedException)
            {
                // Closed
            }
        }
    

    【讨论】:

    • 您提供的设计不适用于 Web 套接字,我认为 :( 在您的情况下,您可以创建一个接受线程。但是 Web 套接字是 HTTP 连接的抽象(通过 TCP 套接字):当我有一个 HttpListenerContext 时,TCP 连接已经创建,并且“AcceptWebSocketAsync”实际上是通过 HTTP 进行握手,所以它不能被卸载到一个线程(每个客户端的一个线程是不可扩展的)。
    【解决方案4】:

    我一直在寻找一种方法来确保 AcceptWebSocketAsync 不会通过与阻止我的侦听线程的敌对远程进行谈判而永远挂起。我是 Task 和 await/async 的新手,但这似乎符合我的要求:

    CancellationTokenSource upgradeTimeout = new CancellationTokenSource(1000);
    HttpListenerWebSocketContext webSocketContext = await Task.Run(async () => { return await httpContext.AcceptWebSocketAsync(null); }, upgradeTimeout.Token);
    

    这样做有什么问题吗?

    谢谢

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-09-02
      • 2023-03-24
      • 2011-12-28
      • 2023-03-09
      • 2011-12-10
      • 2015-09-01
      • 1970-01-01
      • 2021-02-28
      相关资源
      最近更新 更多