Сетевой ввод-вывод зависает на BlackBerry OS 5 - PullRequest
3 голосов
/ 08 июля 2010

У меня есть некоторые проблемы с моим кодом сетевого ввода-вывода в OS 5 BlackBerry.

У меня постоянно возникают спорадические зависания и, в конечном итоге, исключения таймаута TCP во время операций ввода-вывода.

IЯ использую сетевые API-интерфейсы 5.0 для установления соединения, которое работает безупречно каждый раз.

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

Уведомление о завершении выполняется через интерфейс делегата, который передается, когда запрос находится в очереди.

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

Примечания:
Мой HttpRequestсобственный класс, в котором хранятся данные о запросе.
MutableData - это мой собственный класс, в котором хранятся данные, которые читаются.
BUFFER_SIZE = 2048

HttpConnection getConnectionForRequest(final HttpRequest inRequest) {
    final String url = inRequest.getURL();
    final int[] availableTransportTypes = 
        TransportInfo.getAvailableTransportTypes();
    final ConnectionFactory connectionFactory = new ConnectionFactory();
    connectionFactory.setPreferredTransportTypes(availableTransportTypes);
    connectionFactory.setConnectionMode(ConnectionFactory.ACCESS_READ);
    final ConnectionDescriptor connectionDescriptor = 
        connectionFactory.getConnection(url);
    HttpConnection connection = null;
    if (connectionDescriptor != null) {
        connection = (HttpConnection) connectionDescriptor.getConnection();
    }
    return connection;
}

public void run() {
    while (isRunning()) {
        // This blocks waiting on a request to appear in the queue.
        final HttpRequest request = waitForRequest(); 
        final HttpConnection connection = getConnectionForRequest(request);
        final MutableData data = new MutableData();
        final InputStream inputStream = connection.openInputStream();
        final byte[] readBuffer = new byte[BUFFER_SIZE];
        int chunkSize;
        // *** The following read call sporadically hangs and eventually throws
        //  a TCP timeout exception.
        while((chunkSize = inputStream.read(readBuffer, 0, BUFFER_SIZE)) != -1) {
            data.appendData(readBuffer, 0, chunkSize);
        }
        mDelegate.receivedDataForRequest(request, data);
    }
}

Когда он зависает, он всегда со временем выдает таймаут TCPошибка примерно через 30 секунд или около того.Если бы это происходило время от времени, я бы просто записал это на счет обычной перегрузки сети, но это случается достаточно часто, чтобы указать на более глубокую проблему.

Редактировать:

Это происходит на различныхсимуляторы и 2 физических устройства у меня есть.Симуляторы, которые я пробовал: ...

  • Буря 9550
  • Тур 9630
  • Жирный 9000
  • Жемчужина 9100
  • Кривая 8530

У меня есть устройства Curve 8530 и Storm 9550, и это также происходит с обоими из них.

Любая помощь будет принята.

Ответы [ 4 ]

0 голосов
/ 19 декабря 2010

Я думаю, что есть ошибка в реализации read(byte[], int, int) во всех операционных системах BlackBerry, с которыми я работал - от 4,5 до 6,0.
Я написал адаптер для InputStream, который превращает read(byte[], int, int) в один вызовread() и это решило проблему с зависанием потока в приложении, над которым я работаю.

Если вы читаете спецификацию RIM для read(byte[], int, int), она говорит:

Чтение (b, off, len) метод для класса InputStream просто вызывает метод read () несколько раз.Если первый такой вызов приводит к IOException, это исключение возвращается из вызова метода read (b, off, len).Если любой последующий вызов read () приводит к IOException, исключение перехватывается и обрабатывается так, как если бы это был конец файла;байты, считанные до этой точки, сохраняются в b, и возвращается число байтов, прочитанных до возникновения исключения.Подклассы призваны обеспечить более эффективную реализацию этого метода.

Я написал свою собственную версию, следуя этой спецификации, и столкнулся с той же проблемой.Я считаю, что проблема заключается в том, что метод должен возвращаться без блокировки, как только некоторые данные доступны.Единственный способ сделать это - использовать available(), чтобы увидеть, сколько байтов можно прочитать без блокировки.Поскольку в документации RIM не упоминается использование available(), я думаю, что он просто вызывает read(), пока буфер не заполнится или read () не вернет -1.Это может занять много времени, если ваши данные поступают небольшими пакетами.И если это «долгое время» выходит за пределы тайм-аута соединения, соединение просто умирает.

Вот код, который я использовал, который решил проблему зависания соединения:

public int read(byte[] bts, int st, int len) throws IOException {
    if(len == 0) {
        return 0;
    }
    int readByte = this.read();
    if(readByte == -1) {
        return 0;
    }
    bts[st] = (byte)readByte;
    return 1;
}
0 голосов
/ 09 июля 2010

Можете ли вы добавить запись в журнал для отображения типа транспорта, который устройство выбирает для каждого соединения?Возможно, это случай, когда API выбора транспорта выбирает транспорт, который, по его мнению, будет работать, хотя на самом деле это не так.

0 голосов
/ 09 июля 2010

В другом месте было предложено установить детектор останова в моем потоке ввода-вывода в сети, а при обнаружении останова прервать поток и перезапустить запрос. Я делаю это, запуская таймер перед тем, как начать запрос, и когда я читаю каждый кусок данных, я сбрасываю таймер. Если таймер истекает до того, как я смогу прочитать чанк, я предполагаю, что сеть остановилась, и я прерываю поток и начинаю заново по этому запросу.

Я сделал это, и это улучшило ситуацию, по крайней мере, уменьшив задержку, которую я должен ждать, прежде чем я смогу продолжить запрос, поскольку мне не нужно ждать тайм-аута TCP, который может занять очень много времени.

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

Это очень странная проблема, и я не совсем доволен решением для обнаружения останова. Кажется, что это просто маскировка проблемы, но она позволяет мне немного разобраться с большими задержками, которые я получил.

0 голосов
/ 09 июля 2010

Возможно, вы захотите попробовать метод Available () .Даже если вы сериализуете данные в одном фоновом потоке, похоже, что запрос создан в основном потоке.Вы можете столкнуться с какой-то странной расой.

...