現在、HttpClientクラスを使用してHTTPリクエストを行うコードを使用しています。要求のタイムアウトを指定できますが、値は要求全体に適用されます(ホスト名の解決、接続の確立、要求の送信、および応答の受信を含みます)。
名前を解決したり接続を確立したりできない場合にリクエストをすばやく失敗させる方法が必要ですが、大量のデータを受信する必要がある場合もあるため、タイムアウトを減らすことはできません。
組み込み(BCL)クラスまたは代替HTTPクライアントスタックのいずれかを使用してこれを実現する方法はありますか?
RestSharpとServiceStackを簡単に見てきましたが、どちらも接続部分だけにタイムアウトを提供していないようです(ただし、間違っている場合は修正してください)。
接続に時間がかかりすぎる場合は、Timer
を使用してリクエストを中止できます。時間が経過したときにイベントを追加します。次のようなものを使用できます。
static WebRequest request;
private static void sendAndReceive()
{
// The request with a big timeout for receiving large amout of data
request = HttpWebRequest.Create("http://localhost:8081/index/");
request.Timeout = 100000;
// The connection timeout
var ConnectionTimeoutTime = 100;
Timer timer = new Timer(ConnectionTimeoutTime);
timer.Elapsed += connectionTimeout;
timer.Enabled = true;
Console.WriteLine("Connecting...");
try
{
using (var stream = request.GetRequestStream())
{
Console.WriteLine("Connection success !");
timer.Enabled = false;
/*
* Sending data ...
*/
System.Threading.Thread.Sleep(1000000);
}
using (var response = (HttpWebResponse)request.GetResponse())
{
/*
* Receiving datas...
*/
}
}
catch (WebException e)
{
if(e.Status==WebExceptionStatus.RequestCanceled)
Console.WriteLine("Connection canceled (timeout)");
else if(e.Status==WebExceptionStatus.ConnectFailure)
Console.WriteLine("Can't connect to server");
else if(e.Status==WebExceptionStatus.Timeout)
Console.WriteLine("Timeout");
else
Console.WriteLine("Error");
}
}
static void connectionTimeout(object sender, System.Timers.ElapsedEventArgs e)
{
Console.WriteLine("Connection failed...");
Timer timer = (Timer)sender;
timer.Enabled = false;
request.Abort();
}
ここでの時間はほんの一例です。ニーズに合わせて調整する必要があります。
.NETの HttpWebRequest は、リモートHTTPサーバーに接続するためのタイムアウトを指定するための2つのプロパティを公開します。
Timeout
プロパティはあなたが求めているものに最も近いですが、タイムアウト値に関係なく、DNS解決には最大15秒かかる可能性があることを示唆しています。
ドメインネームシステム(DNS)クエリは、戻るかタイムアウトするまでに最大15秒かかる場合があります。リクエストに解決が必要なホスト名が含まれていて、Timeoutを15秒未満の値に設定した場合、リクエストのタイムアウトを示すためにWebExceptionがスローされるまでに15秒以上かかる場合があります。
DNSルックアップのタイムアウトを15秒よりも短くする方法の1つは、 ホスト名を自分でルックアップする ですが、多くのソリューションでは、低レベルの設定を指定するためにP/Invokeが必要です。
基になるHttpWebRequest
TimeoutおよびReadWriteTimeoutプロパティもServiceStackの高レベルHTTPクライアント、つまり C#サービスクライアント で指定します。
var client = new JsonServiceClient(BaseUri) {
Timeout = TimeSpan.FromSeconds(30)
};
または、ServiceStackの HTTP Utils を使用して:
var timeoutMs = 30 * 1000;
var response = url.GetStringFromUrl(requestFilter: req =>
req.Timeout = timeoutMs);
RestSharpにはRestClientにタイムアウトプロパティがあると思います。
var request = new RestRequest();
var client = new RestClient
{
Timeout = timeout, //Timeout in milliseconds to use for requests made by this client instance
ReadWriteTimeout = readWriteTimeout //The number of milliseconds before the writing or reading times out.
};
var response = client.Execute(request);
//Handle response
そうです、この特定のタイムアウトを設定することはできません。ライブラリがどのように構築されたかについての十分な情報はありませんが、ライブラリが意図されている目的のために、それらは適切であると信じています。誰かがリクエストを実行し、すべてにタイムアウトを設定したいと考えています。
別のアプローチを取ることをお勧めします。ここでは、HttpRequest
が同時に実行する2つの異なることを実行しようとしています。
これを2つの段階に分けてみることができます。
Ping
class( これをチェックしてください )を使用して、ホストに到達し、そのタイムアウトを設定しようとします。HttpRequest
[〜#〜] if [〜#〜]を使用して、(タイムアウトの、名前/ルートの解決の一部は最初の段階で行われるため、このプロセスによってすべてが遅くなることはありません。これは完全に使い捨てではありません。
このソリューションには欠点があります。リモートホストはpingを受け入れる必要があります。お役に立てれば。
この方法を使用して、接続を確立できるかどうかを確認しました。ただし、これは保証HttpWebRequest
での後続の呼び出しによって接続を確立できることを保証するものではありません。
private static bool CanConnect(string machine)
{
using (TcpClient client = new TcpClient())
{
if (!client.ConnectAsync(machine, 443).Wait(50)) // Check if we can connect in 50ms
{
return false;
}
}
return true;
}