web-dev-qa-db-ja.com

HttpWebRequest.GetResponse()がタイムアウトし続ける

次のAPI呼び出しでMtGoxから取引履歴を取得する簡単なC#関数を作成しました。

https://data.mtgox.com/api/1/BTCUSD/trades?since=<trade_id>

ここに文書化されています: https://en.bitcoin.it/wiki/MtGox/API/HTTP/v1#Multi_currency_trades

ここに関数があります:

string GetTradesOnline(Int64 tid)
{
    Thread.Sleep(30000);

    // communicate
    string url = "https://data.mtgox.com/api/1/BTCUSD/trades?since=" + tid.ToString();
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
    HttpWebResponse response = (HttpWebResponse)request.GetResponse();
    StreamReader reader = new StreamReader(response.GetResponseStream());

    string json = reader.ReadToEnd();
    reader.Close();
    reader.Dispose();
    response.Close();

    return json;
}

データを取得するために、(最初から)tid = 0(取引ID)から始めています。各リクエストに対して、1000件の取引詳細を含むレスポンスを受け取ります。私は常に次のリクエストの前のレスポンスからトレードIDを送信します。正確に4つの要求と応答に対して正常に機能します。しかし、その後、次の行は「System.Net.WebException」をスローして、「操作がタイムアウトしました」と言っています

HttpWebResponse response = (HttpWebResponse)request.GetResponse();

事実は次のとおりです。

  • 例外をキャッチし、再結合すると同じ例外が発生し続ける
  • デフォルトのHttpWebRequest .Timeoutおよび.ReadWriteTimeoutはすでに十分に高い(1分以上)
  • httpWebRequest.KeepAliveをfalseに変更しても何も解決しませんでした
  • 関数が失敗しているときでも、ブラウザで常に動作するようです
  • https://www.google.com からの応答の取得に問題はありません
  • 例外が発生するまでの成功した応答の量は日によって異なります(ただし、ブラウザーは常に動作します)
  • 前回失敗した取引IDから開始すると、すぐに例外が発生します
  • 代わりにメインスレッドからこの関数を呼び出すと、依然として例外が発生しました
  • 別のマシンで実行すると動作しませんでした
  • 別のIPから実行すると機能しませんでした
  • リクエスト間のThread.Sleepを増やしても効果はありません

間違っている可能性のあるアイデアはありますか?

16
symbiont

タイムアウトには2種類あります。クライアントのタイムアウトとサーバーのタイムアウト。このようなことをしようとしましたか?

request.Timeout = Timeout.Infinite;
request.KeepAlive = true;

このようなものを試してみてください...

20
kevin c

私はまったく同じ問題を抱えていました。私にとっては、修正はブロックを使用してHttpWebResponseコードをラップするのと同じくらい簡単でした。

using (HttpWebResponse response = (HttpWebResponse) request.GetResponse())
{
    // Do your processings here....
}

詳細:この問題は、通常、同じホストに対して複数のリクエストが行われ、WebResponseが適切に処理されない場合に発生します。 usingブロックがWebResponseオブジェクトを適切に破棄し、問題を解決する場所です。

24
Habeeb

Sslを介したLINUXサーバー上のREST Service。

REST API。

        private static string RunWebRequest(string url, string json)
    {
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);

        // Header
        request.ContentType = "application/json";
        request.Method = "POST";
        request.AllowAutoRedirect = false;
        request.KeepAlive = false;
        request.Timeout = 30000;
        request.ReadWriteTimeout = 30000;
        request.UserAgent = "test.net";
        request.Accept = "application/json";
        request.ProtocolVersion = HttpVersion.Version11;
        request.Headers.Add("Accept-Language","de_DE");
        ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls;
        ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
        byte[] bytes = Encoding.UTF8.GetBytes(json);
        request.ContentLength = bytes.Length;
        using (var writer = request.GetRequestStream())
        {
            writer.Write(bytes, 0, bytes.Length);
            writer.Flush();
            writer.Close();
        }

        var httpResponse = (HttpWebResponse)request.GetResponse();
        using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
        {
            var jsonReturn = streamReader.ReadToEnd();
            return jsonReturn;
        }
    }
8
Karl

それが価値があるのは、呼び出しが呼び出しているサーバーに到達したにもかかわらず、使用するたびにタイムアウトに関する同じ問題が発生することでした。私の場合の問題は、Expectがapplication/jsonに設定されていたが、サーバーが返すものではなかったということでした。

1
bech

これは解決策ではなく、単なる代替策です。最近では、ほとんどHttpWebRequestではなくWebClientのみを使用しています。特にPOSTおよびPUTとWebClient.DownloadStringのWebClient.UploadString。これらは単純に文字列を取得および返します。また、必要に応じてWebClient.Headers ["Content-type"]でコンテンツタイプを設定することもできます。

まれにパフォーマンスを向上させるために、System.Net.ServicePointManager.DefaultConnectionLimitを高く設定し、代わりにHttpClientを使用して、同時呼び出しにAsyncメソッドを使用します。

これが今のやり方です

string GetTradesOnline(Int64 tid)
{
    using (var wc = new WebClient())
    {
        return wc.DownloadString("https://data.mtgox.com/api/1/BTCUSD/trades?since=" + tid.ToString());
    }
}

2以上POST例

// POST
string SubmitData(string data)
{
    string response;
    using (var wc = new WebClient())
    {
        wc.Headers["Content-type"] = "text/plain";
        response = wc.UploadString("https://data.mtgox.com/api/1/BTCUSD/trades", "POST", data);
    }

    return response;
}

// POST: easily url encode multiple parameters
string SubmitForm(string project, string subject, string sender, string message)
{
    // url encoded query
    NameValueCollection query = HttpUtility.ParseQueryString(string.Empty);
    query.Add("project", project);
    query.Add("subject", subject);

    // url encoded data
    NameValueCollection data = HttpUtility.ParseQueryString(string.Empty);
    data.Add("sender", sender);
    data.Add("message", message);

    string response;
    using (var wc = new WebClient())
    {
        wc.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded";
        response = wc.UploadString( "https://data.mtgox.com/api/1/BTCUSD/trades?"+query.ToString()
                                  , WebRequestMethods.Http.Post
                                  , data.ToString()
                                  );
    }

    return response;
}

エラー処理

try
{
    Console.WriteLine(GetTradesOnline(0));

    string data = File.ReadAllText(@"C:\mydata.txt");
    Console.WriteLine(SubmitData(data));

    Console.WriteLine(SubmitForm("The Big Project", "Progress", "John Smith", "almost done"));
}
catch (WebException ex)
{
    string msg;
    if (ex.Response != null)
    {
        // read response HTTP body
        using (var sr = new StreamReader(ex.Response.GetResponseStream())) msg = sr.ReadToEnd();
    }
    else
    {
        msg = ex.Message;
    }

    Log(msg);
    throw; // re-throw without changing the stack trace
}
0
symbiont