netty 拆包粘包,也就是半包读写问题。
之前有说过,主要是三种解决方案,一个是消息定长,一个是分隔符,一个是将消息分为消息头消息体。
之前说到分隔符的时候,主要说的是DelimiterBasedFrameDecoder。DelimiterBasedFrameDecoder可以指定任意一个分隔符,注意,它的分隔符要放在前面。
今天要讲的是LineBasedFrameDecoder,LineBasedFrameDecoder也属于分隔符的范畴,不过LineBasedFrameDecoder的分隔符限定是换行符\n,而且是放在消息尾的。而且要配合StringDecoder使用。
关键部分1 配置handler时:
//添加拆包粘包解码器
pipeline.addLast(new LineBasedFrameDecoder(1024));
pipeline.addLast(new StringDecoder());
关键部分2 发送消息时:
future.channel().writeAndFlush(Unpooled.copiedBuffer(("你好"+System.getProperty("line.separator")).getBytes()));
System.getProperty("line.separator")就是添加的分隔符。
有个细节要注意,就是new LineBasedFrameDecoder(1024) 这里的1024是啥意思?意思就是如果消息长度到了1024还没有发现换行符,那么就异常了,消息也就收不到了。所以这个值一定要大于消息的长度,尽量大一些。
下面看具体代码:
server 端三个:
import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.logging.LogLevel; import io.netty.handler.logging.LoggingHandler; public class LineServer { public void bind() { EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workGroup = new NioEventLoopGroup(); ServerBootstrap bootstrap = new ServerBootstrap(); bootstrap.group(bossGroup, workGroup); bootstrap.channel(NioServerSocketChannel.class) .option(ChannelOption.SO_BACKLOG, 1024) .option(ChannelOption.SO_SNDBUF, 32 * 1024) .option(ChannelOption.SO_RCVBUF, 32 * 1024) .option(ChannelOption.SO_KEEPALIVE, true) .handler(new LoggingHandler(LogLevel.DEBUG)) .childHandler(new LineServerInitialzer()); try { ChannelFuture future = bootstrap.bind("127.0.0.1", 8765).sync(); future.channel().closeFuture().sync(); bossGroup.shutdownGracefully(); workGroup.shutdownGracefully(); } catch (InterruptedException e) { e.printStackTrace(); } } public static void main(String[] args) { new LineServer().bind(); } }
import io.netty.channel.Channel; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.handler.codec.LineBasedFrameDecoder; import io.netty.handler.codec.string.StringDecoder; public class LineServerInitialzer extends ChannelInitializer { protected void initChannel(Channel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); //添加拆包粘包解码器 pipeline.addLast(new LineBasedFrameDecoder(1024)); pipeline.addLast(new StringDecoder()); pipeline.addLast(new LineServerHandler()); } }
public class LineServerHandler extends ChannelHandlerAdapter { private Log logger= LogFactory.getLog(LineServerHandler.class); @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { logger.error("server erro",cause); } @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { logger.info("---------------server active-----------"); } @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { logger.info("---------------server inActive-----------"); } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { logger.info("---------------server read-----------"); String rev_msg= (String) msg; logger.info("server read:"+rev_msg); } @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { ctx.close(); } }
Client端 三个
import io.netty.bootstrap.Bootstrap; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelFuture; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.logging.LogLevel; import io.netty.handler.logging.LoggingHandler; public class LineClient { public void connect(){ EventLoopGroup workGroup=new NioEventLoopGroup(); Bootstrap bootstrap=new Bootstrap(); bootstrap.group(workGroup); bootstrap.channel(NioSocketChannel.class) .handler(new LineClientInitializer()) .handler(new LoggingHandler(LogLevel.DEBUG)); try { ChannelFuture future= bootstrap.connect("127.0.0.1",8765).sync(); for(int i=0;i<10;i++){ future.channel().writeAndFlush(Unpooled.copiedBuffer(("你好"+System.getProperty("line.separator")).getBytes())); } future.channel().closeFuture().sync(); workGroup.shutdownGracefully(); } catch (InterruptedException e) { e.printStackTrace(); } } public static void main(String[] args) { new LineClient().connect(); } }
import io.netty.channel.Channel; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.handler.codec.LineBasedFrameDecoder; import io.netty.handler.codec.string.StringDecoder; public class LineClientInitializer extends ChannelInitializer { protected void initChannel(Channel ch) throws Exception { ChannelPipeline pipeline=ch.pipeline(); pipeline.addLast(new LineBasedFrameDecoder(1024)); pipeline.addLast(new StringDecoder()); pipeline.addLast(new LineClientHandler()); } }
import io.netty.channel.ChannelHandlerAdapter; import io.netty.channel.ChannelHandlerContext; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; public class LineClientHandler extends ChannelHandlerAdapter { private Log logger= LogFactory.getLog(LineClientHandler.class); @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { logger.error("client erro",cause); } @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { logger.info("---------------client active-----------"); } @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { logger.info("----------------client inActive----------"); } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { logger.info("----------------client read---------------"); } @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { } }