1. 程式人生 > >Netty解決分包的幾種解碼器介紹

Netty解決分包的幾種解碼器介紹

一:LineBaseFrameDecoder 換行符解碼器
改解碼器的原理是,以此遍歷快取數組裡的位元組,判斷位元組是否為“\n”或者“\r\n”,如果讀到了,就任務是資料包的結束位置,這樣從遍歷的start position到end position 這個閉區間就可以截取出一個完整的業務資料包。這種方式的缺點可以在資料的其他位置中包含這兩個字元,導致擷取資料錯誤,那麼為了保障資料的正確性,一般多加一個固定的長度,如果尋找字元的區間長度大於這個固定的長度,認為資料有問題,那麼就把這個資料扔掉,接著讀下面的資料。

二:DelimiterBaseFrameDecoder 定界符解碼器
該解碼器可以自動完成以定界符為碼流結束標誌,然後擷取資料包。下面通過一個案例來演示一下這個解碼器的使用。
客戶端程式碼:
public class DelimiterClientTime {
private static final int PORT = 8080;
private static final String IP = “127.0.0.1”;
public static void main(String[] args) throws InterruptedException {
new DelimiterClientTime().connect(PORT, IP);
}
public void connect(int port, String host) 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() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
//socket通道管道加入行解碼器
ByteBuf delimiter = Unpooled.copiedBuffer(“#_”.getBytes());
//傳輸遍歷的資料長度和界定符“#_”
socketChannel.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, delimiter));
//socket通道管道加入字串解碼器
socketChannel.pipeline().addLast(new StringDecoder());
//socket通道管道加入客戶端傳送資料的介面卡
socketChannel.pipeline().addLast(new DelimiterClientHandler());
}
});
//發起非同步連線,連線指定的主機,指定主機ip和port
ChannelFuture f = bootstrap.connect(host, port).sync();
//等待客戶端鏈路關閉
f.channel().closeFuture().sync();
} finally {
//釋放執行緒組
group.shutdownGracefully();
}
}
}
public class DelimiterClientHandler extends ChannelHandlerAdapter {
private int counter;
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
for (int i = 0; i < 100; i++) {
ctx.writeAndFlush(Unpooled.copiedBuffer((“HI Netty” + “#_”).getBytes()).toString());
}
}
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
System.out.println(“this is ” + (++counter) + ” receive server message” + msg);
}
@Override
public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
System.out.println(“讀取完成”);
ctx.flush();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
System.out.println(“客戶端異常退出”);
}
}

伺服器程式碼:
public class DelimiterDecoderServer {
public static void main(String[] args) throws InterruptedException {
DelimiterDecoderServer delimiterDecoderServer = new DelimiterDecoderServer();
delimiterDecoderServer.bind(8080);
}
public void bind(int port) throws InterruptedException {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.option(ChannelOption.SO_BACKLOG, 100)
.childHandler(new ChannelInitializer() {
@Override
protected void initChannel(SocketChannel socketChannel) throws Exception {
ByteBuf delimiter = Unpooled.copiedBuffer(“#_”.getBytes());
socketChannel.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, delimiter));
socketChannel.pipeline().addLast(new StringDecoder());
socketChannel.pipeline().addLast(new DelimiterDecoderServerHandler());
}
});
ChannelFuture f = b.bind(port).sync();
f.channel().closeFuture().sync();
} finally {
bossGroup.shutdownGracefully();
workerGroup.shutdownGracefully();
}
}
}
public class DelimiterDecoderServerHandler extends ChannelHandlerAdapter {
private int counter;
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
String body = (String) msg;
System.out.println(“This is ” + (++counter) + ” receive client msg ” + body);
String sendContent = “receive message is ok:” + “#_”;
ByteBuf byteBuf = Unpooled.copiedBuffer(sendContent.getBytes());
ctx.writeAndFlush(byteBuf);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
ctx.close();
}
}