TCP粘包/拆包--利用FixedLengthFrameDecoder解决TCP粘包问题

来源:互联网 发布:app模型制作软件 编辑:程序博客网 时间:2024/06/11 03:10

前面我们介绍了利用LineBasedFrameDecoder和DelemiterBasedFrameDecoder解决TCP的粘包/拆包的问题,

现在我们继续介绍Netty的最后一种解码器--FixedLengthFrameDecoder。

利用FixedLengthFrameDecoder解码器,无论一次接收到多少数据报,它都会按照构造函数中设置的固定长度进行解码,如果是半包消息,FixedLengthFrameDecoder会缓存半包消息并等待下个包到达后进行拼包,直到读取到一个完整的包。

1. EchoServer.java

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;import io.netty.handler.codec.FixedLengthFrameDecoder;import io.netty.handler.codec.string.StringDecoder;public class EchoServer {    private final static int port = 8080;    public static void main(String[] args) {        start();    }    private static void start() {        final EchoServerHandler serverHandler = new EchoServerHandler();        // 创建EventLoopGroup        EventLoopGroup bossGroup = new NioEventLoopGroup();        EventLoopGroup workerGroup = new NioEventLoopGroup();        // 创建EventLoopGroup        ServerBootstrap b = new ServerBootstrap();        b.group(bossGroup, workerGroup)        .channel(NioServerSocketChannel.class)        .option(ChannelOption.SO_BACKLOG, 100)        .childHandler(new ChannelInitializer<SocketChannel>() {            @Override            protected void initChannel(SocketChannel socketChannel) throws Exception {            socketChannel.pipeline().addLast(                    new FixedLengthFrameDecoder(20));            socketChannel.pipeline().addLast(new StringDecoder());            socketChannel.pipeline().addLast(serverHandler);            }        });        try {            // 异步地绑定服务器;调用sync方法阻塞等待直到绑定完成            ChannelFuture f = b.bind(port).sync();            // 获取Channel的CloseFuture,并且阻塞当前线程直到它完成            f.channel().closeFuture().sync();        } catch (InterruptedException e) {            e.printStackTrace();        } finally {            // 优雅的关闭EventLoopGroup,释放所有的资源            bossGroup.shutdownGracefully();            workerGroup.shutdownGracefully();        }    }}
2. EchoServerHandler.java

import io.netty.buffer.ByteBuf;import io.netty.buffer.Unpooled;import io.netty.channel.ChannelHandler.Sharable;import io.netty.channel.ChannelHandlerContext;import io.netty.channel.ChannelInboundHandlerAdapter;@Sharablepublic class EchoServerHandler extends ChannelInboundHandlerAdapter{    private int counter = 0;    @Override    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {        String body = (String )msg;        System.out.println(                "This is " + ++counter +" times receive client [" + body +"]"        );        ByteBuf resp = Unpooled.copiedBuffer(body.getBytes());        ctx.writeAndFlush(resp);    }    /**     * 异常处理     * @param ctx     * @param cause     * @throws Exception     */    @Override    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {        //打印异常栈跟踪        cause.printStackTrace();        // 关闭该Channel        ctx.close();    }}
3. EchoClient.java
import io.netty.bootstrap.Bootstrap;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.NioSocketChannel;import io.netty.handler.codec.FixedLengthFrameDecoder;import io.netty.handler.codec.string.StringDecoder;import java.net.InetSocketAddress;public class EchoClient {    private final static String HOST = "localhost";    private final static int PORT = 8080;    public static void start() {        EventLoopGroup group = new NioEventLoopGroup();        Bootstrap bootstrap = new Bootstrap();        bootstrap.group(group)                .channel(NioSocketChannel.class)                .option(ChannelOption.TCP_NODELAY, true)                .handler(new ChannelInitializer<SocketChannel>() {                    @Override                    protected void initChannel(SocketChannel socketChannel) throws Exception {                        socketChannel.pipeline().addLast(new FixedLengthFrameDecoder(20));                        socketChannel.pipeline().addLast(new StringDecoder());                        socketChannel.pipeline().addLast(new EchoClientHandler());                    }                });        try {            ChannelFuture f = bootstrap.connect(HOST,PORT).sync();            f.channel().closeFuture().sync();        } catch (InterruptedException e) {            e.printStackTrace();        }finally {            group.shutdownGracefully();        }    }    public static void main(String[] args) {        start();    }}
4. EchoClientHandler.java
import io.netty.buffer.Unpooled;import io.netty.channel.ChannelHandler.Sharable;import io.netty.channel.ChannelHandlerContext;import io.netty.channel.ChannelInboundHandlerAdapter;@Sharablepublic class EchoClientHandler extends ChannelInboundHandlerAdapter {    private int counter;    static final String ECHO_REQ = "Hi, Welcome to Netty World!";    @Override    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {        String body = (String) msg;        System.out.println(                "This is "+ ++counter + " times receive server:[" + body +"]"        );    }    @Override    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {        ctx.flush();    }    @Override    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {        cause.printStackTrace();        ctx.close();    }    @Override    public void channelActive(ChannelHandlerContext ctx) throws Exception {        for (int i = 0; i < 10; i++) {            ctx.writeAndFlush(Unpooled.copiedBuffer(ECHO_REQ.getBytes()));        }    }}
分别运行EchoServer和EchoClient,控制台的运行结果如下
服务端运行结果:
This is 1 times receive client [Hi, Welcome to Netty]This is 2 times receive client [ World!Hi, Welcome t]This is 3 times receive client [o Netty World!Hi, We]This is 4 times receive client [lcome to Netty World]This is 5 times receive client [!Hi, Welcome to Nett]This is 6 times receive client [y World!Hi, Welcome ]This is 7 times receive client [to Netty World!Hi, W]This is 8 times receive client [elcome to Netty Worl]This is 9 times receive client [d!Hi, Welcome to Net]This is 10 times receive client [ty World!Hi, Welcome]This is 11 times receive client [ to Netty World!Hi, ]This is 12 times receive client [Welcome to Netty Wor]This is 13 times receive client [ld!Hi, Welcome to Ne]
客户端运行结果:
This is 1 times receive server:[Hi, Welcome to Netty]This is 2 times receive server:[ World!Hi, Welcome t]This is 3 times receive server:[o Netty World!Hi, We]This is 4 times receive server:[lcome to Netty World]This is 5 times receive server:[!Hi, Welcome to Nett]This is 6 times receive server:[y World!Hi, Welcome ]This is 7 times receive server:[to Netty World!Hi, W]This is 8 times receive server:[elcome to Netty Worl]This is 9 times receive server:[d!Hi, Welcome to Net]This is 10 times receive server:[ty World!Hi, Welcome]This is 11 times receive server:[ to Netty World!Hi, ]This is 12 times receive server:[Welcome to Netty Wor]This is 13 times receive server:[ld!Hi, Welcome to Ne]

节选自《Netty权威指南第二版》, 代码部分有部分改动。

原创粉丝点击