HttpClient timeouts before the timeout specified

2020-03-27 05:16发布

问题:

I'm using HttpClient from WP8 and do a Post request. I know the call may take long time as I'm actually simulating slow network scenarios. Therefore I set the HttpClient.Timeout accordingly to 5 minutes.

However, I get a Timeout at around 60s. I believe the Timeout is not working. I believe there is an issue with this for WP as stated in this question: HttpClient Portable returns 404 notfound on WP8.

They use a workaround but that does not applies to my scenario. I do actually want to wait for long time.

My questions:

1) Is it a bug/issue of HttpClient for WP8 or I'm not setting it properly? 2) Do you think of a workaround still using HttpClient?

I've read that maybe HttpWebRequest is an option. However, I believe HttpClient should be ideal for this 'simple' scenario.

My code is simple:

private static async Task<HttpResponseMessage> PostAsync(Uri serverUri, HttpContent httpContent)
{
    var client = new HttpClient();
    client.Timeout = TimeSpan.FromMinutes(5);
    return await client.PostAsync(serverUri, httpContent).ConfigureAwait(false);
}

The server receives the request and while is processing it, the client aborts.

UPDATE: The HttpResponseMessage returned by HttpClient.PostAsyn is this "{StatusCode: 404, ReasonPhrase: '', Version: 0.0, Content: System.Net.Http.StreamContent, Headers: { Content-Length: 0 }}". As I said, the server is found and is receiving the data and processing it.

回答1:

After some search and some tests I've came to the conclusion that the problem is Windows Phone itself and that it has a 60 seconds timeout (irrespective of the HttpClient) and that cannot be changed to my knowledge. See http://social.msdn.microsoft.com/Forums/en-US/faf00a04-8a2e-4a64-b1c1-74c52cf685d3/httpwebrequest-60-seconds-timeout.

As I'm programming the server as well, I will try the advice by Darin Rousseau in the link provided above, specifically to send an OK and then do some more processing.


UPDATE: The problem seems to be the Windows Phone emulator as stated here: http://social.msdn.microsoft.com/forums/wpapps/en-us/6c114ae9-4dc1-4e1f-afb2-a6b9004bf0c6/httpclient-doesnt-work-on-windows-phone?forum=wpdevelop. In my experience the tcp connection times-out if it doesn't hear anything for 60s.


Therefore my solution is to use the Http header characters as a way of keep alive. The first line Http header response always starts with HTTP/1.0. So I send the characters one by one with a delay <60s between them. Of course, if the response gets ready, everything that is left is sent right away. This buys some time, for instance if using a delay of 50s per 9 character we get about 450s.

This is a project for my degree so I wouldn't recommend it for production.

By the way, I also tried with other characters instead the sub string of the header, for instance space character, but that results in a http protocol violation.

This is the main part of the code:

    private const string Header1 = @"HTTP/1.0 ";
    private int _keepAliveCounter = 0;
    private readonly object _sendingLock = new object();
    private bool _keepAliveDone = true;

    private void StartKeepAlive()
    {
        Task.Run(() => KeepAlive());
    }

    /// <summary>
    /// Keeps the connection alive sending the first characters of the http response with an interval.
    /// This is a hack for Windows Phone 8 that need reponses within 60s interval.
    /// </summary>
    private void KeepAlive()
    {
        try
        {
            _keepAliveDone = false;
            _keepAliveCounter = 0;
            while (!_keepAliveDone && _keepAliveCounter < Header1.Length)
            {
                Task.Delay(TimeSpan.FromSeconds(50)).Wait();
                lock (_sendingLock)
                {
                    if (!_keepAliveDone)
                    {
                        var sw = new StreamWriter(OutputStream);
                        sw.Write(Header1[_keepAliveCounter]);
                        Console.Out.WriteLine("Wrote keep alive char '{0}'", Header1[_keepAliveCounter]);
                        _keepAliveCounter++;
                        sw.Flush();
                    }
                }
            }
            _keepAliveCounter = 0;
             _keepAliveDone = true;
        }
        catch (Exception e)
        {
            // log the exception
            Console.Out.WriteLine("Error while sending keepalive: " + e.Message);
        }
    }

Then, the actual processing happens in a different thread.

Comments and critics are appreciated.



回答2:

It is possible that you are hitting the timeout of the network stream. You can change this by doing,

var handler = new WebRequestHandler();
handler.ReadWriteTimeout= 5 * 60 * 1000;
var client = new HttpClient(handler);
client.Timeout = TimeSpan.FromMinutes(5);
return await client.PostAsync(serverUri, httpContent).ConfigureAwait(false);

The default on the desktop OS is already 5mins. However, it is possible that on Windows Phone it has been reduced by default.