it-swarm.com.de

HttpWebRequest.GetResponse () erhält immer ein Zeitlimit

ich habe eine einfache C # -Funktion geschrieben, um den Handelsverlauf von MtGox mit folgendem API-Aufruf abzurufen:

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

hier dokumentiert: https://de.bitcoin.it/wiki/MtGox/API/HTTP/v1#Multi_currency_trades

hier ist die Funktion:

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;
}

ich beginne bei tid = 0 (Handels-ID), um die Daten zu erhalten (von Anfang an). für jede anfrage erhalte ich eine antwort mit 1000 handelsdetails. Ich versende immer die Handels-ID aus der vorherigen Antwort für die nächste Anfrage. es funktioniert gut für genau 4 Anfragen & Antworten. Danach gibt die folgende Zeile eine "System.Net.WebException" aus, die besagt, dass die Operation das Zeitlimit überschritten hat:

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

hier sind die Fakten:

  • die Ausnahme wird abgefangen und die Wiederholung wird wiederholt die gleiche Ausnahme verursachen
  • die Standardwerte für HttpWebRequest .Timeout und .ReadWriteTimeout sind bereits hoch genug (über eine Minute).
  • das Ändern von HttpWebRequest.KeepAlive in false löste ebenfalls nichts
  • es scheint immer im Browser zu funktionieren, selbst wenn die Funktion ausfällt
  • es hat keine Probleme, die Antwort von https://www.google.com abzurufen.
  • die Anzahl der erfolgreichen Antworten vor den Ausnahmen variiert von Tag zu Tag (der Browser funktioniert jedoch immer).
  • ab der zuletzt fehlgeschlagenen Handels-ID wird die Ausnahme sofort ausgelöst
  • das Aufrufen dieser Funktion aus dem Haupt-Thread verursachte immer noch die Ausnahme
  • es auf einem anderen Rechner zu laufen, funktionierte nicht
  • es von einer anderen IP aus zu laufen, hat nicht funktioniert
  • das Erhöhen von Thread.Sleep zwischen Anforderungen hilft nicht

irgendwelche ideen von was könnte falsch sein?

13
symbiont

Es gibt zwei Arten von Timeouts. Client-Timeout und Server-Timeout Haben Sie versucht, so etwas zu tun:

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

Versuchen Sie etwas davon ...

18
kevin c

Ich hatte das gleiche Problem ... Für mich war das Update so einfach wie das Einbetten des HttpWebResponse-Codes in using-Block.

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

Details : Dieses Problem tritt normalerweise auf, wenn mehrere Anforderungen an denselben Host gestellt werden und WebResponse nicht ordnungsgemäß entsorgt wird. Dort wird der using-Block das WebResponse-Objekt ordnungsgemäß entsorgen und so das Problem lösen.

21
Habeeb

Ich hatte gerade ähnliche Probleme, einen REST - Dienst auf einem LINUX-Server über ssl aufzurufen. Nachdem ich viele verschiedene Konfigurationsszenarien ausprobiert hatte, stellte ich fest, dass ich einen UserAgent im http-Kopf senden musste.

Hier ist meine letzte Methode zum Aufrufen der 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

Was es wert ist, ich hatte jedes Mal, wenn ich es benutzte, dieselben Probleme mit Timeouts, obwohl die Anrufe an den Server gingen, den ich anrief. Das Problem in meinem Fall war, dass ich Expect auf application/json gesetzt hatte, wenn dies nicht der Server war.

1
bech

Dies ist keine Lösung, sondern nur eine Alternative: Heutzutage verwende ich fast nur noch WebClient anstelle von HttpWebRequest. Insbesondere WebClient.UploadString für POST und PUT und WebClient.DownloadString. Diese nehmen einfach Strings und geben sie zurück. Auf diese Weise muss ich mich nicht mit Streams-Objekten befassen, außer wenn ich eine WebException bekomme. Ich kann den Inhaltstyp auch mit WebClient.Headers ["Inhaltstyp"] festlegen, falls erforderlich. Die using-Anweisung macht das Leben auch einfacher, indem ich Dispose für mich anrufe.

Selten für die Leistung setze ich System.Net.ServicePointManager.DefaultConnectionLimit hoch und verwende stattdessen HttpClient mit seinen Async-Methoden für gleichzeitige Aufrufe.

So würde ich es jetzt machen

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

2 weitere POST Beispiele

// 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;
}

Fehlerbehandlung

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