概况
本篇文章会在应用层面分享一下netty怎么方便的解决拆包和粘包问题,和我会分享一个最常用于解决拆包和粘包的方案自定义协议的demo,其中还会涉及到自定义编解码器,这个以后有时间再分享
拆包粘包
解决拆包和粘包,最常见的方案有三种。
1 消息定长,每个报文长度固定,如果不够,空位补0;
2 使用分隔符,报文间使用分隔符识别;
3 使用自定义协议,将消息分为消息头,和消息体,消息头记录着消息体的长度。
netty提供很方便的handler实现上面的方案,其中
1 DelimiterBasedFrameDecoder —> 以分隔符为流结束的解码器
2 FixedLengthFrameDecoder ---->定长解码器
自定义协议
自定义协议是netty解决拆包粘包比较好的实践,下面我把例子的代码黏贴出来
实体类:
package org.example.protocoltcp;
public class MessageBean {
private int len;
private byte[] content;
public int getLen() {
return len;
}
public void setLen(int len) {
this.len = len;
}
public byte[] getContent() {
return content;
}
public void setContent(byte[] content) {
this.content = content;
}
}
解码器:
package org.example.protocoltcp;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ReplayingDecoder;
import java.util.List;
public class MessageBeanDecoder extends ReplayingDecoder {
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
int length = in.readInt();
byte[] content = new byte[length];
in.readBytes(content);
MessageBean messageProtocol = new MessageBean();
messageProtocol.setLen(length);
messageProtocol.setContent(content);
out.add(messageProtocol);
}
}
编码器:
package org.example.protocoltcp;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
public class MessageBeanEncoder extends MessageToByteEncoder<MessageBean> {
@Override
protected void encode(ChannelHandlerContext ctx, MessageBean msg, ByteBuf out) throws Exception {
out.writeInt(msg.getLen());
out.writeBytes(msg.getContent());
}
}
客户端:
package org.example.protocoltcp;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
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 NettyClient {
public static void main(String[] args) {
EventLoopGroup group = new NioEventLoopGroup(8);
try{
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group)
.channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline()
.addLast(new MessageBeanEncoder())
.addLast(new MessageBeanDecoder())
.addLast(new NettyClientHandler());
}
});
ChannelFuture channelFuture =
bootstrap.connect("127.0.0.1",6668).sync();
channelFuture.channel().closeFuture().sync();
} catch (Exception e) {
e.printStackTrace();
} finally {
group.shutdownGracefully();
}
}
}
客户端handler:
package org.example.protocoltcp;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import java.nio.charset.Charset;
public class NettyClientHandler extends SimpleChannelInboundHandler<MessageBean> {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
String msg = "==================>AAAA";
byte[] content = msg.getBytes(Charset.forName("utf-8"));
int length = content.length;
MessageBean messageProtocol = new MessageBean();
messageProtocol.setLen(length);
messageProtocol.setContent(content);
ctx.writeAndFlush(messageProtocol);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
cause.printStackTrace();
ctx.close();
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, MessageBean msg) throws Exception {
}
}
服务端:
package org.example.protocoltcp;
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 NettyServer {
public static void main(String[] args) throws Exception {
EventLoopGroup boosGroup = new NioEventLoopGroup(3);
EventLoopGroup workerGroup = new NioEventLoopGroup(3);
try{
ServerBootstrap bootstrap = new ServerBootstrap();
bootstrap.group(boosGroup,workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG,128)
.childOption(ChannelOption.SO_KEEPALIVE,true)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
socketChannel.pipeline()
.addLast(new MessageBeanEncoder())
.addLast(new MessageBeanDecoder())
.addLast(new NettyServerHandler());
}
});
ChannelFuture cf = bootstrap.bind(6668).sync();
cf.channel().closeFuture().sync();
}finally {
boosGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
服务端handler:
package org.example.protocoltcp;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
public class NettyServerHandler extends SimpleChannelInboundHandler<MessageBean> {
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, MessageBean msg) throws Exception {
byte[] content = msg.getContent();
System.out.println("============>"+new String(content));
}
}
参考资源:
netty权威指南
尚硅谷netty视频