[TOC]
The problem and solution of TCP sticky packet when using Messagepack in Netty
The following example code demonstrates the TCP sticky problem that occurs when using Messagpack in Netty, and for the sake of learning coherence, the code in chapter 7th of the Netty authoritative guide is referenced, but it is important to note that the book does not provide complete code, and the code provided is fragment-based. So I based on my understanding of the service side of the code and the client code to write out, can be used as a reference.
Still, it's important to note that I'm using a version of Netty 4.x.
In addition I wrote very detailed comments in the program code, so there is no more explanation here.
TCP sticky packet problem when using Messagepack encoder and decoder Msgpackencoder.java
package cn.xpleaf.msgpack;import org.msgpack.MessagePack;import io.netty.buffer.ByteBuf;import io.netty.channel.ChannelHandlerContext;import io.netty.handler.codec.MessageToByteEncoder;/** * MsgpackEncoder继承自Netty中的MessageToByteEncoder类, * 并重写抽象方法encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) * 它负责将Object类型的POJO对象编码为byte数组,然后写入到ByteBuf中 * @author yeyonghao * */public class MsgpackEncoder extends MessageToByteEncoder<Object> { @Override protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception { // 创建MessagePack对象 MessagePack msgpack = new MessagePack(); // 将对象编码为MessagePack格式的字节数组 byte[] raw = msgpack.write(msg); // 将字节数组写入到ByteBuf中 out.writeBytes(raw); }}
Msgpackdecoder.java
Package Cn.xpleaf.msgpack;import Java.util.list;import Org.msgpack.messagepack;import io.netty.buffer.ByteBuf; Import Io.netty.channel.channelhandlercontext;import Io.netty.handler.codec.bytetomessagedecoder;import io.netty.handler.codec.messagetomessagedecoder;/** * Msgpackdecoder inherits from the Messagetomessagedecoder class in Netty, * and rewrite the abstract method decode (Channelhandlercontext ctx, Bytebuf msg, list<object> out) * First get the byte array that needs to be decoded from the datagram msg (the type of data that is populated when the Messagetomessagedecoder is inherited) * and then call Messagepack's Read method to deserialize it (decode) to Object * The decoded object is added to the Decode list out, which completes the decoding operation of the messagepack * @author Yeyonghao * */public class Msgpackdecoder extends messagetomessagedecoder<bytebuf> {@Override protected void decode (Channelhandlercontext ctx, Bytebuf msg, Lis T<object> out) throws Exception {//From datagram MSG (data Type here is BYTEBUF, because Netty communication is based on Bytebuf object) final byte[] Arra Y Final int length = Msg.readablebytes (); Array = new Byte[length]; /** * Here is the Bytebuf GetBytes method used to convert the Bytebuf object to a wordSection array, preceded by the use of readbytes, directly passed in a received byte array parameter can be * Here the parameters are more, the first parameter is index, about Readerindex, described as follows: * Bytebuf is through Readerindex and WR Iterindex two position pointer to assist the buffer read and write operation, the specific principle wait until Netty source code analysis and then learn in detail * The second parameter is the byte array received * The third parameter is Dstindex the first index of th E destination * The fourth parameter is the length of the number of bytes to transfer */Msg.getbytes (Msg.readerindex (), a Rray, 0, length); Create a Messagepack object messagepack msgpack = new Messagepack (); Decode and add to decode list out in Out.add (Msgpack.read (array)); }}
Service-Side Echoserver.java
Package Cn.xpleaf.echo;import Cn.demo.simple.msgpackdecode;import Cn.xpleaf.msgpack.msgpackdecoder;import Cn.xpleaf.msgpack.msgpackencoder;import Io.netty.bootstrap.serverbootstrap;import Io.netty.channel.ChannelFuture ; Import Io.netty.channel.channelinitializer;import Io.netty.channel.channeloption;import Io.netty.channel.eventloopgroup;import Io.netty.channel.nio.nioeventloopgroup;import Io.netty.channel.socket.socketchannel;import Io.netty.channel.socket.nio.nioserversocketchannel;public Class echoserver {public void bind (int port) throws Exception {//Configure the NIO thread group on the service side eventloopgroup Bossgroup = new Nioeventloopgroup (); Eventloopgroup Workergroup = new Nioeventloopgroup (); try {serverbootstrap b = new Serverbootstrap (); B.group (Bossgroup, Workergroup). Channel (nioserversocketchannel.class). Option (channeloption . So_backlog, 1024x768). Childhandler (New channelinitializer<socketchannel> (){@Override protected void Initchannel (Socketchannel ch) throws Exception { Add Messpagepack decoder Ch.pipeline (). AddLast ("Msgpack decoder", New Msgpackdecode ()); Add Messagepack Encoder Ch.pipeline (). AddLast ("Msgpack encoder", New Msgpackenco Der ()); Add Business Processing Handler ch.pipeline (). AddLast (New Echoserverhandler ()); } }); Bind port, synchronization waits successfully channelfuture F = b.bind (port). sync (); Wait for the service-side listener port to close F.channel (). Closefuture (). sync (); } finally {//gracefully exits, releasing thread pool resource bossgroup.shutdowngracefully (); Workergroup.shutdowngracefully (); }} public static void Main (string[] args) throws Exception {int port = 8080; if (args! = null && args.length > 0) {try {port =Integer.valueof (port); } catch (NumberFormatException e) {//Todo:handle exception}} new Echoserver () . bind (port); }}
Echoserverhandler.java
package cn.xpleaf.echo;import io.netty.channel.ChannelHandlerContext;import io.netty.channel.ChannelInboundHandlerAdapter;public class EchoServerHandler extends ChannelInboundHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { System.out.println("Server receive the msgpack message : " + msg); ctx.write(msg); } @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { ctx.flush(); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { // 发生异常,关闭链路 ctx.close(); }}
Client Echoclient.java
Package Cn.xpleaf.echo;import Cn.demo.simple.msgpackdecode;import Cn.xpleaf.msgpack.msgpackdecoder;import Cn.xpleaf.msgpack.msgpackencoder;import Io.netty.bootstrap.bootstrap;import io.netty.channel.ChannelFuture; Import Io.netty.channel.channelinitializer;import Io.netty.channel.channeloption;import Io.netty.channel.eventloopgroup;import Io.netty.channel.nio.nioeventloopgroup;import Io.netty.channel.socket.socketchannel;import Io.netty.channel.socket.nio.niosocketchannel;public class EchoClient {public void Connect (String host, int port, int sendnumber) throws Exception {//Configure client NIO thread Group EVENTLOOPG Roup Group = new Nioeventloopgroup (); try {Bootstrap b = new Bootstrap (); B.group (Group). Channel (niosocketchannel.class). Option (Channeloption.tcp_nodelay, True)//Set TCP connection time-out. Option (Channeloption.connect_timeout_millis, Handler). New Channelinitializ Er<socketchannel> () { @Override protected void Initchannel (Socketchannel ch) throws Exception { Add Messpagepack decoder Ch.pipeline (). AddLast ("Msgpack decoder", New Msgpackdecode ()); Add Messagepack Encoder Ch.pipeline (). AddLast ("Msgpack encoder", New Msgpackencoder ()); Add Business Processing Handler ch.pipeline (). AddLast (New Echoclienthandler (Sendnumber)); } }); Initiates an asynchronous connection operation channelfuture F = b.connect (host, port). sync (); Wait for the client link to close F.channel (). Closefuture (). sync (); } finally {//gracefully exits, releasing the NIO thread Group group.shutdowngracefully (); }} public static void Main (string[] args) throws Exception {int port = 8080; if (args! = null && args.length > 0) {try {port = integer.valueof (port); } CATCH (NumberFormatException e) {//with default}} int sendnumber = 1000; New Echoclient (). Connect ("localhost", port, Sendnumber); }}
Echoclienthander.java
Package Cn.xpleaf.echo;import Cn.xpleaf.pojo.user;import Io.netty.buffer.bytebuf;import io.netty.buffer.Unpooled; Import Io.netty.channel.channelhandleradapter;import Io.netty.channel.channelhandlercontext;import Io.netty.channel.channelinboundhandleradapter;public class Echoclienthandler extends Channelinboundhandleradapter { Sendnumber the number of objects to write to the send buffer private int sendnumber; Public Echoclienthandler (int sendnumber) {this.sendnumber = Sendnumber; /** * Build an array of User objects of length Usernum * @param usernum * @return * * * Private user[] Getuserarray (int usernum) {user[] users = new User[usernum]; User user = null; for (int i = 0; i < Usernum; i++) {user = new user (); User.setname ("ABCDEFG--->" + i); User.setage (i); Users[i] = user; } return users; } @Override public void channelactive (Channelhandlercontext ctx) {user[] users = Getuserarray (Sendnumber); for (user user:users) {ctx.writeandflush (user); }} @Override public void Channelread (Channelhandlercontext ctx, Object msg) throws Exception {System.out . println ("Client receive the Msgpack message:" + msg); } @Override public void Channelreadcomplete (Channelhandlercontext ctx) throws Exception {Ctx.flush (); } @Override public void Exceptioncaught (Channelhandlercontext ctx, Throwable cause) throws Exception {ctx.cl OSE (); }}
Pojouser.java
package cn.xpleaf.pojo;import org.msgpack.annotation.Message;@Messagepublic class User { private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "User [name=" + name + ", age=" + age + "]"; }}
Test
When 1 o'clock in Echoclient.java sendNumber
, both the server and the client are working, the output of the server and client are as follows:
Service side:
Server receive the msgpack message : ["ABCDEFG --->0",0]
Client:
Client receive the msgpack message : ["ABCDEFG --->0",0]
However, when the sendNumber
number is large, it will not work, such as can be set to 1000, the output is as follows:
Service side:
Server receive the msgpack message : ["ABCDEFG --->0",0]Server receive the msgpack message : ["ABCDEFG --->1",1]Server receive the msgpack message : ["ABCDEFG --->3",3]...省略输出...Server receive the msgpack message : ["ABCDEFG --->146",146]Server receive the msgpack message : 70Server receive the msgpack message : ["ABCDEFG --->156",156]Server receive the msgpack message : ["ABCDEFG --->157",157]...省略输出...
Client:
Client receive the msgpack message : ["ABCDEFG --->0",0]Client receive the msgpack message : 62Client receive the msgpack message : 68
Obviously the result of the operation is not the same as expected, due to the problem of TCP sticky packets.
Solution for sticky bag problem
On the basis of the preceding code, you only need to EchoServer.java
EchoClient.java
modify the code in and.
Echoserver.java
Package Cn.xpleaf.echo02;import Cn.demo.simple.msgpackdecode;import Cn.xpleaf.msgpack.msgpackdecoder;import Cn.xpleaf.msgpack.msgpackencoder;import Io.netty.bootstrap.serverbootstrap;import Io.netty.channel.ChannelFuture ; Import Io.netty.channel.channelinitializer;import Io.netty.channel.channeloption;import Io.netty.channel.eventloopgroup;import Io.netty.channel.nio.nioeventloopgroup;import Io.netty.channel.socket.socketchannel;import Io.netty.channel.socket.nio.nioserversocketchannel;import Io.netty.handler.codec.lengthfieldbasedframedecoder;import Io.netty.handler.codec.lengthfieldprepender;public Class Echoserver {public void bind (int port) throws Exception {//Configure the NIO thread group on the service side Eventloopgroup Bossgroup = new Nioeventloopgroup (); Eventloopgroup Workergroup = new Nioeventloopgroup (); try {serverbootstrap b = new Serverbootstrap (); B.group (Bossgroup, Workergroup). Channel (Nioserversocketchannel.class). option (Channeloption.so_backlog, 1024x768). Childhandler (New channelinitializer<socketchannel> () { @Override protected void Initchannel (Socketchannel ch) throws Exception { Add a Length field decoder//Add Lengthfieldbasedframedecoder before the Messagepack decoder to process half-packet messages It parses the length field information of the message header so that the Msgpackdecoder received is always the entire packet message Ch.pipeline (). AddLast ("Framedecoder", New Lengt Hfieldbasedframedecoder (65535, 0, 2, 0, 2)); Add Messpagepack decoder Ch.pipeline (). AddLast ("Msgpack decoder", New Msgpackdecode ()); Add Length field encoder//Increase Lengthfieldprepender before Messagepack encoder, it will increase the message length field by 2 bytes before Bytebuf Ch.pipeline (). AddLast ("Frameencoder", New Lengthfieldprepender (2)); Add Messagepack Encoder Ch.pipeline (). AddLast ("Msgpack encoder", New MsgpackEncoder ()); Add Business Processing Handler ch.pipeline (). AddLast (New Echoserverhandler ()); } }); Bind port, synchronization waits successfully channelfuture F = b.bind (port). sync (); Wait for the service-side listener port to close F.channel (). Closefuture (). sync (); } finally {//gracefully exits, releasing thread pool resource bossgroup.shutdowngracefully (); Workergroup.shutdowngracefully (); }} public static void Main (string[] args) throws Exception {int port = 8080; if (args! = null && args.length > 0) {try {port = integer.valueof (port); } catch (NumberFormatException e) {//Todo:handle exception}} new Echoserver ( ). bind (port); }}
Echoclient.java
Package Cn.xpleaf.echo02;import Cn.demo.simple.msgpackdecode;import Cn.xpleaf.msgpack.msgpackdecoder;import Cn.xpleaf.msgpack.msgpackencoder;import Io.netty.bootstrap.bootstrap;import io.netty.channel.ChannelFuture; Import Io.netty.channel.channelinitializer;import Io.netty.channel.channeloption;import Io.netty.channel.eventloopgroup;import Io.netty.channel.nio.nioeventloopgroup;import Io.netty.channel.socket.socketchannel;import Io.netty.channel.socket.nio.niosocketchannel;import Io.netty.handler.codec.lengthfieldbasedframedecoder;import Io.netty.handler.codec.lengthfieldprepender;public Class Echoclient {public void connect (String host, int port, int sendnumber) throws Exception {//Configure client NIO thread Group Eventloopgroup Group = new Nioeventloopgroup (); try {Bootstrap b = new Bootstrap (); B.group (Group). Channel (niosocketchannel.class). Option (Channeloption.tcp_nodelay, True)//Set TCP connection timeout. Option (ChanneLoption.connect_timeout_millis). Handler (new channelinitializer<socketchannel> () { @Override protected void Initchannel (Socketchannel ch) throws Exception { Add the Length field decoder//Add Lengthfieldbasedframedecoder before the Messagepack decoder to process half-packet messages// It parses the length field information of the message header so that the Msgpackdecoder received is always the entire packet message Ch.pipeline (). AddLast ("Framedecoder", New Lengthfie Ldbasedframedecoder (65535, 0, 2, 0, 2)); Add Messpagepack decoder Ch.pipeline (). AddLast ("Msgpack decoder", New Msgpackdecode ()); Add Length field encoder//Increase Lengthfieldprepender before Messagepack encoder, it will increase the message length field by 2 bytes before Bytebuf Ch.pipeline (). AddLast ("Frameencoder", New Lengthfieldprepender (2)); Add Messagepack Encoder Ch.pipeline (). AddLast ("Msgpack encoder", New MsgpackencoDer ()); Add Business Processing Handler ch.pipeline (). AddLast (New Echoclienthandler (Sendnumber)); } }); Initiates an asynchronous connection operation channelfuture F = b.connect (host, port). sync (); Wait for the client link to close F.channel (). Closefuture (). sync (); } finally {//gracefully exits, releasing the NIO thread Group group.shutdowngracefully (); }} public static void Main (string[] args) throws Exception {int port = 8080; if (args! = null && args.length > 0) {try {port = integer.valueof (port); } catch (NumberFormatException e) {//takes default}} int sendnumber = 1000; New Echoclient (). Connect ("localhost", port, Sendnumber); }}
Test
You can EchoClient.java
set the medium sendNumber
to 1000 or greater, at which point the server and client outputs are the same as expected.
The result of the test is that the server and the client will print 1000 rows of information (assuming Sendnumber is 1000), where the result of the operation is no longer given.
The problem and solution of TCP sticky packet when using Messagepack in Netty