Чтение байтов из сокета Java: получение ArrayIndexOutOfBounds - PullRequest
1 голос
/ 25 июля 2011

У меня очень странная проблема: у меня есть небольшая программа, которая читает байты из сокета;всякий раз, когда я отлаживаю, программа работает нормально;но каждый раз, когда я запускаю его (например, прямо), я получаю исключение ArrayIndexOutOfBounds.что дает?я читаю это слишком быстро для сокета?я что-то упустил?

вот main ():

public static void main(String[] args){

    TParser p = new TParser();

    p.init();

    p.readPacket(); 

    p.sendResponse();

    p.readPacket();

    p.sendResponse();

    p.shutdown();

}

Метод init - это то, где я создаю сокеты для чтения и записи;Следующий метод (readPacket) - это место, где начинаются проблемы;я прочитал весь буфер в частный байтовый массив, чтобы я мог свободно манипулировать данными;например, в зависимости от некоторых байтов данных я устанавливаю некоторые свойства:

public void readPacket(){       

    System.out.println("readPacket");
    readInternalPacket();
    setPacketInfo();
}

private void readInternalPacket(){
    System.out.println("readInternalPacket");
    try {           
        int available=dataIN.available();           
        packet= new byte[available];    
        dataIN.read(packet,0,available);

        dataPacketSize=available;

    }
    catch (Exception e) {
        e.printStackTrace();
    }
}


private void setPacketInfo() {

    System.out.println("setPacketInfo");
    System.out.println("packetLen: " +dataPacketSize);

    byte[] pkt= new byte[2];
    pkt[0]= packet[0];
    pkt[1]= packet[1];

    String type= toHex(pkt);
    System.out.println("packet type: "+type);
    if(type.equalsIgnoreCase("000F")){
        recordCount=0;
        packetIterator=0;
        packetType=Constants.PacketType.ACKPacket;
        readIMEI();
        validateDevice();

    }
}

Строка, где она разбивается, является строкой

pkt [1] = packet [1];(setPacketInfo)

означает, что он имеет только 1 байт в то время ... но как это может быть, если я отлаживаю его, он работает отлично?Есть ли какая-то проверка работоспособности, которую я должен сделать на розетке?(dataIN имеет тип DataInputStream)

я должен поместить методы в отдельные потоки?Я перебирал это снова и снова, даже заменял свои модули памяти (когда у меня появились странные идеи по этому поводу)

... пожалуйста, помогите мне.

Ответы [ 5 ]

4 голосов
/ 25 июля 2011

Я не знаю окружающий код, особенно класс dataIN, но я думаю, что ваш код делает это:

int available=dataIN.available(); вообще не ждет данных, просто возвращает, что доступно 0 байт

так что ваш массив имеет размер 0, а затем вы делаете:

pkt[0]= packet[0]; pkt[1]= packet[1];, что выходит за пределы.

Я бы порекомендовал вам хотя бы цикл до1012 * возвращает ожидаемый 2, но я не могу быть уверен, что это правильный (*) или правильный (**) способ сделать это, потому что я не знаю реализацию класса dataIN.

Примечания: (*) неверно, если available() возможно, например, вернуть 2 байта отдельно.(**) это неправильный способ сделать это, если dataIN сам предоставляет методы, которые ждут.

1 голос
/ 25 июля 2011

Может быть, чтение данных из сокета является асинхронным процессом, и setPacketInfo () вызывается до того, как ваш пакет [] будет полностью заполнен? Если это так, возможно, он отлично работает при отладке, но ужасно, когда он действительно использует сокеты на разных машинах.

Вы можете добавить код в метод setPacketInfo () для проверки длины переменной package [].

 byte[] pkt= new byte[packet.length];
 for(int x = 0; x < packet.length; x++)
 {
      pkt[x]= packet[x];
 }

не совсем уверен, почему вы даже копируете переменную package [] в pkt []?

0 голосов
/ 25 июля 2011

Вы используете пакетно-ориентированный протокол на потоково-ориентированном уровне без передачи реальной длины пакета. из-за фрагментации размер полученных данных может быть меньше отправленного вами пакета.

Поэтому я настоятельно рекомендую отправить размер пакета данных перед отправкой фактического пакета. На стороне получателя вы можете использовать DataInputStream и использовать блокировку чтения для обнаружения входящего пакета:

private void readInternalPacket() {
    System.out.println("readInternalPacket");
    try {
        int packetSize = dataIN.readInt();
        packet = new byte[packetSize];
        dataIN.read(packet, 0, packetSize);
        dataPacketSize = packetSize;
    } catch (Exception e) {
        e.printStackTrace();
    }
}

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

0 голосов
/ 25 июля 2011

Добавить к ответу от @eznme.Вам нужно читать из вашего основного потока, пока не будет больше ожидающих данных.Для этого может потребоваться одно или несколько чтений, но конец потока будет указан, когда метод available вернет 0. Я бы рекомендовал использовать Apache IOUtils , чтобы «скопировать» входной поток в ByteArrayOutputStream , затем получим из этого массив byte [].

В вашем методе setPacketInfo вы должны проверить длину буфера данных перед получением байтов заголовка протокола:

byte[] pkt= new byte[2];
if((packet != null) && (packet.length >= 2)) {
    pkt[0]= packet[0];
    pkt[1]= packet[1];
    // ...
}

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

0 голосов
/ 25 июля 2011

Вы никогда не должны полагаться на dataIN.available(), а dataIN.read(packet,0,available); возвращает целое число, которое говорит, сколько байтов вы получили. Это не всегда то же значение, что указано в доступном, и оно также может быть меньше размера буфера.

Вот как вы должны читать:

byte[] packet = new byte[1024];  //
dataPacketSize = dataIN.read(packet,0,packet.length);

Вам также следует обернуть DataInputStream в BufferedInputStream и позаботиться о случае, когда вы получите менее 2 байтов, чтобы не пытаться обрабатывать байты, которые вы не получили. *

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...