2011-09-05 6 views
1

Im работает с мессенджером, используя java 1.6. IM использует многопоточность - основной поток, прием и пинг. Для связи tcp/ip я использовал SocketChannel. И кажется, что есть проблема с получением больших пакетов с сервера. Сервер вместо одного отправляет пару пакетов, и там проблема начинается. Каждые первые 8 байтов говорят, что такое тип пакета и насколько он большой. Это, как мне удалось читать:ReadableByteChannel висит на чтении (bytebuffer)

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) он просто останавливается, и я не знаю, почему, никаких ошибок нет ничего даже после некоторых времени и Im из идей. Im сражается с этой целую неделю и я не получаю где-нибудь около решения. Буду признателен за любые предложения. Благодарю.


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

ответ

1

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

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

+0

Спасибо за быстрый ответ. Однако я думаю, что я делаю это правильно, и все на месте, как предполагается. Я проверял размер буфера перед отправкой и его правильность. Для отправки требуется только две переменные int и byte, поэтому это довольно простая математика. Контакты хранятся в Hashtable, и я повторяю сверху вниз. –

+0

Проблема - это предел принимающего буфера. Вы очищаете его, когда вы должны установить его размер «размер», прочитать, очистить, затем установить ограничение на буфер на основе размера, а затем снова прочитать то же самое для данных. Я инженер сети BTW. –

0

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

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

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

+0

Невозможно получить способ (ii) работать в данный момент. Все еще блокируется, даже когда я переместил позицию так, чтобы доступные доступные байты не превышали buffer.remaining() –