我编写了一个应用程序,部分可以从特定的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.
}
}
}
}
}