Netty组件与编码

ServerSocketChannel.open() -> ServerSocketChannel

Selector.open() -> Selector

把ServerSocketChannel的accept事件注册给selector进行监听

accept事件就表示有一个客户端要向服务端进行连接

Selector.select()是一个阻塞函数,一直等待他监听的事件,这个函数触发代表着有一个SocketChannel要连接,然后我们把这个SocketChannel的read和write事件注册到selector上,此时selector就监听了三个事件

同时要给SocketChannel加key,当key可读时,就会读取key里面的ByteBuffer,按协议切割每一个完整消息包,通过反序列化转成具体的Object,再进行处理

以上是整个的读取流程

来看一个场景:

现在正常读取key1里面的数据,处理数据的过程中,有另一个SocketChannel要来进行连接,此时无法处理这个连接

为什么?

因为我们虽然是多路复用非阻塞模型,但是只有一个线程来进行处理,如果想处理accept事件,就要先处理完手中的read事件

为了在处理数据的同时还可以接收新的客户端连接,我们增加了一个Selector

selector1监听ServerSocketChannel的accept事件,selector2监听所有SocketChannel的read、write事件,对应两个线程分别持有两个selector,这样就把建立连接和读写事件分开了

那如果连接非常多,也就是SocketChannel非常多,一个读写线程可能就处理不过来这么多的读写事件了,我们可以把读写线程变成多个,把读写连接平衡地分给每一个selector,把这些处理读写事件的线程放到一个线程组里,称为worker线程组,之前的accetp线程称为boss线程(组)

为什么Boss线程只有一个:因为ServerSocketChannel只有一个

Netty组件与编码_第1张图片

线程是在一个循环中不断处理事件,可以起一个更有意义的名字 -> EventLoop事件循环,线程组也分别命名为boss EventLoopGroup、worker EventLoopGroup

Netty组件与编码_第2张图片

Netty组件与编码_第3张图片

ChannelHandler分为ChannelInboundHandler和ChannelOutboundHandler

对于每一个Channel,都表示一个Server和Client互相通信的通道,Server和Client都维护了一条处理器的链路,称为pipeline,这个管道里面就维护着若干个入站处理器和出站处理器。

在pipline中,每一个消息处理器都会被维护成一个双向链表的节点:

Netty组件与编码_第4张图片

读数据:入站处理器h1 -> h3 -> h5 -> h7 -> tail

写数据:出站处理器h6 -> h4 -> h2 -> head

组合pipline,handler,eventLoop,eventLoopGroup这几个组件,完成整个通信流程,称为BootStrap

编码

Server:

public class NettyServer {
    public static void main(String[] args) {
//        增加服务器收到消息后,持久化到数据库以及客户端断开连接后,打印messageList中所有内容的功能
        Map<Channel, List<String>> db = new ConcurrentHashMap<>();//模拟数据库

//        配置BootStrap,表示服务器接收到数据后,解成字符串并且打印
        ServerBootstrap serverBootstrap = new ServerBootstrap()
                .group(new NioEventLoopGroup(), new NioEventLoopGroup())
                .channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel sc) throws Exception {
                sc.pipeline().addLast(new LineBasedFrameDecoder(1024))//以换行符为标准解包 按换行符来分割出一条一条消息
                        .addLast(new StringDecoder())//添加解码器 反序列化 得到String
                        .addLast(new StringEncoder())
                        .addLast(new SimpleChannelInboundHandler<String>() { //泛型:处理什么类型的消息
                            @Override //真正读取了消息的函数
                            protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
                                System.out.println(msg);
//                                服务端也可以给客户端发消息 需要出站处理器
                                String message = msg + " world\n";
                                ctx.channel().writeAndFlush(message);
                                ctx.fireChannelRead(msg);
                            }
                        }).addLast(new SimpleChannelInboundHandler<String>() {
                            @Override
                            protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
                                List<String> messageList = db.computeIfAbsent(ctx.channel(), k -> new ArrayList<>());
                                messageList.add(msg);
                            }
                            @Override
                            public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
                                System.out.println(ctx.channel() + "注册了");
                            }
                            @Override
                            public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
                                System.out.println(ctx.channel() + "解除注册了");
                            }
                            @Override
                            public void channelActive(ChannelHandlerContext ctx) throws Exception {
                                System.out.println(ctx.channel() + "可以使用了");
                            }
                            @Override
                            public void channelInactive(ChannelHandlerContext ctx) throws Exception {
//                                channel不活跃
                                System.out.println(db.get(ctx.channel()));
                            }
                        });
            }
        });
//        netty中,所有异步的返回值,都需要用ChannelFuture来接收
        ChannelFuture bindFuture = serverBootstrap.bind(8080);
        bindFuture.addListener(f -> {
            if (f.isSuccess()) {
                System.out.println("服务器监听端口成功" + 8080);
            } else{
                System.out.println("服务器监听端口失败");
            }
        });
    }
}

重构一下:

public class NettyServer {
    public static void main(String[] args) {
//        增加服务器收到消息后,持久化到数据库以及客户端断开连接后,打印messageList中所有内容的功能
        Map<Channel, List<String>> db = new ConcurrentHashMap<>();//模拟数据库

//        配置BootStrap,表示服务器接收到数据后,解成字符串并且打印
        ServerBootstrap serverBootstrap = new ServerBootstrap()
                .group(new NioEventLoopGroup(), new NioEventLoopGroup())
                .channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel sc) throws Exception {
                sc.pipeline().addLast(new LineBasedFrameDecoder(1024))//以换行符为标准解包 按换行符来分割出一条一条消息
                        .addLast(new StringDecoder())//添加解码器 反序列化 得到String
                        .addLast(new StringEncoder())
                        .addLast(new responseHandler())
                        .addLast(new DBHandler(db));
            }
        });
//        netty中,所有异步的返回值,都需要用ChannelFuture来接收
        ChannelFuture bindFuture = serverBootstrap.bind(8080);
        bindFuture.addListener(f -> {
            if (f.isSuccess()) {
                System.out.println("服务器监听端口成功" + 8080);
            } else{
                System.out.println("服务器监听端口失败");
            }
        });
    }

    static class responseHandler extends SimpleChannelInboundHandler<String> {
        @Override //真正读取了消息的函数
        protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
            System.out.println(msg);
//                                服务端也可以给客户端发消息 需要出站处理器
            String message = msg + " world\n";
            ctx.channel().writeAndFlush(message);
            ctx.fireChannelRead(msg);
        }
    }

    static class DBHandler extends SimpleChannelInboundHandler<String> {
        private Map<Channel, List<String>> db;
        public DBHandler(Map<Channel, List<String>> db){
            this.db = db;
        }
        @Override
        protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
            List<String> messageList = db.computeIfAbsent(ctx.channel(), k -> new ArrayList<>());
            messageList.add(msg);
        }
        @Override
        public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
            System.out.println(ctx.channel() + "注册了");
        }
        @Override
        public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
            System.out.println(ctx.channel() + "解除注册了");
        }
        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            System.out.println(ctx.channel() + "可以使用了");
        }
        @Override
        public void channelInactive(ChannelHandlerContext ctx) throws Exception {
//                                channel不活跃
            System.out.println(db.get(ctx.channel()));
        }
    }
}

Client:

public class NettyClient {
    public static void main(String[] args) throws InterruptedException{
        Bootstrap bootstrap = new Bootstrap()
                .group(new NioEventLoopGroup())
                .channel(NioSocketChannel.class)
                .handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel sc) throws Exception {
                        sc.pipeline()
                                .addLast(new LineBasedFrameDecoder(1024))
                                .addLast(new StringEncoder())//将来字符串编码成字节数组
                                .addLast(new StringDecoder())
                                .addLast(new SimpleChannelInboundHandler<String>() {
                                    @Override
                                    protected void channelRead0(ChannelHandlerContext ctx, String msg) throws Exception {
                                        System.out.println(msg);
                                    }
                                });
                    }
                });
        ChannelFuture connect = bootstrap.connect("localhost", 8080);
        connect.addListener(f -> {
            if (f.isSuccess()) {
                System.out.println("连接服务器8080成功");
                EventLoop eventLoop = connect.channel().eventLoop();//EventLoop拥有执行定时任务的功能
//                每一秒钟写一个hello
                eventLoop.scheduleAtFixedRate(() -> {
                    connect.channel().writeAndFlush("hello" + System.currentTimeMillis() + "\n");
                }, 0, 1, TimeUnit.SECONDS);

            } else{
                System.out.println("连接服务器8080失败");
            }
        });
    }
}

你可能感兴趣的:(Netty组件与编码)