HttpWebResponse ReadAsync тайм-аут - PullRequest
       8

HttpWebResponse ReadAsync тайм-аут

1 голос
/ 16 апреля 2019

Я пытаюсь прочитать поток HttpWebResponse, используя await / async:

    async static Task testHttpWebClientAsync()        
    {

        string url = "http://localhost/1.txt";
        HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
        req.Method = "GET";
        HttpWebResponse resp = (HttpWebResponse)await req.GetResponseAsync();            
        Stream stream = resp.GetResponseStream();
        stream.ReadTimeout = 10 * 1000;
        byte[] buffer = new byte[1024];
        while (await stream.ReadAsync(buffer, 0, buffer.Length) > 0)                           
        {
            //time out exception never thrown
        }
    }

Но это не работает, оно никогда не прерывается на ReadAsync.Для сравнения не асинхронная версия прекрасно работает с тем же тестовым сервером localhost:

    static void testHttpWebClient()
    {            
        string url = "http://localhost/1.txt";
        HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
        req.Method = "GET";
        HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
        Stream stream = resp.GetResponseStream();
        stream.ReadTimeout = 10 * 1000;
        byte[] buffer = new byte[1024];
        while (stream.Read(buffer, 0, buffer.Length) > 0)
        {
            //time out exception thrown here
        }
    }

Приведенный выше код протестирован в консольном приложении:

    static void Main(string[] args)
    {
        testHttpWebClient();
        MainAsync(args).GetAwaiter().GetResult();
    }
    async static Task MainAsync(string[] args)
    {
        await testHttpWebClientAsync();            
    }

Но это не относится кпроблема, действительно, я нахожу проблему в проекте WinForms и создаю консольный проект для проверки проблемы.

Для справки, код тестового сервера выглядит примерно так:

                    int c = 10;
                    byte[] ba = new byte[1024];
                    SendHeader(sHttpVersion, sMimeType,(int) ba.Length*c, " 200 OK", ref mySocket);
                    for (int k = 0; k < c; k++)
                    {
                        //set break point here
                        SendToBrowser(ba, ref mySocket);
                    }

Естьнесколько похожих тем на SO, но похоже, что ни одна из них не решает эту проблему.С точки зрения разработки API, очевидно, что нет причин, по которым ReadAsync () не срабатывает так же, как и Read (), ReadAsync должен наблюдать только за сокетом и событием внутреннего таймера, так работает Task.Delay ().Это не имеет ничего общего с CancellationToken и т. Д., Потому что нам не нужно ничего отменять, даже у ReadAsync есть версия, которая принимает CancellationToken.

Так что этот вопрос как для решения проблемы, так и почему ReadAsync нетайм-аут, как и ожидалось.

1 Ответ

1 голос
/ 18 апреля 2019

Асинхронные API на HttpWebRequest (и на WebClient, поскольку он использует HttpWebRequest для внутреннего использования) не используют тайм-ауты для внутреннего использования.Хотя я не могу действительно объяснить причину этого, это сделано по замыслу .

Это особенно очевидно в логике записи ConnectStream (используется внутри HttpWebResponse) :

if (async) {
    m_Connection.BeginMultipleWrite(buffers, m_WriteCallbackDelegate, asyncResult);
}
else {
    SafeSetSocketTimeout(SocketShutdown.Send);
    m_Connection.MultipleWrite(buffers);
}

SafeSetSocketTimeout - это метод, отвечающий за установку тайм-аута на базовом сокете.Как видите, он намеренно пропущен по асинхронному пути.Это то же самое для операций чтения, но код более запутанный, поэтому его труднее показать.

Следовательно, у вас действительно есть только два решения:

  1. Реализуйте время ожидания самостоятельно (обычнос таймером, который вызывает .Abort() на HttpWebRequest)
  2. Используйте HttpClient вместо
...