Idea for a packet system

Now what I was thinking of doing was having a byte[] buffer, and then filling it with ints floats, and booleans somehow.

I figure I could read the next variable by saying “buffer << sizeOf(int)” or something. would this work well? and does anyone have any idea how I could get the data to go IN the buffer?

that’s what buffers do already.

is this what you’re looking for? (very simple, just added the methods you mentioned)

public class Buffer {

	public Buffer() {
		this(128);
	}

	public Buffer(int size) {
		this.size = size;
		buffer = new byte[size];
	}

	private final int size;

	private byte[] buffer;
	private int pos, limit;

	public boolean getBoolean() {
		return buffer[pos++] == 1;
	}

	public float getFloat() {
		return Float.intBitsToFloat(getInt());
	}

	public int getInt() {
		return (buffer[pos++] & 0xFF) << 24 | (buffer[pos++] & 0xFF) << 16 | (buffer[pos++] & 0xFF) << 8 | (buffer[pos++] & 0xFF);
	}

	public int limit() {
		return limit;
	}

	public int position() {
		return pos;
	}

	public int remaining() {
		return limit - pos;
	}

	public Buffer putBoolean(boolean bool) {
		ensure(1);
		buffer[pos++] = (byte) (bool ? 1 : 0);
		return this;
	}

	public Buffer putFloat(float f) {
		return putInt(Float.floatToRawIntBits(f));
	}

	public Buffer putInt(int i) {
		ensure(4);
		buffer[pos++] = (byte) (i >> 24);
		buffer[pos++] = (byte) (i >> 16);
		buffer[pos++] = (byte) (i >> 8);
		buffer[pos++] = (byte) i;
		return this;
	}

	public void clear() {
		pos = limit = 0;
		buffer = new byte[size];
	}

	private void ensure(int size) {
		int required = pos + size;
		if (buffer.length <= required) {
			byte[] temp = new byte[required + 64];
			System.arraycopy(buffer, 0, temp, 0, buffer.length);
			buffer = temp;
		}
	}

	public void flip() {
		limit = pos;
		pos = 0;
	}

	public void rewind() {
		pos = 0;
	}

}

Why re-invent the wheel? See DataInputStream and DataOutputStream.

There’s a huge difference between writing to/reading from a stream and writing/reading buffered data.

You should have compared this to a ByteBuffer not DataInputStream/DataOutputStream.

Even then, this still has speed optimizations.

So it’s not reinventing the wheel.

What’s the point of the variables “size” and “limit” in your code? They are never used…?

easier to use and/or more easy to manage



ByteArrayOutputStream bos=new ByteArrayOutputStream();
DataOutputStream dos=new DataOutputStream(bos);
dos.writeSomething();
bytes=bos.toByteArray()
>> send packet id
>> send packet size
>>send size bytes over network

and


>> read paket id
>> read packet size
>>receive size bytes from network
ByteArrayInputStream bais=new ByteArrayInputStream(bytes);
DataInputStream dis =new DataInputStream(bais);
dis.readSomething();

Also in a base/superclass Packet you should have at least

int packetId 
int packetSize

and some kind of packets identification :

public final static int PACKET_PING = 0x1;
public final static int PACKET_START = 0x2;
etc...

your stream then should look something like :
PACKET_ID,PACKET_SIZE,PACKET_CONTENT,PACKET_ID,PACKET_SIZE,PACKET_CONTENT,PACKET_ID,PACKET_SIZE,PACKET_CONTENT, etc…

size is used to reset the internal byte array to the original size after clearing (check the clear() method).

and limit is important, it indicates at what position the buffer was at after it was flipped. it let’s you know how much data is available to be read (check the remaining() method)

I have seen the flip() also used in the NIO package and I could never understand its use. All I see here is the same as rewind except for setting limit to pos. That’s it or am I missing something…?

Yes the same thing happened to me when I first saw the flip() method, I had no idea really what it did. But I eventually figured it out after asking people :wink:

Basically, it flips from writing to reading.

So you have this example code:

Buffer buffer = new Buffer();
buffer.putInt(100); // Integer consists of 4 bytes, therefore the position of the internal byte array is now at 4
System.out.println(buffer.getInt()); // Begin reading from the 4th byte onward, because the position is still at 4 after writing. You are not reading at the correct position this is INCORRECT!

This will not print 100. Why? Because the buffer’s position is not set to the beginning.

This is what it should be:

Buffer buffer = new Buffer();
buffer.putInt(100); // Integer consists of 4 bytes, therefore the position of the internal byte array is now at 4
buffer.flip(); // Set the position back to 0. Set limit to 4 (old position)
System.out.println(buffer.getInt()); // Before this is read, the remainder() is 4. After this is read the remainder is 0, because the position is increased by 4 since you are reading an integer (4 bytes). You begin reading from 0 because the position was reset to 0 (which is where you wrote the integer 100, the first 4 bytes in the byte array)

Once you flip it, the position is set back to 0 (so you can start reading from the beginning instead of where the position was left off when you wrote the integer 100) and the limit becomes the position.

Then you can use the remainder() method to see how many more bytes can be read. Without flipping, there is no limit, and therefore the remainder will not be accurate.

Oh so with these buffers, you can only read after you have written everything you needed? If you call putInt(100) after you call flip(), it will overwrite the first 4 values of the array? Why can’t the buffer just be a FIFO queue?

It’s not realistic to use any such implementations like a queue, one of the main priorities of a buffer is to be fast as possible. And it would be much more complicated…

it wont be slower, you can use things like two “rotative index”, one fore reading and another for writing, but in this case care must be taken and you have to ensure flow control.

pseudo code :

byte readByte()
{
 return bytes[readIndex++%bytes.length];
}

void writeByte(byte ) throw OverflowException
{
 if(writeIndex==readIndex)
  throw new OverflowException();
 bytes[writeIndex++%bytes.length]=byte;
}

once you can read/write one byte you can implement all other method writeFloat/writeInt etc…

@DzzD
My thoughts exactly :slight_smile:

You should probably stay away from wrapping around and overwriting data (unless you’re positive you will never read over a certain amount like you said)

And I’m not sure what kind of design you will be using where you need to read and write at the same time…

I have created my own “Packet” system that uses ObjectInputStream and ObjectOutputStream. I’d say it’s quite efficient and easy :slight_smile:

I can think of three possibilities :
1 - hang on read until data are available
2 - throw NoDataAvailableException
3 - return a special value indicating that no data is available (this one could be weird to manage)

Well 2 and 3 can be done with any standard Buffer (after all, you know how much is remaining).

As for 1, it’s true that with this implementation that can be done… But I really don’t see a purpose for not reading fully and writing additional data :stuck_out_tongue:

For the games I’ve written, I process all data when it’s available, I don’t wait for more things to be written.

[quote]But I really don’t see a purpose for not reading fully and writing additional data :stuck_out_tongue:
[/quote]
having a thread that do the network job asynchronously and give you only fullyfilled packets is very practice & efficient and hanging on no data avaialble is the best for such architecture (inded you have to make it thread safe), in your mainloop you can do things like :

if(networkManager.isPacketAvailable())
{
 Packets packets=networkManager.popPackets();
 foreach(packet in packets)
 {
  processPacket(packet)
 }
}

it is very important to process network asynchronously especially for low connection, if you get first byte of a packet (wich is for example 50 bytes long) and try to read it fully synchronously you may hang up to 6.25ms (50/8000) on a 80Kb connection (best case only if everything goes well), also by using a separated thread the requiered buffer dont have to be that big because you can read ASAP new arriving byte on network an push them into the right packet (wich is very good to avoid network conjonction ), finally on your mainloop you only have to ask all fullfilled packet and threat them, no risk of uncomplete packet and for “simplicity of your project” packet validity/discarding/flowcontrol can be done in the network thread

Ehh, I stay away from IO, too much grief, I use NIO. No wait time for reading <3 data buffered up before it’s sent to my custom buffer so no fragmented packets either.

I’ve noticed a lot of people here tend to stay away from NIO, I wonder why? It’s very elegant…

Hehe I tend to stay away from NIO, IO is easier :smiley: