Ali*_*hid 6 c# http dotnet-httpclient
我的HttpClient使用摘要式身份验证连接到服务器,并期望响应搜索查询.这些搜索查询可以随时进入,因此客户端应始终保持连接处于打开状态.
使用以下代码建立连接:
public static async void ListenForSearchQueries(int resourceId)
{
var url = $"xxx/yyy/{resourceId}/waitForSearchRequest?token=abc";
var httpHandler = new HttpClientHandler { PreAuthenticate = true };
using (var digestAuthMessageHandler = new DigestAuthMessageHandler(httpHandler, "user", "password"))
using (var client = new HttpClient(digestAuthMessageHandler))
{
client.Timeout = TimeSpan.FromMilliseconds(Timeout.Infinite);
var request = new HttpRequestMessage(HttpMethod.Get, url);
var tokenSource = new CancellationTokenSource();
tokenSource.CancelAfter(TimeSpan.FromMilliseconds(Timeout.Infinite));
using (var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, tokenSource.Token))
{
Console.WriteLine("\nResponse code: " + response.StatusCode);
using (var body = await response.Content.ReadAsStreamAsync())
using (var reader = new StreamReader(body))
while (!reader.EndOfStream)
Console.WriteLine(reader.ReadLine());
}
}
}
Run Code Online (Sandbox Code Playgroud)
这是在控制台应用程序的Main方法中使用该方法的方法.
private static void Main(string[] args)
{
const int serviceId = 128;
.
.
.
ListenForSearchQueries(resourceId);
Console.ReadKey();
}
Run Code Online (Sandbox Code Playgroud)
这是控制台窗口上的输出如下所示:
Response code: OK
--searchRequestBoundary
Run Code Online (Sandbox Code Playgroud)
即使客户端的超时设置为无穷大,连接在第一次输出后大约五分钟(这不是HttpClient的默认超时)后超时,抛出以下异常.
System.IO.IOException occurred
HResult=0x80131620
Message=The read operation failed, see inner exception.
Source=System.Net.Http
StackTrace:
at System.Net.Http.HttpClientHandler.WebExceptionWrapperStream.Read(Byte[] buffer, Int32 offset, Int32 count)
at System.Net.Http.DelegatingStream.Read(Byte[] buffer, Int32 offset, Int32 count)
at System.IO.StreamReader.ReadBuffer()
at System.IO.StreamReader.get_EndOfStream()
at ConsoleTester.Program.<ListenSearchQueriesDigestAuthMessageHandler>d__10.MoveNext() in C:\Users\xyz\ProjName\ConsoleTester\Program.cs:line 270
Inner Exception 1:
WebException: The operation has timed out.
Run Code Online (Sandbox Code Playgroud)
用于身份验证的DelegateHandler是对此代码的粗略修改(请参阅源代码部分).
为什么客户端超时,我该如何防止这种情况发生?
我的最终目标是打个电话并无限期地等待回应.当响应确实到来时,我不希望关闭连接,因为将来可能会有更多响应.不幸的是,我无法在服务器端更改任何内容.
尽管的默认值Stream.CanTimeout 是false,但通过返回流会response.Content.ReadAsStreamAsync()提供CanTimeout属性返回true的流。
该流的默认读写超时为5分钟。也就是说,闲置五分钟后,流将引发异常。与问题中显示的异常非常相似。
若要更改此行为,可以调整流的ReadTimeout和/或WriteTimeout属性。
下面是将ListenForSearchQueriesReadTimeout更改为Infinite 的方法的修改版本。
public static async void ListenForSearchQueries(int resourceId)
{
var url = $"xxx/yyy/{resourceId}/waitForSearchRequest?token=abc";
var httpHandler = new HttpClientHandler { PreAuthenticate = true };
using (var digestAuthMessageHandler = new DigestAuthMessageHandler(httpHandler, "user", "password"))
using (var client = new HttpClient(digestAuthMessageHandler))
{
client.Timeout = TimeSpan.FromMilliseconds(Timeout.Infinite);
var request = new HttpRequestMessage(HttpMethod.Get, url);
var tokenSource = new CancellationTokenSource();
tokenSource.CancelAfter(TimeSpan.FromMilliseconds(Timeout.Infinite));
using (var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, tokenSource.Token))
{
Console.WriteLine("\nResponse code: " + response.StatusCode);
using (var body = await response.Content.ReadAsStreamAsync())
{
body.ReadTimeout = Timeout.Infinite;
using (var reader = new StreamReader(body))
while (!reader.EndOfStream)
Console.WriteLine(reader.ReadLine());
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
这修复了实际上由流引发但似乎由HttpClient引发的异常。