简体   繁体   English

Java Nio通道数据未正确读取

[英]Java nio channel data is not read correctly

I send a stream of data through a java nio channel, which is basically build like this: 我通过java nio通道发送数据流,该通道基本上是这样构建的:

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

packet data is filled with different data types in a specific order (the packet id tells how to parse it). 数据包数据以特定顺序填充了不同的数据类型(数据包ID指示了如何解析数据包)。 When I try to send some data locally it works just fine, but as soon as I try to run it on a Windows Server 2012 it reads an invalid packet size value such as negative values or far too big values: 当我尝试在本地发送一些数据时,效果很好,但是当我尝试在Windows Server 2012上运行它时,它会读取无效的数据包大小值,例如负值或太大的值:

Client output: 客户输出:

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)

The numbers at the end are the read package sizes, where you can notice that one ridiculous number which leads to an exception while trying to prepare a buffer for it. 末尾的数字是读取的包大小,您可以在其中注意到一个荒谬的数字,它在尝试为其准备缓冲区时会导致异常。

Server output: 服务器输出:

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)

a PacketChunkRequest contains two ints, two coordinates, while the PacketChunkData also contains these two ints, plus the binary data, which is a int describing the length of the data followed by the actual data. PacketChunkRequest包含两个int,两个坐标,而PacketChunkData也包含这两个int和二进制数据,二进制数据是一个int,用于描述数据的长度和实际数据。

I send my data like this: 我这样发送数据:

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);
}

This is the code for reading the packets: 这是读取数据包的代码:

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);
}

This code tries to reconstruct the individual packages, that were sent together, by reading as much bytes into single buffers as the packet size (the first int of a package). 此代码通过将与包大小(包的第一个int)一样多的字节读取到单个缓冲区中来尝试重构一起发送的单个包。

This error isn't always reproducable but keeps coming, but not locally (at least I never encountered it) 该错误并不总是可重现的,但是会不断出现,但不是局部的(至少我从未遇到过)

You're getting out of sync with the sender. 您与发件人不同步。 I haven't analysed your receiving code in detail, but clearly it doesn't handle every possible case and correctly read a packet length as an integer. 我没有详细分析您的接收代码,但是显然它不能处理所有可能的情况,并且不能正确读取一个整数的数据包长度。 You're reading part of a pacet as the length word. 您正在读取部分长音作为长度词。 You need to reconsider your logic. 您需要重新考虑您的逻辑。

I would question why you're using NIO for this at all. 我会问为什么您要为此全部使用NIO。 Try it with a DataInputStream , using successively readInt() and readFully() until EOFException is thrown. 尝试使用DataInputStream ,依次使用readInt()readFully()直到抛出EOFException为止。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM