MonoDroid: периодическая ошибка при чтении большой строки JSON из веб-службы - PullRequest
2 голосов
/ 17 ноября 2011

Я использую приведенный ниже код в потоке (показывающий диалог прогресса в пользовательском интерфейсе) для чтения строки json из веб-службы ASP.Net MVC. Данные могут быть от 1 до 4 МБ.

    public static class WebRequestEx
    {
        public static string ExecuteRequestReadToEnd(this WebRequest req)
        {
            var resp = req.GetResponse();
            using (var resps = resp.GetResponseStream())
            {
                StringBuilder sb = new StringBuilder();
                // read through the stream loading up the string builder
                using (var respRdr = new StreamReader(resps))
                {
                    //return respRdr.ReadToEnd();
                    while (!respRdr.EndOfStream)
                    {
                        sb.Append(respRdr.ReadLine());
                    }
                    return sb.ToString();
                }
            }
        }
}

Трассировка стека выглядит следующим образом.

I/mono    (15666): Stacktrace:
I/mono    (15666):
I/mono    (15666):   at System.Threading.WaitHandle.set_Handle (intptr) <0x0008b>
I/mono    (15666):   at System.Threading.EventWaitHandle..ctor (bool,System.Threading.EventResetMode) <0x00053>
I/mono    (15666):   at System.Threading.ManualResetEvent..ctor (bool) <0x0001f>
I/mono    (15666):   at (wrapper remoting-invoke-with-check) System.Threading.ManualResetEvent..ctor (bool) <0xffffffff>
I/mono    (15666):   at System.Net.WebAsyncResult.get_AsyncWaitHandle () <0x00073>
I/mono    (15666):   at System.Net.WebAsyncResult.WaitUntilComplete (int,bool) <0x00033>
I/mono    (15666):   at System.Net.WebConnectionStream.Read (byte[],int,int) <0x000b3>
I/mono    (15666):   at System.IO.StreamReader.ReadBuffer () <0x00047>
I/mono    (15666):   at System.IO.StreamReader.ReadLine () <0x0014b>
I/mono    (15666):   at System.WebRequestEx.ExecuteRequestReadToEnd (System.Net.WebRequest) <0x000ab>

Проблема в том, что это происходит не каждый раз и периодически. Это плохо, потому что приводит к зависанию всего приложения, и диалоговое окно прогресса застревает, когда пользователь не может ничего сделать с приложением. У меня есть блок try / catch в вызывающем коде, но, похоже, это исключение обходит его.

Раньше я использовал ReadToEnd, и он тоже периодически взрывался, поэтому я переключился на чтение построчно.

Трассировка стека не особенно полезна, поскольку что-то происходит в Readline ().

Мысли / советы / альтернативы?

Ответы [ 3 ]

0 голосов
/ 18 ноября 2011

Вам нужно увеличить значение maxArrayLength, потому что ваш возвращенный массив byte [] превышает размер 16348. Максимальное значение, которое вы можете установить для maxArrayLength, снова равно максимальному числу Int32, равному 2147483647. Измените этот параметр, и ваш веб-сервис сможет передавать большие данные между вашим веб-сервисом и клиентским приложением.

В Web.config создаются ссылки на веб-службы для клиентов.

"Превышена квота максимального размера сообщения для входящих сообщений (65536). Чтобы увеличить квоту, используйте свойство MaxReceivedMessageSize в соответствующем элементе привязки."

установите для максимального значения 2 ^ 31 - 1 или 2147483647 самого большого 32-разрядного числа.

Обратите внимание на TransferMode, который по умолчанию является Buffered.

  • Bufferred означает, что сообщения запроса и ответа будут буферизованы.

Другие перечисления для TransferMode:

  • Потоковый , что означает, что и запрос, и ответные сообщения будут передаваться в потоковом режиме, или
  • StreamedRequest , где запрос будет передаваться в потоковом режиме, пока ответное сообщение буферизовано, или
  • StreamedResponse , где сообщение-запрос будет буферизовано, а ответное сообщение - в потоковом режиме.

Для тех, кто не знает, Буферизованный означает, что передача будет держать все сообщение в буфере памяти все, пока передача не будет завершена. Потоковый означает, что буферизуются только заголовки сообщений, а тело сообщения будет представлено в виде потока.

Измените значение maxReceivedMessageSize с 65536 на 2147483647 - если вы оставите параметр TransferMode = "Buffered", мы можем получить еще одну ошибку:

Для TransferMode.Buffered значения MaxReceivedMessageSize и MaxBufferSize должны совпадать. Имя параметра: bindingElement

Оба атрибута требуют одинакового значения, MaxReceivedMessageSize больше, чем MaxBufferSize, если все сообщение буферизовано.

У вас есть два варианта: a) Измените размер MaxBufferSize на 2147483647. б) Сделайте TransferMode для: Streamed, StreamedRequest или StreamedResponse.

"Решение использовать буферизованные или потоковые передачи является локальным решением конечной точки для транспортных протоколов HTTP. Для транспортных протоколов HTTP режим передачи не распространяется через соединение или на прокси-серверы или других посредников. Настройка режима передачи не отражено в описании договора на обслуживание. После создания прокси для службы вы можете (это разрешено, но не обязательно) редактировать файл конфигурации для служб, предназначенных для использования с потоковыми передачами, для установки режима передачи. Для TCP и для именованных конвейеров, режим передачи распространяется как утверждение политики ". http://msdn.microsoft.com/en-us/library/system.servicemodel.transfermode.aspx

0 голосов
/ 19 ноября 2011

Если у вас нет особых требований к WebRequest, вы должны использовать WebClient.

Такие методы, как DownloadString и (еще лучше) DownloadStringAsync защитят вас от обработки битов ответа, когда легко выделить место для большого объема памяти и временныхстроки, которые повлияют на производительность вашего приложения.

0 голосов
/ 18 ноября 2011

Попробуйте использовать это:

    public static string ExecuteRequestReadToEnd(this WebRequest req)
    {
        var resp = req.GetResponse();
        using (var resps = resp.GetResponseStream())
        {
            var buffer = new byte[16 * 1024];
            using (var ms = new MemoryStream())
            {
                int read;
                while ((read = resps.Read(buffer, 0, buffer.Length)) > 0)
                {
                    ms.Write(buffer, 0, read);
                }
                var content = ms.ToArray();
                return Encoding.UTF8.GetString(content, 0, content.Length);
            }
        }
    }
...