Чтение большого объема XML-данных из сокета и анализ на лету - PullRequest
2 голосов
/ 16 августа 2011

Я работаю на клиенте Android, который читает поток данных XML с моего сервера Java через сокет TCP.Сервер отправляет символ '\ n' в качестве разделителя между последовательными ответами.Ниже приведена модель реализации.

<response1>
   <datas>
      <data>
           .....
           .....
      </data>
      <data>
           .....
           .....
      </data>
      ........
      ........
   </datas>
</response1>\n    <--- \n acts as delimiter ---/> 
<response2>

   <datas>
      <data>
           .....
           .....
      </data>
      <data>
           .....
           .....
      </data>
      ........
      ........
   </datas>
</response2>\n

Хорошо, я надеюсь, что структура теперь ясна.Этот ответ передается с сервера, сжатого zlib.Поэтому я должен сначала надуть все, что я читаю с сервера, отдельно по ответу, используя разделитель и анализ.И Я использую SAX для анализа моего XML

Теперь моя главная проблема в том, что xml-ответ от сервера может быть очень большим (может быть в диапазоне от 3 до 4 МБ).Поэтому

  • для разделения ответов на основе разделителя (\ n) я должен использовать stringBuilder для хранения блоков ответов, так как он читает из сокета, а на некоторых телефонах StringBuilder не можетхранить строки в диапазоне MegaBytes.Это дает OutOfMemory исключение, и из таких потоков, как this Я узнал, что хранить большие строки (даже на временной основе) не очень хорошая идея.

  • Далее я попытался передать inflatorReadStream (который, в свою очередь, получает данные из входного потока сокетов) в качестве входного потока анализатора SAX (не пытаясь самостоятельно разделить xml и полагаясь на способность SAX найти конец документа на основепо тегам).На этот раз один ответ успешно анализируется, но затем при нахождении разделителя '\ n' SAX выбрасывает ExpatParserParseException , говоря мусор после элемента документа .

  • После перехватачто ExpatParserParseException Я попытался прочитать снова, но после выдачи исключения SAX Parser закрывает поток, поэтому, когда я пытаюсь снова прочитать / проанализировать, он дает IOException , говорящий, что входной поток закрыт.

Ниже приведен фрагмент кода того, что я сделал (для ясности удалены все несвязанные блоки try catch).

private Socket clientSocket     =   null;
DataInputStream readStream      =   null;
DataOutputStream writeStream        =   null;
private StringBuilder incompleteResponse    =   null;
private AppContext  context     =   null;


public boolean connectToHost(String ipAddress, int port,AppContext myContext){
        context                     =   myContext;
        website                     =   site;
        InetAddress serverAddr          =   null;

    serverAddr                      =   InetAddress.getByName(website.mIpAddress);

    clientSocket                    =   new Socket(serverAddr, port);

    //If connected create a read and write Stream objects..
    readStream   =  new DataInputStream(new InflaterInputStream(clientSocket.getInputStream()));
    writeStream             =   new DataOutputStream(clientSocket.getOutputStream());

    Thread readThread = new Thread(){
            @Override
            public void run(){                              
            ReadFromSocket();                   
        }
    };
    readThread.start();     
    return true;
}


public void ReadFromSocket(){
   while(true){
       InputSource xmlInputSource = new InputSource(readStream);
       SAXParserFactory spf =   SAXParserFactory.newInstance();
       SAXParser sp =   null;
       XMLReader xr =   null;
       try{
           sp   = spf.newSAXParser();
       xr   = sp.getXMLReader();
       ParseHandler xmlHandler =    new ParseHandler(context.getSiteListArray().indexOf(website), context);
       xr.setContentHandler(xmlHandler);
       xr.parse(xmlInputSource);
   //  postSuccessfullParsingNotification();
       }catch(SAXException e){
           e.printStackTrace();
           postSuccessfullParsingNotification();
       }catch(ParserConfigurationException e){
           e.printStackTrace();
           postSocketDisconnectionBroadcast();
           break;
       }catch (IOException e){
           postSocketDisconnectionBroadcast();
           e.printStackTrace();
           e.toString();
           break;
       }catch (Exception e){
           postSocketDisconnectionBroadcast();
           e.printStackTrace();
           break;
       }
    }
}

А теперь мои вопросы

  1. Есть ли способ заставить SAX Parser игнорировать ненужные символы после ответа xml, а не генерировать исключение и закрывать поток ..
  2. Если нет, есть ли способ избежать ошибки нехватки памяти в stringBuilder.Честно говоря, я не исключаю положительного ответа на этот вопрос.Есть обходной путь?

Ответы [ 2 ]

2 голосов
/ 16 августа 2011
  1. Вы можете использовать оболочку для читателя или потока, который вы передаете фильтру, который обнаруживает новую строку, а затем закрывает синтаксический анализатор и запускает новый анализатор, продолжающий поток: ваш поток НЕ является допустимым XML, и вы выиграли не сможет разобрать, как вы в настоящее время реализовали. Взгляните на http://commons.apache.org/io/api-release/org/apache/commons/io/input/CloseShieldInputStream.html.
  2. номер
1 голос
/ 17 августа 2011

Если ваш синтаксический анализатор SAX поддерживает push-модель (когда вы помещаете в нее фрагменты необработанных данных и он запускает события при анализе необработанных данных), вы можете просто вставить свой собственный исходный тег XML в начале сеанса SAX. , Это станет тэгом документа верхнего уровня, тогда вы можете отправлять ответы по мере их получения, и они будут тегами второго уровня, если говорить о SAX. Таким образом, вы можете отправить несколько ответов в одном сеансе SAX, а затем в событии OnTagOpen (или где бы вы ни использовали) вы узнаете, когда начинается новый ответ, когда вы обнаружите его имя тега на уровне 1.

...