通过数据包从服务器发送 PNG 到客户端:索引越界异常

huangapple go评论71阅读模式
英文:

Send png via packet from server to client: indexOutOfBoundsException

问题

我在尝试从服务器发送PNG图像到客户端时,使用Netty出现了IndexOutOfBoundsException异常。错误发生在客户端的p.readBytes()这一行。

异常线程中的错误信息如下:

Exception in thread "LWJGL Application" java.lang.IndexOutOfBoundsException: readerIndex(97) + length(199852) exceeds writerIndex(185453): PooledUnsafeDirectByteBuf(ridx: 97, widx: 185453, cap: 185453)
	at io.netty.buffer.AbstractByteBuf.checkReadableBytes0(AbstractByteBuf.java:1395)
	at io.netty.buffer.AbstractByteBuf.checkReadableBytes(AbstractByteBuf.java:1389)
	at io.netty.buffer.AbstractByteBuf.readBytes(AbstractByteBuf.java:850)
	at io.netty.buffer.AbstractByteBuf.readBytes(AbstractByteBuf.java:858)
	at com.benberi.cadesim.client.codec.util.Packet.readBytes(Packet.java:79)
	at com.benberi.cadesim.client.packet.in.ListAllMapsPacket.execute(ListAllMapsPacket.java:29)

服务器端:

  1. 从服务器所在位置的特定文件夹获取图像
  2. 将图像转换为字节数组以便传输数据
  3. 使用数据包将数据发送到客户端

客户端:

  1. 接收图像的字节数组 //我认为问题出在这里
  2. 将字节数组转换为Pixmap(libgdx)
  3. 将Pixmap添加到Pixmap数组的特定索引位置
  4. 稍后处理Pixmap

服务器数据包部分:

	@Override
    public void encode() {
        setPacketLengthType(PacketLength.MEDIUM); //
        writeByte((byte)ServerConfiguration.getAvailableMaps().size());
        for (int i=0; i<ServerConfiguration.getAvailableMaps().size(); i++)
        {
        	String map = ServerConfiguration.getAvailableMaps().get(i).replace(".txt", "");
            writeByteString(map);
            String mapDir = String.format("maps/%s.png", map);
            try {
            	File imageFile = new File(mapDir);
				BufferedImage img = ImageIO.read(imageFile);
				writeInt((int)imageFile.length()); //写入图像大小
				ByteArrayOutputStream stream = new ByteArrayOutputStream();
				ImageIO.write(img, "png", stream);
				writeBytes(stream.toByteArray()); // 写入图像的字节数组
				stream.flush();
				stream = null;
			} catch (IOException e) {
				writeInt(0);
			}
        }
        setLength(getBuffer().readableBytes());
    }

客户端数据包部分:

    @Override
    public void execute(Packet p) {
    	size = (int)p.readByte();
    	context.pixmapArray = new Pixmap[size]; //创建与地图数量相符的Pixmap数组
    	int i=0;
    	while(i<size) {
    		context.getMaps().add((String)p.readByteString()); //将所有地图名称写入列表
    		Integer fileSize = p.readInt(); //读取PNG大小
    		if(fileSize == 0) {
    			context.pixmapArray[i] = null;
    		}else {
				Pixmap pixmap = new Pixmap(p.readBytes(fileSize), 0, fileSize); //字节数组转换为Pixmap
				context.pixmapArray[i] = pixmap; //将Pixmap添加到Pixmap数组中以供将来使用
    		}
    		i++;
    	}
    }

readBytes方法:

    public byte[] readBytes(int length) {
        byte[] bytes = new byte[length];
        this.dataBuffer.readBytes(bytes); //netty的ByteBuf 

        return bytes;
    }
英文:

I am getting an indexOutOfBoundException using netty when trying to send over a png from server to client. The error occurs on the line where it reads the bytes p.readBytes() in client.

Exception in thread &quot;LWJGL Application&quot; java.lang.IndexOutOfBoundsException: readerIndex(97) + length(199852) exceeds writerIndex(185453): PooledUnsafeDirectByteBuf(ridx: 97, widx: 185453, cap: 185453)
	at io.netty.buffer.AbstractByteBuf.checkReadableBytes0(AbstractByteBuf.java:1395)
	at io.netty.buffer.AbstractByteBuf.checkReadableBytes(AbstractByteBuf.java:1389)
	at io.netty.buffer.AbstractByteBuf.readBytes(AbstractByteBuf.java:850)
	at io.netty.buffer.AbstractByteBuf.readBytes(AbstractByteBuf.java:858)
	at com.benberi.cadesim.client.codec.util.Packet.readBytes(Packet.java:79)
	at com.benberi.cadesim.client.packet.in.ListAllMapsPacket.execute(ListAllMapsPacket.java:29)

Server Side:

  1. Get images in a specific folder where the server is located
  2. Convert image to a byte array for sending data
  3. Send data to the client using packet

Client-Side:

  1. Receive byte array of image // issue occurs here I believe
  2. Convert byte array to Pixmap (libgdx)
  3. Add pixmap to a specific index of Pixmap array
  4. Do something with pixmap later

Server Packet:

	@Override
    public void encode() {
        setPacketLengthType(PacketLength.MEDIUM); //
        writeByte((byte)ServerConfiguration.getAvailableMaps().size());
        for (int i=0; i&lt;ServerConfiguration.getAvailableMaps().size(); i++)
        {
        	String map = ServerConfiguration.getAvailableMaps().get(i).replace(&quot;.txt&quot;, &quot;&quot;);
            writeByteString(map);
            String mapDir = String.format(&quot;maps/%s.png&quot;, map);
            try {
            	File imageFile = new File(mapDir);
				BufferedImage img = ImageIO.read(imageFile);
				writeInt((int)imageFile.length()); //write size of image
				ByteArrayOutputStream stream = new ByteArrayOutputStream();
				ImageIO.write(img, &quot;png&quot;, stream);
				writeBytes(stream.toByteArray()); // write image; byte array
				stream.flush();
				stream = null;
			} catch (IOException e) {
//				e.printStackTrace();
				writeInt(0);
			}
        }
        setLength(getBuffer().readableBytes());
    }


Client Packet:

    @Override
    public void execute(Packet p) {
    	size = (int)p.readByte();
    	context.pixmapArray = new Pixmap[size]; //create Pixmap array with number of maps
    	int i=0;
    	while(i&lt;size) {
    		context.getMaps().add((String)p.readByteString()); //writes all map names to list
    		Integer fileSize = p.readInt(); //read size of png
    		if(fileSize == 0) {//incase image not found from server
    			context.pixmapArray[i] = null;
    		}else {
				Pixmap pixmap = new Pixmap(p.readBytes(fileSize), 0, fileSize); //byte[] to pixmap
				context.pixmapArray[i] = pixmap; //add pixmap to pixmap array for future use
    		}
    		i++;
    	}
    }

readBytes method:

    public byte[] readBytes(int length) {
        byte[] bytes = new byte[length];
        this.dataBuffer.readBytes(bytes); //netty ByteBuf 

        return bytes;
    }

答案1

得分: 2

问题在于您正在重新编码图像,从而更改文件大小。当您读取图像 ImageIO.read() 时,它可能会丢失元数据,然后当您使用 ImageIO.write() 写入时,不能保证与最初在磁盘上编码时的字节完全相同。我建议直接从文件中复制字节:

File imageFile = new File(mapDir);
byte[] fileContent = Files.readAllBytes(imageFile.toPath());
writeInt((int)imageFile.length()); //这应该与fileContent.length相同
writeBytes(fileContent); 

请注意,上述代码部分是代码示例,不需要翻译。

英文:

Your issue is that you are re-encoding the image, and thus changing the file size. When you read the image ImageIO.read() it may lose metadata, then when you write it with ImageIO.write() it is not guaranteed to encode it byte for byte the exact same as it was initially encoded on disk. I'd recommend just copying the bytes directly from the file:

File imageFile = new File(mapDir);
byte[] fileContent = Files.readAllBytes(imageFile.toPath());
writeInt((int)imageFile.length()); //this should be the same as fileContent.length
writeBytes(fileContent); 

huangapple
  • 本文由 发表于 2020年9月8日 23:50:32
  • 转载请务必保留本文链接:https://go.coder-hub.com/63797438.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定