3、解决粘包和半包问题三种方式:自定义字符串分隔符、消息定长,系统回车符

注:源代码来自享学课堂,学习之后所做笔记,方便回顾,也给大家一个参考

1、Client主函数

以下三种任意使用一种
1、通过添加DelimiterBasedFrameDecoder使用自定义字符串分隔符
2、消息定长,把消息的长度作为固定长度
3、加分割符:系统回车符

import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.Unpooled;
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;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;

import java.net.InetSocketAddress;

public class EchoClient {

    private final int port;
    private final String host;

    public EchoClient(int port, String host) {
        this.port = port;
        this.host = host;
    }

    //netty的标准程序基本都是下面这个流程
    public void start() throws InterruptedException {
        /*线程组*/
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            /*客户端启动必备,服务端使用ServerBootstrap*/
            Bootstrap b = new Bootstrap();
            b.group(group)
                    /*指明使用NIO进行网络通讯*/
                    .channel(NioSocketChannel.class)
                    /*配置远程服务器的地址,服务器端就是配置LocalAdderss*/
                    .remoteAddress(new InetSocketAddress(host, port))
                    //这里配置我们自定义的handler,netty主要就是写各种各样的handler
                    .handler(new ChannelInitializer() {
                        //解决粘包和半包问题
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            //以下三种任意使用一种
                            //1、通过添加DelimiterBasedFrameDecoder使用自定义字符串分隔符
                            socketChannel.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, Unpooled.copiedBuffer("---".getBytes())));
                            //2、消息定长,把消息的长度作为固定长度
                            //socketChannel.pipeline().addLast(new FixedLengthFrameDecoder(EchoClientHandler.MSG.length()));
                            //3、加分割符:系统回车符
                            /**
                             * 这个需要在EchoClientHandler每次发送消息,在消息上加上换行,代码如下
                             *      @Override
                             *     public void channelActive(ChannelHandlerContext ctx) throws Exception {
                             *         ByteBuf msg = null;
                             *         String request = "Hello,Netty  + System.getProperty("line.separator");
                             *         for(int i=0;i<100;i++){
                             *             msg = Unpooled.buffer(request.length());
                             *             msg.writeBytes(request.getBytes());
                             *             ctx.writeAndFlush(msg);
                             *         }
                             *     }
                             */
                            //socketChannel.pipeline().addLast(new LineBasedFrameDecoder(1024*100000));

                            socketChannel.pipeline().addLast(new EchoClientHandler());
                        }
                    });
            /*连接到远程节点,阻塞等待直到连接完成*/
            ChannelFuture f = b.connect().sync();
            /*阻塞,直到channel关闭*/
            f.channel().closeFuture().sync();
        } finally {
            //阻塞关闭线程组
            group.shutdownGracefully().sync();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        new EchoClient(8080, "127.0.0.1").start();
    }
}

2、自定义客户端Handler

在channelActive建立连接之后,循环100次数据,本来应该是server接受100次的,但是看最后的结果只接受了两次

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.util.CharsetUtil;

//SimpleChannelInboundHandler帮我们封装好了很多东西,客户端和服务端都是使用ByteBuf通信的
public class EchoClientHandler extends SimpleChannelInboundHandler {

    /*客户端读取到服务器发送过来的数据了,在这里做处理*/
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, ByteBuf msg)
            throws Exception {
        System.out.println("Client accept:" + msg.toString(CharsetUtil.UTF_8));
    }

    /*客户端被通知channel活跃以后,做事*/
    //这里循环发送数据
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        for (int i = 0; i < 100; i++) {
            ctx.writeAndFlush(Unpooled.copiedBuffer("Hello,Netty ",
                    CharsetUtil.UTF_8));
        }
    }

    //客户端读取数据完毕之后做事
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channelReadComplete");
    }

    //发生异常的处理
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
            throws Exception {
        cause.printStackTrace();
        ctx.close();
    }

}

4、Server端主函数

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
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.nio.NioServerSocketChannel;
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
import java.net.InetSocketAddress;

public class EchoServer {
    private Integer port;

    public EchoServer(Integer port) {
        this.port = port;
    }

    public void start() {
        EventLoopGroup group = new NioEventLoopGroup();
        ServerBootstrap bootstrap = new ServerBootstrap();
        try {
            bootstrap.group(group)
                    .localAddress(new InetSocketAddress(port))
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer() {
                        @Override
                        protected void initChannel(Channel channel) throws Exception {
                            //以下三种任意使用一种
                            //1、通过添加DelimiterBasedFrameDecoder使用自定义字符串分隔符
                            channel.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, Unpooled.copiedBuffer("---".getBytes())));
                            //2、消息定长,把消息的长度作为固定长度
                            //channel.pipeline().addLast(new FixedLengthFrameDecoder(EchoClientHandler.MSG.length()));
                            //3、加分割符:系统回车符
                            /**
                             * 这个需要在EchoClientHandler每次发送消息,在消息上加上换行,代码如下
                             *      @Override
                             *     public void channelActive(ChannelHandlerContext ctx) throws Exception {
                             *         ByteBuf msg = null;
                             *         String request = "Hello,Netty  + System.getProperty("line.separator");
                             *         for(int i=0;i<100;i++){
                             *             msg = Unpooled.buffer(request.length());
                             *             msg.writeBytes(request.getBytes());
                             *             ctx.writeAndFlush(msg);
                             *         }
                             *     }
                             */
                            //channel.pipeline().addLast(new LineBasedFrameDecoder(1024*100000));
                            channel.pipeline().addLast(new EchoServerHandler());

                        }
                    });
            ChannelFuture future = bootstrap.bind().sync();
            future.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            try {
                group.shutdownGracefully().sync();
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        new EchoServer(8089).start();
    }
}

5、server端的Handler

counter.incrementAndGet()用来计算客户端发送数据的次数,客户端循环了100次,但是服务端只接受了

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.CharsetUtil;

import java.util.concurrent.atomic.AtomicInteger;

/*指明我这个handler可以在多个channel之间共享,意味这个实现必须线程安全的。*/
@ChannelHandler.Sharable//如果不加这个注解,主函数在.childHandler(new EchoServerHandler()),确保每个链接都单独的handler
public class EchoServerHandler extends ChannelInboundHandlerAdapter {

    private AtomicInteger counter = new AtomicInteger();

    /*** 服务端读取到网络数据后的处理*/
    //计算接受数据次数,按道理客户端发送了100次,也应该接受100次,结果只有两次
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg)
            throws Exception {
        ByteBuf in = (ByteBuf) msg;/*netty实现的缓冲区*/
        System.out.println("Server Accept:" + in.toString(CharsetUtil.UTF_8) + counter.incrementAndGet());
        ctx.write(in);
    }

    /*** 服务端读取完成网络数据后的处理*/
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx)
            throws Exception {
        ctx.writeAndFlush(Unpooled.EMPTY_BUFFER)/*flush掉所有的数据*/
                .addListener(ChannelFutureListener.CLOSE);/*当flush完成后,关闭连接*/
    }

    /*** 发生异常后的处理*/
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)
            throws Exception {
        cause.printStackTrace();
        ctx.close();
    }
}

6、运行结果

server

服务器即将启动
Server Accept[hello server,I`m client:1] and the counter is:1
Server Accept[hello server,I`m client:2] and the counter is:2
Server Accept[hello server,I`m client:3] and the counter is:3
Server Accept[hello server,I`m client:4] and the counter is:4
Server Accept[hello server,I`m client:5] and the counter is:5
Server Accept[hello server,I`m client:6] and the counter is:6
Server Accept[hello server,I`m client:7] and the counter is:7
Server Accept[hello server,I`m client:8] and the counter is:8
Server Accept[hello server,I`m client:9] and the counter is:9
Server Accept[hello server,I`m client:10] and the counter is:10

这里能够正确输出10次,代表粘包和半包解决

client

Hello,hello server,I`m client:1. Welcome to Netty World!
Hello,hello server,I`m client:2. Welcome to Netty World!
Hello,hello server,I`m client:3. Welcome to Netty World!
Hello,hello server,I`m client:4. Welcome to Netty World!
Hello,hello server,I`m client:5. Welcome to Netty World!
Hello,hello server,I`m client:6. Welcome to Netty World!
Hello,hello server,I`m client:7. Welcome to Netty World!
Hello,hello server,I`m client:8. Welcome to Netty World!
Hello,hello server,I`m client:9. Welcome to Netty World!
Hello,hello server,I`m client:10. Welcome to Netty World!

下一篇介绍使用netty编写自己的服务器

你可能感兴趣的:(netty学习专栏)