Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Netty concatenates messages that arrive simultaneously

I have one client connected to a server. The communication in the TCP session is bidirectional. If I try to send multiple messages simultaneously from the sever to the client they are mixed up in one array. The message is already corrupted when it reaches the ByteArrayDecoder. Here is my pipeline:

    ChannelPipeline channelPipeline = ch.pipeline();

    channelPipeline.addLast("byteArrayEncoder", new ByteArrayEncoder());
    channelPipeline.addLast("myRequestEncoder", new MyRequestEncoder());
    channelPipeline.addLast("mySecondEncoder", new MySecondEncoder());

    channelPipeline.addLast("byteArraydDecoder", new ByteArrayDecoder());
    channelPipeline.addLast("myResponseDecoder", new MyResponseDecoder());
    channelPipeline.addLast("mySecondDecoder", new MySecondDecoder());

    channelPipeline.addLast("mytHandler", myHandler);

For example I have noticed that the byte array in ByteArrayDecoder has length more than 500 instead of 230. And I have seen that the contents of the array are corresponding to two or more messages that have been concatenated.

I have only one channel. I have tried to use more than one EventExecutorGroup in the pipeline but this keeps happening and I end up to miss several inbound messages.

However with 1tps everything seems to work OK.

Is netty supposed to have such behavior? Am I missing anything?

like image 401
ampofila Avatar asked Sep 12 '25 06:09

ampofila


1 Answers

TCP is a stream based protocol, you application should work from that concept. Netty also receives the bytes as a stream from the networking layer, and its the job of the application to convert the stream of bytes in something useful.

You are failing to correctly convert the incoming streaming bytes in correct blocks of data that your application can handle.

There are multiple ways to introduce a "reframer" for the bytes:

Make every packet a fixed length

If every packet is a fixed length, you can just read X amount of bytes, and use that as a simple framing solution, this is really simple using netty:

channelPipeline.addLast("framer", new FixedLengthFrameDecoder(512));

This splits all the incoming bytes up in blocks of 512 bytes.

The arguments to FixedLengthFrameDecoder are:

  • Length of the incoming packets

Prepend the length of the packet before sending

Instead of difficulty making every packet the same size, you can also add a length to a packet, so you can split the packets later. Netty also comes with useful utility classes for this:

channelPipeline.addLast("framer", new LengthFieldBasedFrameDecoder(Short.MAX_VALUE,0,2,0,2));
channelPipeline.addLast("framer-prepender", new LengthFieldPrepender(2, false));

The arguments for LengthFieldBasedFrameDecoder are:

  • Max frame length
  • Offset in receiving data for frame length
  • Length in bytes of our length field
  • Bytes to strip from the received message

The arguments for LengthFieldPrepender are:

  • Length in bytes of our length field
like image 187
Ferrybig Avatar answered Sep 14 '25 21:09

Ferrybig