将内容复制到流时出错

本文关键字:出错 复制 | 更新日期: 2023-09-27 18:18:39

我正在使用。net Framework 4.5.2中的HttpClient类。

我对第三方web服务调用PostAsync。80%的时间这篇文章是有效的,20%的时间我们的回应被缩短了。在这种情况下,我们得到以下异常:

System.Net.Http。HttpRequestException: 复制内容到一个流。——比;System.IO.IOException:无法从传输连接:现有连接被强制关闭远端主机。——比;socketexception:一个已经存在的连接被远程主机强制关闭System.Net.Sockets.NetworkStream。BeginRead(Byte[] buffer, Int32offset, Int32大小,AsyncCallback,对象状态)——结束内部异常堆栈跟踪——atSystem.Net.Sockets.NetworkStream。BeginRead(Byte[] buffer, Int32offset, Int32大小,AsyncCallback回调,对象状态)System.Net.FixedSizeReader.StartReading ()System.Net.Security._SslStream。StartFrameHeader(Byte[] buffer, Int32offset, Int32 count, AsyncProtocolRequest, asyncRequest) atSystem.Net.Security._SslStream。StartReading(Byte[] buffer, Int32offset, Int32 count, AsyncProtocolRequest, asyncRequest) atSystem.Net.Security._SslStream。ProcessRead(Byte[] buffer, Int32offset, Int32 count, AsyncProtocolRequest, asyncRequest) atSystem.Net.TlsStream。BeginRead(Byte[] buffer, Int32 offset, Int32AsyncCallback的用法和样例System.Net.ConnectStream。BeginReadWithoutValidation (Byte[]缓冲区,Int32偏移量,Int32大小,AsyncCallback回调,对象状态)atSystem.Net.ConnectStream。BeginRead(Byte[] buffer, Int32 offset, Int32大小,AsyncCallback,回调,对象状态)System.Net.Http.HttpClientHandler.WebExceptionWrapperStream.BeginRead (Byte []缓冲区,Int32偏移量,Int32计数,AsyncCallback回调,对象

后续相同的请求成功。

我们无法重试此请求,因为业务操作已被执行。所以这让我们陷入了尴尬的境地。

这是我的代码:

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秒)来写入数据库。

我能做些什么来更好地处理这个问题?

将内容复制到流时出错

我们通过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

我在使用共享HttpClient连接到服务器进行REST调用时遇到了类似的问题。问题最终是客户端和服务器上的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.

我有相同的错误(错误时复制内容到流)与HTTPClient PutAsync()方法:

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

你需要指定HttpCompletionOption。ResponseHeadersRead标志在PutAsync中不可用,所以我切换到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);
}