私の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());
}
}
}
これは、コンソールアプリケーションのMainメソッドでメソッドが使用されている方法です。
private static void Main(string[] args)
{
const int serviceId = 128;
.
.
.
ListenForSearchQueries(resourceId);
Console.ReadKey();
}
コンソールウィンドウの出力は次のようになります。
Response code: OK
--searchRequestBoundary
クライアントのタイムアウトが無限大に設定されていても、接続は最初の出力から約5分後にタイムアウトし(これは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.
認証に使用されるDelegateHandlerは、 this コードの大まかな適応です(ソースセクションを参照)。
クライアントがタイムアウトするのはなぜですか?これを防ぐにはどうすればよいですか?
私の最終的な目標は、電話をかけ、無期限に応答を待つことです。応答があった場合、将来さらに応答が来る可能性があるため、接続を閉じたくありません。残念ながら、サーバー側では何も変更できません。
_Stream.CanTimeout
_ false のデフォルト値ですが、response.Content.ReadAsStreamAsync()
を介してストリームを返すと、CanTimeoutプロパティがtrueを返すストリームが得られます。
このストリームのデフォルトの読み取りおよび書き込みタイムアウト 5分 。つまり、非アクティブ状態が5分間続くと、ストリームは例外をスローします。質問に示されている例外とよく似ています。
この動作を変更するには、ストリームのReadTimeoutおよび/またはWriteTimeoutプロパティを調整できます。
以下は、ReadTimeoutをInfiniteに変更するListenForSearchQueries
メソッドの修正バージョンです。
_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());
}
}
}
}
_
これにより、実際にはストリームによってスローされていたが、HttpClientによってスローされているように見えた例外が修正されました。
メソッドがTask
を返すようにします
public static async Task ListenForSearchQueries(int resourceId) {
//...code removed for brevity
}
コンソールのmainメソッドをWait
のTask
に更新して完了します。
public static void Main(string[] args) {
const int serviceId = 128;
.
.
.
ListenForSearchQueries(resourceId).Wait();
Console.ReadKey();
}
私はこの問題を次の方法で解決しました。
var stream = await response.Content.ReadAsStreamAsync();
while (b == 1)
{
var bytes = new byte[1];
try
{
var bytesread = await stream.ReadAsync(bytes, 0, 1);
if (bytesread > 0)
{
text = Encoding.UTF8.GetString(bytes);
Console.WriteLine(text);
using (System.IO.StreamWriter escritor = new System.IO.StreamWriter(@"C:\orden\ConSegu.txt", true))
{
if (ctext == 100)
{
escritor.WriteLine(text);
ctext = 0;
}
escritor.Write(text);
}
}
}
catch (Exception ex)
{
Console.WriteLine("error");
Console.WriteLine(ex.Message);
}
}
このようにして、答えをバイト単位で取得し、後でtxtに保存します。後で、txtを読んで、もう一度消去します。今のところ、永続的なHTTP接続からサーバーから送信された通知を受信することがわかったソリューションです。