ReadableByteChannel зависает при чтении (bytebuffer) - PullRequest
1 голос
/ 05 сентября 2011

Я работаю над Instant Messenger, используя Java 1.6.IM использует многопоточность - основной поток, получение и пинг.Для связи по tcp / ip я использовал SocketChannel.И, похоже, проблема в получении больших пакетов с сервера.Сервер вместо одного отправляет пару пакетов, и именно здесь начинается проблема.Каждые первые 8 байт указывают тип пакета и его размер.Вот как мне удалось прочитать:

public void run(){
    while(true){
        try{
        Headbuffer.clear();
        bytes = readChannel.read(Headbuffer); //ReadableByteChannel
        Headbuffer.flip();
        if(bytes != -1){
            int head = Headbuffer.getInt();
            int size = Headbuffer.getInt();
            System.out.println("received pkg: 0x" + Integer.toHexString(head)+" with size "+ size+" bytes);
            switch(head){
            case incoming.Pkg1: ReadWelcome(); break;
            case incoming.Pkg2: ReadLoginFail();break;
            case incoming.Pkg3: ReadLoginOk();break;
            case incoming.Pkg4: ReadUserList();break;
            case incoming.Pkg5: ReadUserData();break;
            case incoming.Pkg6: ReadMessage();break;
            case incoming.Pkg7: ReadTypingNotify();break;
            case incoming.Pkg8: ReadListStatus();break;
            case incoming.Pkg9: ChangeStatus();break;
            }
        }
        }catch(Exception e){
            e.printStackTrace();
        }
    }   
}

И во время тестов все было в порядке, пока я не вошел в свой аккаунт и не импортировал список друзей.Я отправляю запрос на сервер о статусах, и он отправляет мне обратно около 10 из 80 контактов.Итак, я придумал что-то вроде этого:

public synchronized void readInStatus(ByteBuffer headBuffer){

    byteArray.add(headBuffer); //Store every buffer in ArrayList
    int buddies = MainController.controler.getContacts().getSize();
    while(buddies>0){

          readStuff();
          readDescription();
        --buddies; 
    }       
}

и каждый readStuff () и readDescription () проверяют каждый размер параметра с оставшимися байтами в буфере:

 if(byteArray.get(current).remaining() >= 4){

      uin = byteArray.get(current).getInt();
      }else{
          byteArray.add(Receiver.receiver.read());
          current = current +1;
          uin = byteArray.get(current).getInt(); 
      }

и Receiver.receiver.read ():

public ByteBuffer read(){
    try {
        ByteBuffer bb = ByteBuffer.allocate(40000);
        bb.order(ByteOrder.LITTLE_ENDIAN);
        bytes = readChannel.read(bb);
        bb.flip();
        return bb;
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

Итак, приложение запускается, регистрируется и затем отправляет контакты.Сервер отправил мне обратно только часть моего списка.Но в методе readInStatus (ByteBuffer headBuffer) я пытаюсь форсировать оставшуюся часть списка.А теперь самое интересное - через некоторое время он попадает в Receiver.receiver.read () и в bytes = readChannel.read (bb) просто останавливается, и я не знаю, почему, без ошибок, нет ничего, даже через некоторое время, и я из идей.Я борюсь с этой целой неделей, и я не могу приблизиться к решению.Буду признателен за любые предложения.Спасибо.


Спасибо за ответ.Да, я использую блокировку SocketChannel, я пробовал неблокирование, но оно выходит из-под контроля и выходит из-под контроля, поэтому я пропустил эту идею.Насчет байтов, которые я ожидаю - это довольно странно, потому что он дает мне размер только один раз в голове, но размер первой части, а не всего пакета, другие части вообще не содержат байтов заголовка.Я не могу предсказать, сколько это будет байтов, причина в том, что описания имеют емкость 255 байтов.Именно поэтому я создал переменную buddies in: public synchronized void readInStatus(ByteBuffer headBuffer), которая в основном равна длине моего списка друзей, и перед чтением каждого поля я проверяю, достаточно ли осталось байтов, если его нет, я делаю чтение () .Но последнее поле перед описанием является целым с длиной входящего описания.Но невозможно определить, насколько длинным будет пакет, пока не будет выполнена некоторая обработка.@ Роберт, как ты думаешь, я должен попытаться снова переключиться на неблокирующий SocketChannel в этой ситуации?

Ответы [ 2 ]

1 голос
/ 05 сентября 2011

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

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

0 голосов
/ 07 сентября 2011

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

Вы можете либо: (i) попробуйте использовать неблокирующий SocketChannel, многократно читая, пока чтение не доставит 0 байтов (но остерегайтесь 0 байтов, не обязательно означает конец потока - это может означать прерывание) или (ii) если вам нужно использовать блокирующую версию, и вы знаете, сколько байтов вы ожидали от сервера, например, из заголовка, когда число байтов, оставшихся для чтения, меньше, чем buffer.capacity (), перемещает позицию и / или ограничивает буфер, чтобы оставить только необходимое пространство в буфере до чтения. Я работаю над этим решением сейчас. Если это работает для вас, пожалуйста, дайте мне знать!

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

...