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

时间:2015-10-20 10:07:58

标签: c# asp.net .net asp.net-web-api

我在.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(Byte []缓冲区,Int32   offset,Int32 size,AsyncCallback callback,Object state)---结束   内部异常堆栈跟踪--- at   System.Net.Sockets.NetworkStream.BeginRead(Byte []缓冲区,Int32   offset,Int32 size,AsyncCallback callback,Object state)at   System.Net.FixedSizeReader.StartReading()at   System.Net.Security._SslStream.StartFrameHeader(Byte []缓冲区,Int32   offset,Int32 count,AsyncProtocolRequest asyncRequest)at   System.Net.Security._SslStream.StartReading(Byte []缓冲区,Int32   offset,Int32 count,AsyncProtocolRequest asyncRequest)at   System.Net.Security._SslStream.ProcessRead(Byte []缓冲区,Int32   offset,Int32 count,AsyncProtocolRequest asyncRequest)at   System.Net.TlsStream.BeginRead(Byte []缓冲区,Int32偏移量,Int32   size,AsyncCallback asyncCallback,Object asyncState)at   System.Net.ConnectStream.BeginReadWithoutValidation(Byte []缓冲区,   Int32偏移量,Int32大小,AsyncCallback回调,对象状态)at   System.Net.ConnectStream.BeginRead(Byte []缓冲区,Int32偏移量,Int32   size,AsyncCallback回调,对象状态)at   System.Net.Http.HttpClientHandler.WebExceptionWrapperStream.BeginRead(字节[]   缓冲区,Int32偏移量,Int32计数,AsyncCallback回调,对象   System)Net.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秒)。

谢谢你的帮助

3 个答案:

答案 0 :(得分:17)

我们通过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);
    
  3. 具有添加此Http头的效果

        Connection: close 
    

    而不是这个

        Connection: keep-alive
    

答案 1 :(得分:1)

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

与客户端相比,服务器上的超时时间较短,导致服务器仅在客户端尝试连接时偶尔关闭连接。这导致了报告的异常。

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

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

答案 2 :(得分:0)

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

file3.js

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

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