HttpClient, WebClient или HttpWebRequest отнема 90 секунди, за да прочете файл при първия опит, след което се изпълнява бързо

Имам приложение, което работи на компютър и изтегля файлове от уеб сървър, работещ във вградено устройство, което е свързано към компютъра чрез RNDIS през USB. RNDIS връзката изглежда като мрежов адаптер и ми позволява да изпълнявам TCP/IP връзки между компютъра и вграденото устройство.

Когато устройството е включено, мога да използвам уеб браузър, за да видя съдържанието на устройството в рамките на няколко секунди. Но когато приложението ми се изпълнява, първото извикване на HttpClient отнема 90 секунди, за да завърши. След това. HttpClient работи нормално. Ако затворя приложението си и го отворя отново, HttpClient вече работи правилно при първия опит. Забавянето от 90 секунди при стартиране на приложението е много забележимо за потребителите.

Този проблем изглежда се появява само на някои компютри, но не и на други. Опитах да използвам HttpClient, WebClient и HttpWebRequest, но резултатите са същите. Също така експериментирах с настройките в ServicePointManager и ServicePoint, след като прочетох други теми в StackOverflow, но те нямат ефект при този проблем.

Мога да демонстрирам проблема, като използвам само тези няколко реда код в конзолно приложение:

static async Task Main(string[] args)
{
    using (HttpClient httpClient = new HttpClient())
    {
        string url = "http://169.254.21.151/eventlog/2020-10-11.txt";
        for (; ; )
        {
            DateTime t1 = DateTime.Now;
            try
            {
                HttpResponseMessage httpResponseMessage = await httpClient.GetAsync(url);
                DateTime t2 = DateTime.Now;
                string content = await httpResponseMessage.Content.ReadAsStringAsync();
                DateTime t3 = DateTime.Now;
                TimeSpan dt1 = t2 - t1;
                TimeSpan dt2 = t3 - t2;
                Console.WriteLine($"Http Read {content.Length} bytes after {dt1.TotalMilliseconds:F0} ms + {dt2.TotalMilliseconds:F0} ms");
            }
            catch (HttpRequestException)
            {
                DateTime t2 = DateTime.Now;
                TimeSpan dt = t2 - t1;
                Console.WriteLine($"Http Failed to read after {dt.TotalMilliseconds:F0} ms");
            }
            await Task.Delay(1000);
        }
    }
}

Тествах кода по-горе така:

  1. Включете вграденото устройство
  2. Използвайте уеб браузър, за да тествате HTTP връзка и да изтеглите файл
  3. Стартирайте горния код

Резултатите показват, че първото извикване на httpClient.GetAsync(url) отнема 90 секунди, за да завърши. 90 секунди ми се струват подозрително число, което предполага, че някъде има настройка за изчакване, която все още не съм намерил. Измереното забавяне от 90 секунди е много последователно при множество тестове.

Http Read 1106623 bytes after 90449 ms + 0 ms
Http Read 1106623 bytes after 1080 ms + 0 ms
Http Read 1106623 bytes after 1070 ms + 0 ms
Http Read 1106623 bytes after 1110 ms + 0 ms

Има ли някъде настройка, която пропускам?


person snaddenm    schedule 12.10.2020    source източник


Отговори (1)


Намерих решение с груба сила. Мисля, че проблемът е някъде в ServicePointManager, така че моето решение е да избягвам използването на ServicePointManager, когато правя HTTP заявки към този хардуер, като използвам TcpClient вместо HttpClient и след това сам изграждам заглавките на HTTP заявките. Знам, че това идва с понижаване на производителността поради създаването и унищожаването на сокети за всяка заявка, но мисля, че това е приемливо в този случай, тъй като обемът на заявките към устройството е сравнително малък. Ще се радвам на предложение за по-елегантно решение.

Моят код, който е еквивалентен на кода във въпроса, е:

static void Main(string[] args)
{
    string host = "169.254.21.151";
    string file = "/eventlog/2020-10-11.txt";
    for (; ; )
    {
        DateTime t1 = DateTime.Now;
        try
        {
            string query = $"GET {file} HTTP/1.1\r\nConnection: close\r\nHost: {host}\r\n\r\n";
            byte[] queryBytes = Encoding.UTF8.GetBytes(query);
            using (TcpClient tcpClient = new TcpClient(host, 80))
            {
                tcpClient.GetStream().Write(queryBytes, 0, queryBytes.Length);
                using (StreamReader streamReader = new StreamReader(tcpClient.GetStream()))
                {
                    string response = streamReader.ReadToEnd();
                    int index = response.IndexOf("\r\n\r\n");
                    if (index > 0)
                    {
                        string headers = response.Substring(0, index);
                        string content = response.Substring(index + 4);
                        if (headers.Contains("HTTP/1.1 200 OK"))
                        {
                            DateTime t2 = DateTime.Now;
                            TimeSpan dt = t2 - t1;
                            Console.WriteLine($"Http Read {content.Length} bytes after {dt.TotalMilliseconds:F0} ms");
                        }
                    }
                }
            }
        }
        catch (Exception)
        {
            DateTime t2 = DateTime.Now;
            TimeSpan dt = t2 - t1;
            Console.WriteLine($"Http Failed to read after {dt.TotalMilliseconds:F0} ms");
        }
        Thread.Sleep(1000);
    }
}
person snaddenm    schedule 16.10.2020