2016-03-31 3 views
0

Я посылаю поток данных через Java NIO канал, который в основном строят так:Java данных NiO канал не правильно читать

<int:size of packet><int:packet id><packet data> 

пакет данных заполнен различными типами данных в определенном порядке (идентификатор пакета указывает, как его анализировать). Когда я пытаюсь отправить некоторые данные локально, он работает нормально, но как только я попытаюсь запустить его на Windows Server 2012, он считывает недопустимое значение размера пакета, такое как отрицательные значения или слишком большие значения:

Выход клиента :

Sending packet: network.PacketChunkRequest (size: 24) 
Sending packet: network.PacketChunkRequest (size: 24) 
Sending packet: network.PacketChunkRequest (size: 24) 
Sending packet: network.PacketChunkRequest (size: 24) 
Sending packet: network.PacketChunkRequest (size: 24) 
Sending packet: network.PacketChunkRequest (size: 24) 
Sending packet: network.PacketChunkRequest (size: 24) 
Sending packet: network.PacketChunkRequest (size: 24) 
Sending packet: network.PacketChunkRequest (size: 24) 
2988 
-2032198748 
java.lang.IllegalArgumentException 
    at java.nio.ByteBuffer.allocate(Unknown Source) 
    at network.ClientSocket.run(ClientSocket.java:66) 
    at java.lang.Thread.run(Unknown Source) 

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

выход

Сервер:

Sending packet: network.PacketLoginAck (size: 16) 
Sending packet: network.PacketPlayerData (size: 951) 
Sending packet: network.PacketWorldInfo (size: 33) 
Received packet: network.PacketChunkRequest (size: 24) 
Sending packet: network.PacketChunkData (size: 2988)  // This is the first package that still worked 
Received packet: network.PacketChunkRequest (size: 24) 
Received packet: network.PacketChunkRequest (size: 24) 
Received packet: network.PacketChunkRequest (size: 24) 
Received packet: network.PacketChunkRequest (size: 24) 
Received packet: network.PacketChunkRequest (size: 24) 
Received packet: network.PacketChunkRequest (size: 24) 
Received packet: network.PacketChunkRequest (size: 24) 
Received packet: network.PacketChunkRequest (size: 24) 
Sending packet: network.PacketChunkData (size: 2518) 
Sending packet: network.PacketChunkData (size: 2741) 
Sending packet: network.PacketChunkData (size: 2966) 
Sending packet: network.PacketChunkData (size: 2449) 
Sending packet: network.PacketChunkData (size: 2769) 
Sending packet: network.PacketChunkData (size: 1862) 
Sending packet: network.PacketChunkData (size: 2526) 
Sending packet: network.PacketChunkData (size: 2353) 

PacketChunkRequest содержит два целых, две координаты, в то время как PacketChunkData также содержит эти два целых, плюс двоичных данных, который представляет собой INT, описывающий длину данных, за которым следует фактические данные.

Я посылаю мои данные, как это:

for(Packet p : packets) { 
    System.out.println("Sending packet: "+p.getClass().getName()+" (size: "+p.length()+")"); 

    ByteBuffer b = p.getBuffer(); 
    while (b.hasRemaining()) { 
     clientChannel.write(b); 
    } 
    b.clear(); 

    sentPackages.add(p); 
} 

Это код для считывания пакетов:

List<ByteBuffer> packets = new ArrayList<ByteBuffer>(); 

ByteBuffer bin = null; 

int packetLength = 0; 

while((bytesRead = channel.read(buffer)) > 0) { 
    buffer.flip(); 
    while(buffer.remaining() > 0) { 
     if(packetLength == 0) { 
      if(buffer.remaining() < 4) break; 

      packetLength = buffer.getInt(); 
      System.out.println(packetLength);  // This is the output of the length 
      bin = ByteBuffer.allocate(packetLength); // This is where the error happens 
     } 

     int readSize = Math.min(packetLength, buffer.remaining()); 

     buffer.limit(buffer.position() + readSize); 

     bin.put(buffer); 

     buffer.limit(bytesRead); 

     packetLength -= readSize; 

     if(packetLength == 0) { 
      bin.flip(); 

      packets.add(bin); 
     } 
    } 

    byte[] remaining = new byte[buffer.remaining()]; 
    for(int i = 0; buffer.remaining() > 0; i++) remaining[i] = buffer.get(); 

    buffer.clear(); 
    for(byte b : remaining) buffer.put(b); 
} 

Этот код пытается восстановить отдельные пакеты, которые были отправлены вместе, путем чтения столько же байтов в отдельные буферы, сколько размер пакета (первый int пакета).

Эта ошибка не всегда воспроизводимость, но продолжает идти, но не локально (по крайней мере, я никогда не сталкивались с этим)

ответ

0

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

Я бы спросил, почему вы используете NIO для этого вообще. Попробуйте с помощью DataInputStream, используя последовательно readInt() и readFully() до EOFException.

+0

Я использую NIO, потому что мне приходится обрабатывать несколько клиентов одновременно и только хочу реагировать на людей, которые действительно отправляют запросы, а не ждут одного человека, который может заблокировать все остальное. Итак, что именно вы подразумеваете, выйдя из синхронизации? Есть ли какой-нибудь документ, который я могу посмотреть? – user3088126

+0

Я имею в виду, что вы читаете часть пакета как слово длины. То, что я сказал. Простая концепция, не требуется документа. Вы используете NIO в режиме блокировки, который ничем не отличается от использования потоков. Или вы неправильно используете его в неблокирующем режиме, используя циклы чтения вместо 'select()' loop. В любом случае вы не достигаете цели, указанной в вашем комментарии: как только вы начинаете обслуживать клиента, все остальные ждут. – EJP

+0

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

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