Netty源码解读-server端(一)

一、回顾NIO中的server

下面是我在学习nio时,写的selctor版本的服务端,具体代码如下:

public static void nioSelectorServer() throws Exception{

        //1。创建Selector
        Selector selector = Selector.open();

        ServerSocketChannel ssc = ServerSocketChannel.open();
        ssc.configureBlocking(false);//默认是true,设置为false就是非阻塞模式(下面的accept方法就会变成非阻塞)

        //2。建立selector 和 channel 的联系(注册)
        //SelectionKey 就是将来事件发生后,通过它可以知道事件和哪个channel的事件
        SelectionKey ssckey = ssc.register(selector, 0, null);
        // key 只关注 accept 事件
        ssckey.interestOps(SelectionKey.OP_ACCEPT);
        log.info("register key:{}",ssckey);

        ssc.bind(new InetSocketAddress(8080));

        while (true) {
            //3.select当啊,没有事件发生,线程阻塞。有事件发生,线程才会恢复运行
            //在事件未处理时,select不会阻塞。( 处理有两种方法:key.cancel() 和 key.chaneel().accept()  )
            selector.select();
            //4.处理事件,selectedKeys 内部包含了所有发生的事件
            Iterator<SelectionKey> iter = selector.selectedKeys().iterator();
            while (iter.hasNext()) {
                SelectionKey key = iter.next();
                //!!!处理key时,要从selectedKey集合中删除,否则下厨处理可能抛出空指针(至于为什么可以看image/1.png)
                iter.remove();
                log.info("key: {}",key);
                //5.区分事件类型
                if (key.isAcceptable()) {
                    //如果下面三行取消,whilet(true)代码块就会一直运行,打印上面的log
                    ServerSocketChannel channel = (ServerSocketChannel)key.channel();
                    SocketChannel sc = channel.accept();//处理事件
                    sc.configureBlocking(false);
                    SelectionKey scKey = sc.register(selector, 0, null);
                    scKey.interestOps(SelectionKey.OP_READ);
                    log.info("{}",sc);
                }else if (key.isReadable()) {
                    try {
                        SocketChannel channel = (SocketChannel) key.channel();//拿到触发事件的channel
                        ByteBuffer buffer = ByteBuffer.allocate(16);
                        int read = channel.read(buffer);//如果是正常断开,read方法返回值是-1
                        if (read == -1){
                            key.cancel();//!!!!
                        }else {
                            buffer.flip();
                            System.out.println(Charset.defaultCharset().decode(buffer));
                        }
                    }catch (IOException e) {
                        e.printStackTrace();
                        //!!!因为客户端断开了,因此需要将key取消(从selector 的 keys 集合中真正删除key)
                        key.cancel();
                    }
                }
            }
        }

    }

从上述代码中,我们可以看到几个重要的步骤:
在这里插入图片描述

二、Bind源码分析

1.大体观摩

下面是我编写的netty版本server端的demo
在这里插入图片描述
咱可以重点看一下doBind方法中调用的下面这两个方法,注意线程是怎么从main线程变成Nio线程的
在这里插入图片描述

2.initAndRegister方法

2.1 initAndRegister方法可以大体上看成下面两个部分,上面是init,下面是register
在这里插入图片描述

2.2 init相关步骤

2.2.1往下追channelFactory.newChannel()的底层,发现是init其实就是通过反射的方式调用构造方法:
在这里插入图片描述
2.2.2上面这个初始化的方法其实会调用到NioServerSocketChannel的构造方法中去,下面是其构造方法的部分源码:
从中可以得知nio原生的ServerSocketChannel.open操作是在此执行的。
在这里插入图片描述

在这里插入图片描述

2.2.3回到initAndRegister方法,咱们在看看其调用的init()做了什么事情?
答:主要就是给NioServerSocketChanel添加了一个handler处理器
在这里插入图片描述

小结:init相关操作主要做了两件事:
1.创建出了NioServerSocketChannel对象,ServerSocketChannel.open()
2.为NioServerSocketChannel对象添加了一个handler处理器,等待后续调用执行






2.3 register相关步骤

2.3.1回到initAndRegister方法,咱继续看 config().group().register(channel); 方法的底层,需要追很深哦
可以发现在这里面发生了线程的切换,注意切换为Nio线程执行时,main线程会继续往下执行,这里先提一嘴留个影响。

在这里插入图片描述
2.3.2继续追register0,首先看其内部调用的doRegister
从中可以得知nio原生的ServerSocketChannel的register方法在此执行
在这里插入图片描述
2.3.3继续看register0中调用的 pipeline.invokeHandlerAddedIfNeeded() 的源码
这个方法其实就是执行了2.2.3步骤中添加的handler,为ServerSocketChannel添加acceptHandler,当accept事件发生后建立链接
在这里插入图片描述

2.3.4继续看register0中调用的 safeSetSuccess(promise)
这个方法的作用是给promise对象赋值,好让doBind方法中的main线程继续执行后续逻辑
在这里插入图片描述
小结:register相关操作主要做了3件事:
1.将serverSocketChannel绑定到了Selector上
2.执行了前面init方法中准备好的handler,目的是给NioServerSocketChannel添加一个关注accept事件的handler
3.当前面两部都完成后,让dobind方法中的main线程继续执行doBind0

3.doBind0方法

上面2.3.4可以得知执行到这一步时NioServerSocket以及绑定了一个Accept事件
dobind0其底层调用到了AbstractChannel中的bind方法
在这里插入图片描述

3.1dobind方法底层

底层相当于就是执行nio原生的ServerSocketChannel的bind方法
在这里插入图片描述

3.2invokeLater方法底层

它会去执行每个handler的active方法,重点是看head头节点的active
在这里插入图片描述
从其底层可以看出是调用到了nio的原生方法selectionKey.interestOps,让selector关注到accept事件
在这里插入图片描述
小结:dobind0主要做了2件事:
1.执行ServerSocketChannel的bind方法,绑定端口号
2.执行selectionKey.interestOps,关注Accept事件

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

键盘歌唱家

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值