System.Net.Http.HttpRequestException将内容复制到流时出错

问题描述 投票:12回答:3

我在.Net 4.5.2框架中使用HttpClient类。我正在对第三方Web服务进行PostAsync。这篇文章80%的时间都在工作,20%的时间我们的回复被缩短了。在这种情况下,我们得到以下异常:

System.Net.Http.HttpRequestException:将内容复制到流时出错。 ---> System.IO.IOException:无法从传输连接读取数据:远程主机强制关闭现有连接。 ---> System.Net.Sockets.SocketException:一个现有的连接被强制由远程主机在System.Net.Sockets.NetworkStream.BeginRead关闭(字节[]缓冲液,的Int32偏移的Int32大小,AsyncCallback的回调,对象状态) ---内部异常堆栈跟踪的末尾在System.Net.Sockets.NetworkStream.BeginRead(字节[]缓冲液,的Int32偏移的Int32大小,AsyncCallback的回调,对象状态)在System.Net.FixedSizeReader.StartReading()在System.Net.Security._SslStream.StartFrameHeader(字节[]缓冲液,的Int32偏移的Int32计数,AsyncProtocolRequest asyncRequest)在System.Net.Security._SslStream.StartReading(字节[]缓冲液,的Int32偏移的Int32计数,AsyncProtocolRequest asyncRequest)在System.Net.Security._SslStream.ProcessRead(字节[]缓冲液,的Int32偏移的Int32计数,AsyncProtocolRequest asyncRequest)在System.Net.TlsStream.BeginRead(字节[]缓冲液,的Int32偏移的Int32大小,的AsyncCallback的AsyncCallback,对象asyncState )在System.Net.ConnectStream.BeginReadWithoutValidation(Byte [])缓冲器,的Int32偏移的Int32大小,AsyncCallback的回调,在System.Net.ConnectStream.BeginRead(字节[]缓冲液,的Int32偏移的Int32大小,AsyncCallback的回调,对象状态)在System.Net.Http.HttpClientHandler.WebExceptionWrapperStream对象状态) .BeginRead(字节[]缓冲液,的Int32偏移的Int32计数,AsyncCallback的回调,对象状态)在System.Net.Http.StreamToStreamCopy.StartRead()

随后的相同请求成功。这不是我们可以重新设置的请求,因为已经放置了业务。所以它让我们处于一种尴尬的境地。

这是我的代码:

using (var httpClient = new HttpClient())
{
    httpClient.DefaultRequestHeaders.Authorization = authorizationHeader;
    HttpContent httpContent = new StringContent(someXml);

    //Exception occurs on next line...
    var response = await httpClient.PostAsync("https://thirdpartyendpoint", httpContent);
    var responseXml = await response.Content.ReadAsStringAsync();  
    //convert to Dto              
}

第三方服务成功地将记录保存到他们的数据库中,并且在他们的结尾没有看到任何明显的例外。他们确实注意到,写入数据库的失败请求通常需要比成功请求更长的时间(大约18-30秒)。

谢谢您的帮助

c# asp.net .net asp.net-web-api
3个回答
19
投票

我们用2个代码更改解决了这个问题:

  1. 处理httpResponseMessage并使用简单的DTO using (var httpResponseMessage = await httpClient.SendAsync(httpRequestMessage)) { return await CreateDto(httpResponseMessage); }
  2. 将HTTP版本降级到v1.0 var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, new Uri(url)) { Version = HttpVersion.Version10, Content = httpContent }; await client.SendAsync(httpRequestMessage);

具有添加此Http标头的效果

    Connection: close 

而不是这个

    Connection: keep-alive

2
投票

使用连接到服务器进行REST调用的共享HttpClient时,我遇到了类似的问题。问题最终是客户端和服务器上的KeepAlive超时不匹配。客户端超时由ServicePointManager上的MaxServicePointIdleTime设置设置,默认为100秒。服务器端空闲超时在我们的服务器中设置为较短的值。

与客户端相比,服务器上的超时更短导致服务器在客户端尝试连接时偶尔关闭连接。这导致了报告的例外情况。

请注意,我最终发现了问题,因为我在相同的条件下也收到了此异常:

System.Net.WebException: The underlying connection was closed: A connection that was expected to be kept alive was closed by the server.

0
投票

使用HTTPClient PutAsync()方法时,我遇到了同样的错误(将内容复制到流时出错):

using (StreamContent content = new StreamContent(stream))
{
    HttpResponseMessage response = await client.PutAsync(url, content))
}

您需要指定在PutAsync中不可用的HttpCompletionOption.ResponseHeadersRead标志,因此我切换到SendAsync:

using (StreamContent content = new StreamContent(stream))
{
    var httpRequest = new HttpRequestMessage(HttpMethod.Put, url);
    httpRequest.Content = content;

    HttpResponseMessage response = await client.SendAsync(httpRequest, HttpCompletionOption.ResponseHeadersRead);
}
© www.soinside.com 2019 - 2024. All rights reserved.