private void receive(Buffer buf) {
		int cursor;
		int lastBufLength = lastRxBuf.length();

		//keep processing packet header/body as long as we have enough bytes to read it in full
		for (cursor = 0; cursor - lastBufLength + rxLen <= buf.length(); ) {
			byte[] message;
			if (cursor - lastBufLength >= 0) {
				//lastBuf is exhausted
				message = buf.getBytes(cursor - lastBufLength, cursor - lastBufLength + rxLen);
				cursor += rxLen;
			} else {
				//first finishes off lastBuf if it's not empty, then goes to buf
				int readFromLastBuf = Math.min(lastBufLength - cursor, rxLen);
				message = new byte[rxLen];
				System.arraycopy(lastRxBuf.getBytes(cursor, readFromLastBuf), 0, message, 0, readFromLastBuf);
				cursor += rxLen;
				rxLen -= readFromLastBuf;
				if (rxLen > 0)
					System.arraycopy(buf.getBytes(0, rxLen), 0, message, readFromLastBuf, rxLen);
			}
			switch (rxType) {
				case HEADER: {
					if (!ClientEncryption.checkPacket(message, rxIv)) {
						close("Failed packet test");
						return;
					}
					rxLen = ClientEncryption.getPacketLength(message);
					rxType = MessageType.BODY;
					break;
				}
				case BODY: {
					rxLen = HEADER_LENGTH;
					rxType = MessageType.HEADER;
					decryptBytes(message);
					pp.process(new LittleEndianByteArrayReader(message));
					break;
				}
			}
		}

		//check if buffer contains more than we can read. if so, keep a copy of the remaining bytes
		if (cursor - lastBufLength < buf.length())
			lastRxBuf = buf.getBuffer(cursor - lastBufLength, buf.length());
		else
			lastRxBuf = EMPTY_BUFFER;
	}