2009-11-16 3 views
1

Я создал SocketChannel для удаленного сервера для отправки и получения сообщений на Tomcat. Чтобы получать сообщения с удаленного компьютера, я использовал поток, посвященный задаче (только этот поток будет читать из сокета, ничего больше).Java SocketChannel Eating my bytes

Когда некоторые байты принимаются в SocketChannel (я продолжаю опрос SocketChannel в режиме без блокировки для новых данных), я сначала прочитал 4 байта, чтобы получить длину следующего сообщения, а затем выделить и прочитать x байтов из SocketChannel, который затем декодируется и реконструируется в сообщение.

Ниже мой код для получающего потока:

@Override 
public void run() { 

    while (true) { //Don't exit thread 

     //Attempt to read the size of the incoming message 
     ByteBuffer buf = ByteBuffer.allocate(4); 

     int bytesread = 0; 
     try { 
      while (buf.remaining() > 0) { 
       bytesread = schannel.read(buf); 

       if (bytesread == -1) { //Socket was terminated 

       } 

       if (quitthread) break; 
      } 

     } catch (IOException ex) { 

     } 

     if (buf.remaining() == 0) { 
      //Read the header 
      byte[] header = buf.array(); 
      int msgsize = (0xFF & (int)header[0]) + ((0xFF & (int)header[1]) << 8) 
        + ((0xFF & (int)header[2]) << 16) + ((0xFF & (int)header[3]) << 24); 

      //Read the message coming from the pipeline 
      buf = ByteBuffer.allocate(msgsize); 
      try { 
       while (buf.remaining() > 0) { 
        bytesread = schannel.read(buf); 

        if (bytesread == -1) { //Socket was terminated 

        } 

        if (quitthread) break; 
       } 
      } catch (IOException ex) { 

      } 

      parent.recvMessage(buf.array()); 
     } 

     if (quitthread) { 
      break; 
     } 
    } 

} 

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

Что не так с кодом? Ни одна другая нить не читается из SocketChannel.

+0

Вместо того чтобы преобразовать буфер заголовка в 4 байта, вы можете просто вызвать getInt() на нем, чтобы прочитать длину. Если вы хотите рассматривать это как unsigned int, вы всегда можете отбрасывать длинный и побитовый AND с 0xFFFFFFFF. Кроме того, – Adamski

+0

Я, вероятно, могу, хотя я не был уверен, что java будет читать arr [4] byte [] как немного endian. В любом случае, я сомневаюсь, что это является причиной проблемы. Первая длина сообщения правильная, и я прочитал это количество байтов. Проверка сообщения чтения также не указала ошибок. Проблема только в том, что я пытаюсь прочитать следующие 4 байта для заголовка. – futureelite7

ответ

1

Ваш скобка выключены, код:

(0xFF & ((int)header[1] << 8)) 

, который всегда 0 (то же самое с < < 16 и < < 24), я думаю, вы имели в виду:

((0xFF & ((int)header[1])) << 8) 

Это приведет к чтению недостаточно байтов сообщений, что также приведет к несоответствию в синхронизации (в отличие от чтения слишком большого количества).

Редактировать: теперь вы исправили выше, я не вижу ничего плохого. Не могли бы вы рассказать нам о связи между длиной первого сообщения и точным количеством байтов, которые есть?

Основываясь на приведенном коде, моя единственная догадка заключается в том, что вы отредактировали какое-либо поведение из показанного образца, которое может повлиять на schannel, является ли ссылка, упоминаемая в другом месте?

Если линия:

ByteBuffer buf = ByteBuffer.allocate(4); 

бы вне while, что привело бы к поведению вы описываете, но в примере кода это не так.

+0

Похоже, что это проблема с расшифровкой заголовка. Теперь он отлично работает. – futureelite7

0

Я полагаю, что, когда вы говорите, что вы проводите опрос сокета в неблокирующем режиме, вы имеете в виду, что используете «стандартный» подход Selector.select()?

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

Рассмотрите возможность перекомпоновки кода, чтобы использовать подход с конечным автоматом. Например, я использовал это в прошлом, используя модель с тремя состояниями: IDLE, READ_MESSAGE_LENGTH и READ_MESSAGE.

+0

Поскольку у меня есть только один сокет на поток, я не знаю, нужно ли использовать Selector.Select(). В любом случае, -1 означает, что конец потока достигнут = должен уведомить родителя. На данный момент я просто не занимаюсь обработкой. Я попробую использовать простой Socket() и посмотреть, как это происходит. – futureelite7

Смежные вопросы