对于通过分隔符进行粘包与拆包问题的处理,Netty提供了两个编码类,LineBasedFrameDecoder和DelimiterBasedFrameDecoder,这里LineBasedFrameDecoder的作用主要是通过换行符,即"\n" 或者 "\r\n"对数据进行处理,而DelimiterBasedFrameDecoder的作用主要是通过用户指定的分隔符进行粘包与拆包处理,同样的,这两个类都是解码器,而对于数据的编码,也即在每个数据包最后添加换行或者指定分隔符的部分需要用户自行出里,代码如下:
//Server端
public class EchoServer {
public void bind(int port) throws InterruptedException {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workGroup = new NioEventLoopGroup();
try {
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 1024)
.handler(new LoggingHandler(LogLevel.INFO))
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
String delimiter = "_$";
/**
* 将delimiter设置到DelimiterBasedFrameDecoder中,经过该解码器进行处理之后,
* 源数据将会按照 _$ 进行分隔,这里1024指的是分隔的最大长度,即当读取到1024个字节的数据后,
* 若还是未读取到分隔符,则舍弃当前的数据段,因为其很有可能是由于源码紊乱造成的,
*/
ChannelPipeline pipeline = ch.pipeline();
//maxFrameLength:读取最大长度字节数,delimiter:分隔符
pipeline.addLast(new DelimiterBasedFrameDecoder(1024, Unpooled.wrappedBuffer(delimiter.getBytes())));
//将分隔之后的字节数据转成字符换
pipeline.addLast(new StringDecoder());
//自定义编码器,主要作用是在返回的响应数据后添加分隔符
pipeline.addLast(new DelimiterBasedFrameEncoder(delimiter));
//添加自定义的handler进行数据处理
pipeline.addLast(new EchoServerHandler());
}
});
ChannelFuture channelFuture = serverBootstrap.bind(port).sync();
channelFuture.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workGroup.shutdownGracefully();
}
}
public static void main(String[] args) throws InterruptedException {
new EchoServer().bind(7006);
}
}
//Client端
public class EchoClient {
public void connect(String host, int port) throws InterruptedException {
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(group)
.channel(NioSocketChannel.class)
.option(ChannelOption.TCP_NODELAY, true)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
String delimiter = "_$";
ChannelPipeline pipeline = ch.pipeline();
//对服务器返回的消息通过 _$ 进行分隔,并且每次查找的最大大小为1024,
pipeline.addLast(new DelimiterBasedFrameDecoder(1024, Unpooled.wrappedBuffer(delimiter.getBytes())));
//将分隔后的字节数据转成字符串
pipeline.addLast(new StringDecoder());
//对客户端发送的数据进行编码,这里主要是在客户端发送的数据在最后添加分隔符
pipeline.addLast(new DelimiterBasedFrameEncoder(delimiter));
//客户端发送数据给服务器,并且处理从服务器响应的数据
pipeline.addLast(new EchoClientHandler());
//对服务器
}
});
ChannelFuture channelFuture = bootstrap.connect(host, port).sync();
channelFuture.channel().closeFuture().sync();
} finally {
group.shutdownGracefully();
}
}
public static void main(String[] args) throws InterruptedException {
new EchoClient().connect("127.0.0.1", 7006);
}
}
ServerHandler处理客户端发送的数据:
public class EchoServerHandler extends SimpleChannelInboundHandler<String> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg)
throws Exception {
System.out.println(" server receives message:" + msg.trim());
ctx.writeAndFlush(" hello client");
}
}
CleintHandler处理服务器响应的数据:
public class EchoClientHandler extends SimpleChannelInboundHandler<String> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, String msg)
throws Exception {
System.out.println(" client receives message: " + msg.trim());
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
ctx.writeAndFlush(" hello server");
}
}
上面的pipeline中,添加了解码器主要有DelimiterBasedFrameDecoder()和StringDecoder(),经过这两个处理后,接收到的字节流会被分隔,并且转换成字符串,最终交由EchoServerHandler处理,这里的DelimiterBasedFrameEncoder是自定义的编码器,其主要作用是返回的响应数据之后添加上指定的分隔符。
public class DelimiterBasedFrameEncoder extends MessageToByteEncoder<String> {
private String delimiter;
public DelimiterBasedFrameEncoder(String delimiter) {
this.delimiter = delimiter;
}
@Override
protected void encode(ChannelHandlerContext ctx, String msg, ByteBuf out)
throws Exception {
//在响应的数据后面添加分隔符
ctx.writeAndFlush(Unpooled.wrappedBuffer((msg + delimiter).getBytes()));
}
}