2013-12-11 2 views
0

Сетевые сообщения TCP могут быть фрагментированы. Но фрагментированные сообщения трудно разобрать, особенно когда передаются типы данных, превышающие байты. Например, buffer.getLong() может потерпеть неудачу, если некоторые байты long, я ожидаю, вернутся во второй буфер.Можно ли удалить фрагментацию TCP с помощью трубки?

Анализ был бы намного проще, если бы несколько каналов могли быть объединены на лету. Поэтому я решил отправить все данные через java.nio.channels.Pipe.

// count total length 
int length = 0; 
foreach (Buffer buffer: buffers) { 
    length += buffer.remaining() 
} 

// write to pipe 
Pipe pipe = Pipe.open(); 
pipe.sink().write(buffers); 

// read back from pipe 
ByteBuffer complete = ByteBuffer.allocateDirect(length) 
if (pipe.source().read(complete) != length) { 
    System.out.println("Fragmented!") 
} 

Но это будет гарантировать полное заполнение буфера? Или Труба может снова ввести фрагментацию? Иными словами, будет ли тело состояния достигнуто?

ответ

0

Номер A Pipe предназначен для написания одной нити и чтения другой. Существует внутренний буфер всего 4k. Если вы напишете больше этого, вы остановитесь.

На самом деле они практически не используются, кроме как демонстрации.

Я не понимаю этого:

Например buffer.getLong() может потерпеть неудачу, если некоторые байты долго я ожидаю, в конечном итоге во втором буфере.

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

Я тоже не понимаю:

Синтаксический было бы гораздо проще, если несколько каналов может быть рекомбинируют на лету

Конечно вы имеете в виду несколько буферов, но основную идею должен иметь только один буфер в первую очередь.

+0

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

+0

Когда TCP фрагментирует сообщение, он может фрагментироваться после любого байта, также между «длинным», нажатым на поток. Вот почему 'getLong()' может завершиться ошибкой с 'BufferUnderflowException', если' long' не был полностью получен. – XZS

+0

@XZS Вы знаете, что такое сообщение максимальной длины, верно? Я не знаю, что ваш второй комментарий должен сказать мне, что я еще не знаю, за исключением того, что он доказывает, что вы не можете использовать два буфера, потому что «длинный» может быть разделен между ними. Основная проблема заключается в том, что вы должны продолжать чтение в том же буфере, пока не получите все, что вам нужно, а затем проанализируйте его. – EJP

1

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

Независимо от того, вы обрабатываете количество байтов (ByteBuffer) в качестве входного потока. Вы сообщаете JVM читать «остальную часть того, что находится в буфере», в ByteBuffer. Между тем вторая половина вашего long теперь находится внутри сетевого буфера. ByteBuffer, который вы сейчас пытаетесь прочитать, никогда не оставит этого long.

Рассмотрите возможность использования Scanner для чтения длин, он будет блокироваться до тех пор, пока не будет прочитан длинный.

Scanner scanner= new Scanner(socket.getChannel()); 
scanner.nextLong(); 

рассмотреть также с помощью DataInputStream читать длинные позиции, хотя я не могу сказать, если он блокирует до целого long считывается на основе документации.

DataInputStream dis = new DataInputStream(socket.InputStream); 
dis.readLong(); 

Если у вас есть контроль над сервером, рекомендуется использовать flush(), чтобы предотвратить ваши пакеты от получать в буфер и отправлен «фрагментарный» или ObjectOutputStream/ObjectInputStream как более удобный способ сделать IO.

+0

Он действительно блокируется до тех пор, пока не будет получено много времени. – EJP

+0

Не все ли смысл каналов и буферов заменить старый 'Stream'-API? Поэтому возврат к 'InputStream' кажется мне несколько устаревшим, особенно когда я хочу использовать асинхронные каналы с помощью' Selector'. Классы сериализации классов добавят предотвратимые накладные расходы по сравнению с отправкой необработанных базовых типов данных. – XZS

+0

Я добавил предложение использовать сканер, который можно использовать с каналами. – Charlie

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