后者可以自动完成对定长消息的解码。
首先是分隔符解码器在netty中的应用,服务端:
public class EchoServer { public static void main(String[] args) { int port = 6767; if(args!=null&&args.length>0){ try { port = Integer.valueOf(args[0]); }catch (NumberFormatException e){ //采用默认值 } } new EchoServer().blind(port); } public void blind(int port){ EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workGroup = new NioEventLoopGroup(); try{ ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup,workGroup) .channel(NioServerSocketChannel.class) .option(ChannelOption.SO_BACKLOG,100) .handler(new LoggingHandler(LogLevel.INFO)) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { //本例中使用"$_"作为分隔符 ByteBuf delimiter = Unpooled.copiedBuffer("$_".getBytes()); /** * 创建DelimiterBasedFrameDecoder对象,它有多个构造方法; * 1024代表单条消息的最大长度,当达到该长度后任然没有找到分隔符, * 就抛出ToolLongFrameException异常; * 第二个参数就是分隔符缓冲对象 */ ch.pipeline().addLast(new DelimiterBasedFrameDecoder(1024,delimiter)); //将ByteBuf解码成字符串对象 ch.pipeline().addLast(new StringDecoder()); //EchoServerHandler接收到的msg消息就是解码后的字符串对象 ch.pipeline().addLast(new EchoServerHandler()); } }); //绑定端口,同步等待成功 ChannelFuture f = b.bind(port).sync(); //等待服务端监听端口关闭 f.channel().closeFuture().sync(); } catch (InterruptedException e) { e.printStackTrace(); }finally { bossGroup.shutdownGracefully(); workGroup.shutdownGracefully(); } } }EchoServerHandler类: public class EchoServerHandler extends ChannelHandlerAdapter{ int counter = 0; @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { /** * DelimiterBasedFrameDecoder自动请求消息进行了解码 * 后续的ChannelHandler接收到的msg对象就是个完整的消息包 */ String body = (String) msg; System.out.println("This is "+ ++counter+"times receive client :【"+ body+"】"); /** * 由于DelimiterBasedFrameDecoder过滤掉了分隔符, * 所以返回给客户端是需要在请求消息尾部拼接分隔符"$_" */ body += "$_"; ByteBuf echo = Unpooled.copiedBuffer(body.getBytes()); ctx.writeAndFlush(echo); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); } } 客户端代码: public class EchoClient { public static void main(String[] args) { int port = 6767; if(args!=null&&args.length>0){ try { port = Integer.valueOf(args[0]); }catch (NumberFormatException e){ //采用默认值 } } new EchoClient().connect(port,"127.0.0.1"); } public void connect(int port,String host){ //配置客户端NIO线程组 EventLoopGroup group = new NioEventLoopGroup(); try { Bootstrap b = new Bootstrap(); b.group(group).channel(NioSocketChannel.class) .option(ChannelOption.TCP_NODELAY,true) .handler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ByteBuf delimiter = Unpooled.copiedBuffer("$_".getBytes()); ch.pipeline().addLast( new DelimiterBasedFrameDecoder(1024,delimiter) ); ch.pipeline().addLast(new StringDecoder()); ch.pipeline().addLast(new EchoClientHandler()); } }); //发起异步连接操作 ChannelFuture f = b.connect(host,port).sync(); //等待客户端链路关闭 f.channel().closeFuture().sync(); } catch (InterruptedException e) { e.printStackTrace(); }finally { group.shutdownGracefully();//优雅退出,释放NIO线程组 } } } EchoClientHandler类: public class EchoClientHandler extends ChannelHandlerAdapter{ private int counter; static final String ECHO_REQ = "Hi,LING. Welcome to Netty.$_"; public EchoClientHandler(){} @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { for(int i = 0;i<10;i++){ ctx.writeAndFlush(Unpooled.copiedBuffer(ECHO_REQ.getBytes())); } } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { System.out.println("This is "+ ++counter+"time receive server:["+msg+"]"); } @Override public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { ctx.flush(); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); } } 分别启动服务端和客户端:服务端运行结果如下:客户端运行结果:
即客户端发送了10条信息到服务端,并且服务端也接收到了客户端的10条信息,本例通过“$_”分割符,
用DelimiterBasedFrameDecoder对消息进行分割,没有出现粘包和拆包的现象。
定长解码器FixedLengthFrameDecoder在Netty中的应用:
服务端开发:
public class EchoServer1 { public static void main(String[] args) { int port = 6765; if(args!=null&&args.length>0){ try { port = Integer.valueOf(args[0]); }catch (NumberFormatException e){ //采用默认值 } } new EchoServer1().blind(port); } public void blind(int port){ //配置服务端的NIO线程组 NioEventLoopGroup bossGroup = new NioEventLoopGroup(); NioEventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup,workerGroup) .channel(NioServerSocketChannel.class) .option(ChannelOption.SO_BACKLOG,100) .handler(new LoggingHandler(LogLevel.INFO)) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline p = ch.pipeline(); p.addLast(new FixedLengthFrameDecoder(20)); //定长解码器,截取20个字节长度的请求消息 p.addLast(new StringDecoder()); p.addLast(new EchoServerHandler1()); } }); //绑定端口,同步等待成功 ChannelFuture f = b.bind(port).sync(); //等待服务端监听端口关闭 f.channel().closeFuture().sync(); } catch (InterruptedException e) { e.printStackTrace(); } } } EchoServerHandler1类: public class EchoServerHandler1 extends ChannelHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { System.out.println("Receive client :["+msg+"]"); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close();//发生异常,关闭链路 } } 启动服务端,利用telnet命令进行测试EchoServer1服务端:1.在【运行】菜单中输入cmd命令,打开命令行窗口;
2.在命令行输入“telnet localhost 6765”连接服务端;(和服务端端口对应)
3.输入命令行内容(需要开启本地回显才能看到输入的内容,我这里一直输入aaaa)
服务端运行结果:
由于之前设置了截取20字节长度,所以这里有20个a
这就是FixedLengthFrameDecoder在netty中的应用。