First we must know the TCP sticky packet and unpacking, TCP is a "flow" protocol, so-called flow, is no bounds of a string of data, TCP bottom is not aware of the specific meaning of the upper business data, it will be based on the actual data of the TCP buffer partition, a complete package may be split into multiple packets to send, It is also possible to encapsulate multiple small packages into a large packet for sending. This is illustrated in the figure in the user guide of the Netty website:
Dealing with a stream-based transportone Small caveat of Socket Buffer
In a stream-based transport such as TCP/IP, received data is stored into a socket receive buffer. Unfortunately, the buffer of a stream-based transport is not a queue of packets but a queue of bytes. It means, even if you sent, and messages as, independent packets, an operating system would not treat them as both message s but as just a bunch of bytes. Therefore, there is no guarantee so what are you read are exactly what your remote peer wrote. For example, let us assume, the TCP/IP stack of an operating system have received three packets:
Because of a stream-based protocol, there ' s high chance of reading them in the following fragment Ed form in your application:
Therefore, a receiving part, regardless it's server-side or client-side, should defrag the received data into one or more Meaningful frames that could is easily understood by the application logic. In case of the example above, the received data should is framed like the following:
So how do we solve this problem in general? There are several options that I know of:
>1. Message fixed length
>2. Add an identifier at the end of the package to be segmented by this marker
>3. Divides the message into two parts, the message header and the end of the message, in which the total length of the data to be sent is written, usually in the first field of the message header, using an int value to identify the length of the sending data.
This is the third way, for example, to transfer objects. The Netty4 itself comes with Objectdecoder,objectencoder to serialize the custom object, but with Java built-in serialization, because the performance of Java serialization is not very good, so many times we need to use other serialization methods, Common have kryo,jackson,fastjson,protobuf and so on. What we want to write here is not the focus of what serialization is, but how we design our decoder and encoder.
First we write a encoder, we inherit from the messagetobyteencoder<t>, convert the object to byte, inherit this object, will ask us to implement a encode method:
@Override protected void encode (channelhandlercontext ctx, Object msg, bytebuf out) throws Span style= "color: #000000;" > Exception { byte [] body = converttobytes (msg); // you choose to convert the object to byte, pseudo-code, and exactly what to serialize. I can use some of the int datalength = body.length; // read the length of the message out.writeint (datalength); // out.writebytes (body); // }
So what do we do with the data sent when we are decode? Here we inherit the Bytetomessagedecoder method, inheriting this object, will ask us to implement a Decode method
Public voidDecode (Channelhandlercontext CTX, bytebuf in, list<object>Out ) { if(In.readablebytes () < head_length) {//This head_length is the number of bytes we use to represent the head length. Since we are passing a value of type int, the value of Head_length here is 4. return; } in.markreaderindex (); //We mark the location of the current Readindex intDatalength = In.readint ();//Read the length of the message that was sent over. Bytebuf's Readint () method will add 4 to his readindex. if(Datalength < 0) {//We read that the message body length is 0, this is not supposed to happen, here comes the case, close the connection. Ctx.close (); } if(In.readablebytes () < datalength) {//Read the length of the message body if it is less than the length of the message we transmit, then resetreaderindex. This is used in conjunction with Markreaderindex. Reset the Readindex to mark's place.In.resetreaderindex (); return; } byte[] BODY =New byte[Datalength];//Well, at this time, we read the length, meet our requirements, send over the data, take it out ~ ~In.readbytes (body);//Object o = converttoobject (body);//Convert the byte data to the object we need. Pseudo code, with what serialization, choose for yourselfOut.add (o); }
Of course we netty also have their own lengthfieldbasedframedecoder, but in the use of custom serialization, I think it is more convenient to write their own, anyway, is not to write code.
I walk through the pit: with the use of reading bytebuf, it is important to note that whether the method will increase readindex, otherwise it will not be able to read the data we want.
Netty4 custom Decoder,encoder for object delivery