我在.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秒)。
谢谢您的帮助
投票
我们用2个代码更改解决了这个问题:
处理httpResponseMessage并使用简单的DTO using (var httpResponseMessage = await httpClient.SendAsync(httpRequestMessage)) { return await CreateDto(httpResponseMessage); }
将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
投票
使用连接到服务器进行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.
投票
使用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);
}