【Netty】服务端和客户端

栏目: Java · 发布时间: 5年前

内容简介:欢迎关注公众号:【如果有需要后台回复

欢迎关注公众号:【 爱编程

如果有需要后台回复 2019 赠送 1T的学习资料 哦!!

【Netty】服务端和客户端

本文是基于Netty4.1.36进行分析

服务端

Netty服务端的启动代码基本都是如下:

private void start() throws Exception {

        final EchoServerHandler serverHandler = new EchoServerHandler();
        /**
         * NioEventLoop并不是一个纯粹的I/O线程,它除了负责I/O的读写之外
         * 创建了两个NioEventLoopGroup,
         * 它们实际是两个独立的Reactor线程池。
         * 一个用于接收客户端的TCP连接,
         * 另一个用于处理I/O相关的读写操作,或者执行系统Task、定时任务Task等。
         */
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup childGroup = new NioEventLoopGroup();

        try {
            //ServerBootstrap负责初始化netty服务器,并且开始监听端口的socket请求
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, childGroup)
                    .channel(NioServerSocketChannel.class)
                    .localAddress(new InetSocketAddress(port))
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
//                            为监听客户端read/write事件的Channel添加用户自定义的ChannelHandler
                            socketChannel.pipeline().addLast(serverHandler);
                        }
                    });

            ChannelFuture f = b.bind().sync();

            f.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            bossGroup.shutdownGracefully().sync();
            childGroup.shutdownGracefully().sync();
        }
    }

从上图的代码可以总结为以下几个步骤:

1、创建ServerBootStrap实例

2、设置并绑定Reactor线程池:EventLoopGroup,EventLoop就是处理所有注册到本线程的Selector上面的Channel

3、设置并绑定服务端的channel

4、5、创建处理网络事件的ChannelPipeline和handler,网络时间以流的形式在其中流转,handler完成多数的功能定制:比如编解码 SSl安全认证

6、绑定并启动监听端口

7、当轮训到准备就绪的channel后,由Reactor线程:NioEventLoop执行pipline中的方法,最终调度并执行channelHandler

服务端创建时序图

【Netty】服务端和客户端

ServerBootStrap引导启动服务端

它就是主要引导启动服务端,工作包括以下:

  • 1.创建服务端Channel
  • 2.初始化服务端Channel
  • 3.将Channel注册到selector
  • 4.端口绑定

1.创建服务端Channel

流程:

首先从用户代码的bind()其实就是AbstractBootstrap.bind(),然后通过 反射 工厂将用户通过b.channel(NioServerSocketChannel.class)传入的NioServerSocketChannel通过调用底层的jdk的SelectorProvider创建 channel ,同时也接着创建好对应的 ChannelPipeline

详情可以参考下图,自己去查看一下源码:

【Netty】服务端和客户端

2.初始化服务端Channel

主要工作如下:

1)设置的option缓存到NioServerSocketChannelConfig里

2)设置的attr设置到channel里

3)保存配置的childOptions,配置的childAttrs 到ServerBootstrapAcceptor里

4)往NioSocketChannel的pipeline中添加一个 ServerBootstrapAcceptor

【Netty】服务端和客户端

主要的核心源码如下:

@Override
    void init(Channel channel) throws Exception {
        final Map<ChannelOption<?>, Object> options = options0();
        synchronized (options) {
            setChannelOptions(channel, options, logger);
        }

        final Map<AttributeKey<?>, Object> attrs = attrs0();
        synchronized (attrs) {
            for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) {
                @SuppressWarnings("unchecked")
                AttributeKey<Object> key = (AttributeKey<Object>) e.getKey();
                channel.attr(key).set(e.getValue());
            }
        }

        ChannelPipeline p = channel.pipeline();

        final EventLoopGroup currentChildGroup = childGroup;
        final ChannelHandler currentChildHandler = childHandler;
        final Entry<ChannelOption<?>, Object>[] currentChildOptions;
        final Entry<AttributeKey<?>, Object>[] currentChildAttrs;
        synchronized (childOptions) {
            currentChildOptions = childOptions.entrySet().toArray(newOptionArray(0));
        }
        synchronized (childAttrs) {
            currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(0));
        }

        p.addLast(new ChannelInitializer<Channel>() {
            @Override
            public void initChannel(final Channel ch) throws Exception {
                final ChannelPipeline pipeline = ch.pipeline();
                ChannelHandler handler = config.handler();
                if (handler != null) {
                    pipeline.addLast(handler);
                }

                ch.eventLoop().execute(new Runnable() {
                    @Override
                    public void run() {
                        pipeline.addLast(new ServerBootstrapAcceptor(
                                ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
                    }
                });
            }
        });
    }

小结:

总体如上面工作流程所述。

特别地建议:查看ServerBootstrapAcceptor源码,你可以发现ServerBootstrapAcceptor在channelRead事件触发的时候(也就有客户端连接的时候),把childHandler加到childChannel Pipeline的末尾,设置childHandler的options和attrs,最后把childHandler注册进childGroup

3.将Channel注册到selector

注册过程如下图

【Netty】服务端和客户端

小结:

Channel 注册过程所做的工作就是将 Channel 与对应的 EventLoop 关联。

1).每个 Channel 都会关联一个特定的 EventLoop, 并且这个 Channel 中的所有 IO 操作都是在这个 EventLoop 中执行的;

2).当关联好 Channel 和 EventLoop 后, 会继续调用底层的 Java NIO SocketChannel 的 register 方法, 将底层的 Java NIO SocketChannel 注册到指定的 selector 中.

通过这两步, 就完成了 Netty Channel 的注册过程.

4.端口绑定

端口绑定的源码流程基本如下图,详情可以还是你自己读一下源码比较好点。

【Netty】服务端和客户端

小结:

其实netty端口绑定是调用 jdk的javaChannel().bind(localAddress, config.getBacklog());进行绑定,然后TCP链路建立成功,Channel激活事件,通过channelPipeline进行传播。

客户端

客户端启动的常规代码如下:

private void start() throws Exception {

        /**
         * Netty用于接收客户端请求的线程池职责如下。
         * (1)接收客户端TCP连接,初始化Channel参数;
         * (2)将链路状态变更事件通知给ChannelPipeline
         */
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap b = new Bootstrap();
            b.group(group)
                    .channel(NioSocketChannel.class)
                    .remoteAddress(new InetSocketAddress(host,port))
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            socketChannel.pipeline().addLast(new EchoClientHandler());
                        }
                    });
            //绑定端口
            ChannelFuture f = b.connect().sync();

            f.channel().closeFuture().sync();
        } catch (Exception e) {
            group.shutdownGracefully().sync();
        }


    }

【Netty】服务端和客户端

流程:

1.用户线程创建Bootstrap实例,通过API设置创建客户端相关的参数,异步发起客户端连接。

2.创建处理客户端连接、I/O读写的Reactor线程组NioEventLoopGroup,默认为CPU内核数的2倍。

3.通过Bootstrap的ChannelFactory和用户指定的Channel类型创建用于客户端NioSocketChannel,它的功能类似于JDK NIO类库提供的SocketChannel

4.创建默认的Channel Handler Pipeline,用于调度和执行网路事件。

5.异步发起TCP连接,判断连接是否成功。如果成功,则直接将NioSocketChannel注册到多路复用器上,监听读操作位,用于数据包读取和消息发送,如果没有立即连接成功,则注册连接监听为到多路复用器,等待连接结果。

6.注册对应的网络监听状态为到多路复用器。

7.由多路复用器在I/O现场中轮询个Channel,处理连接结果。

8.如果连接成功,设置Future结果,发送连接成功事件,触发ChannelPipeline执行。

9.由ChannelPipeline调度执行系统和用户的ChannelHandler,执行逻辑。

源码调用流程如下图:

【Netty】服务端和客户端

小结:

客户端是如何发起 TCP 连接的?

如下图:

【Netty】服务端和客户端

特别提醒:

在AbstractChannelHandlerContext.connect()#findContextOutbound这步操作是返回的结果next其实是 头节点 ,也就是说在下一步next.invokeConnect()这里的next就是 头节点 ,所以最终是调用 HeadContext .connect()

总结

本文主要讲述netty服务端和客户端的简单工作流程。

具体服务端与客户端如何通信,以及内存管理等方面的知识下一次再写。

最后

如果对 Java 、大数据感兴趣请长按二维码关注一波,我会努力带给你们价值。觉得对你哪怕有一丁点帮助的请帮忙点个赞或者转发哦。

关注公众号 【爱编码】 ,回复 2019 有相关资料哦。

【Netty】服务端和客户端


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Cyberwar

Cyberwar

Kathleen Hall Jamieson / Oxford University Press / 2018-10-3 / USD 16.96

The question of how Donald Trump won the 2016 election looms over his presidency. In particular, were the 78,000 voters who gave him an Electoral College victory affected by the Russian trolls and hac......一起来看看 《Cyberwar》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具