我编写了一个应用程序,部分可以从特定的Web服务下载文件 . 该代码利用HttpClient进行调用 . 问题是我偶尔会收到一条失败的请求,并带有以下异常消息:

无法从传输连接读取数据:连接已关闭 .

我确实遇到过这些博文,其中作者必须将协议版本恢复为1.0,禁用保持活动,并限制服务点连接的数量:

我遵循这些说明,因为我知道如何并仍然得到错误 . 我还确保保持HttpClient的单个实例(遵循Singleton原则) .

有趣的是,当运行Fiddler时我还没有得到错误,这让我觉得有一些事情可以在客户端完成,因为Fiddler似乎在做一些事情以保持连接活着(虽然问题是如此零星,这可能是一个红鲱鱼) .

还有几点说明:

  • 错误总是发生在下载过程中(从不在启动请求时) .

  • 文件继续下载到故障点(首先没有延长的暂停或延迟) .

--UPDATE--

错误发生在以下行:

responseTask.Wait(cancellationTokenSource.Token);

以下是完整的例外情况:

System.AggregateException occurred   HResult=-2146233088   Message=One
or more errors occurred.   Source=mscorlib   StackTrace:
       at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken)
       at Form1.StartDownload() in c:\Projects\Visual Studio 2012\Demo\Demo\Form1.cs:line 88   InnerException:
System.Net.Http.HttpRequestException
       HResult=-2146233088
       Message=Error while copying content to a stream.
       InnerException: System.IO.IOException
            HResult=-2146232800
            Message=Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host.
            Source=System
            StackTrace:
                 at System.Net.ConnectStream.EndRead(IAsyncResult asyncResult)
                 at System.Net.Http.HttpClientHandler.WebExceptionWrapperStream.EndRead(IAsyncResult
asyncResult)
                 at System.Net.Http.Handlers.ProgressStream.EndRead(IAsyncResult
asyncResult)
                 at System.Net.Http.StreamToStreamCopy.BufferReadCallback(IAsyncResult ar)
            InnerException: System.Net.Sockets.SocketException
                 HResult=-2147467259
                 Message=An existing connection was forcibly closed by the remote host
                 Source=System
                 ErrorCode=10054
                 NativeErrorCode=10054
                 StackTrace:
                      at System.Net.Sockets.NetworkStream.EndRead(IAsyncResult asyncResult)
                 InnerException:

--UPDATE #2--

我想我会尝试将完成选项从“内容读取”更改为“ Headers 读取” . 虽然在不同的位置(TODO评论的位置,读取内容流),但同样的例外也失败了 .

--UPDATE #3--

我可以确认Web服务(在IIS中托管)正在中止连接(IIS日志显示win32状态代码为1236 - ERROR_CONNECTION_ABORTED) . 为了尝试缩小范围,MinFileBytesPerSec配置数据库属性设置为零(在客户端暂时停止下拉数据的情况下),并且连接仍在中止 . 我已经仔细检查了所有超时和缓冲区大小,我认为无济于事 . 此刻 grab 空气稀薄 . 任何想法,将不胜感激 .

Client Setup:

private void SetupClient()
{
    // In case we're taxing the web server, limit the number
    // connections we're allowed to make to one.
    ServicePointManager.DefaultConnectionLimit = 1;

    // Set up the progress handler so that we can keep track of the download progress.
    _progressHandler = new ProgressMessageHandler();
    _progressHandler.HttpReceiveProgress += ProgressHandler_HttpReceiveProgress;

    // Create our HttpClient.
    _client = HttpClientFactory.Create(_progressHandler);
    _client.BaseAddress = new Uri("http://localhost");
    _client.Timeout = TimeSpan.FromMinutes(30);
    _client.DefaultRequestHeaders.TransferEncodingChunked = true;
}

Download Logic:

private void StartDownload()
{
    // Create the request.
    using (HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "http://localhost/Download"))
    {
        // Revert the protocol version and turn off keep alive in accordance with:
        // http://briancaos.wordpress.com/2012/07/06/unable-to-read-data-from-the-transport-connection-the-connection-was-closed/
        // http://briancaos.wordpress.com/2012/06/15/an-existing-connection-was-forcibly-closed-by-the-remote-host/

        request.Version = new Version("1.0");
        request.Headers.Add("Keep-Alive", "false");

        // Set the cancellation token's timeout to 30 minutes.
        int timeoutInMilliseconds = 30 * 60 * 1000;

        using (CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(timeoutInMilliseconds))
        {
            // Making sure that the message isn't "complete" until everything is read in so we can cancel it at anytime.
            Task<HttpResponseMessage> responseTask = _client.SendAsync(request, HttpCompletionOption.ResponseContentRead);

            responseTask.Wait(cancellationTokenSource.Token);

            using (HttpResponseMessage response = responseTask.Result)
            {
                if (!response.IsSuccessStatusCode)
                {
                    throw new Exception("Request failed!");
                }

                Task<Stream> streamTask = response.Content.ReadAsStreamAsync();

                using (Stream contentStream = streamTask.Result)
                {
                    // TODO: Save to disk.
                }
            }
        }
    }
}