nio模拟netty

代码参考:https://github.com/wuyinxian124/nettybook2.git

netty底层调用了nio相关功能,但是由于netty代码量较大,将nio层层封装,导致没接触过nio开发的程序员无法快速理解netty底层究竟怎样调用nio,又对nio实现了什么封装。本篇文章尽可能(水平有限,只能做到"尽可能")在不依赖netty库的前提下模拟netty对nio的调用,尽量把每一行代码在netty中的相应位置阐释出来。

未完待续,部分注解后期补充。

接收服务端消息并打印

package io.netty.channel.nio;

import io.netty.util.internal.PlatformDependent;
import io.netty.util.internal.SocketUtils;

import java.io.IOException;
import java.lang.reflect.Field;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.channels.spi.AbstractSelector;
import java.nio.channels.spi.SelectorProvider;
import java.security.AccessController;
import java.security.PrivilegedAction;

class DemoApplicationTests3 {

    public static void main(String[] args) throws Throwable {
        /**
         * 关键对象:io.netty.channel.nio.NioEventLoop#selectedKeys
         * 用户层:
         * netty层:netty中将NioEventLoop#selectedKeys以反射的方式设置到selector中,这样selector中的key就可以通过NioEventLoop#selectedKeys直接看到
         *
         */
        SelectedSelectionKeySet selectedKeys = new SelectedSelectionKeySet();

        /**
         * 关键代码:io.netty.channel.nio.NioEventLoopGroup#NioEventLoopGroup(int, java.util.concurrent.Executor)
         * 用户层:初始化loopGroup
         * netty层:单例模式。
         */
        SelectorProvider provider = SelectorProvider.provider();
        /**
         * 关键代码:io.netty.channel.nio.NioEventLoop#openSelector()
         * 关键对象:io.netty.channel.nio.NioEventLoop#selector#delegate
         * 用户层:初始化loopGroup
         * netty层:初始化loopGroup的过程中会创建loop,创建loop的过程中会创建selector,并将其存储在NioEventLoop#selector#delegate。然后通过反射的方式修改selector中的selectedKeys
         */
        AbstractSelector unwrappedSelector = provider.openSelector();
        Object maybeSelectorImplClass = AccessController.doPrivileged(new PrivilegedAction<Object>() {
            @Override
            public Object run() {
                try {
                    return Class.forName(
                            "sun.nio.ch.SelectorImpl",
                            false,
                            PlatformDependent.getSystemClassLoader());
                } catch (Throwable cause) {
                    return cause;
                }
            }
        });
        Class<?> selectorImplClass = (Class<?>) maybeSelectorImplClass;
        Field selectedKeysField = selectorImplClass.getDeclaredField("selectedKeys");
        long selectedKeysFieldOffset = PlatformDependent.objectFieldOffset(selectedKeysField);
        PlatformDependent.putObject(
                unwrappedSelector, selectedKeysFieldOffset, selectedKeys);

        /**
         * 关键代码:sun.nio.ch.SelectorProviderImpl#openServerSocketChannel()
         * 关键对象:io.netty.channel.nio.AbstractNioChannel#ch
         * 用户层:服务端绑定端口
         * netty层:绑定端口时首先会创建channel(io.netty.bootstrap.AbstractBootstrap#initAndRegister()),
         *      netty创建NioServerSocketChannel时会同时创建ServerSocketChannel
         *      (io.netty.channel.socket.nio.NioServerSocketChannel#NioServerSocketChannel(java.nio.channels.spi.SelectorProvider, io.netty.channel.socket.InternetProtocolFamily)),
         *      创建好的ServerSocketChannel会被赋值到AbstractNioChannel#ch中(NioServerSocketChannel是AbstractNioChannel的子类)
         */
        ServerSocketChannel serverSocketChannel = provider.openServerSocketChannel();
        /**
         * 关键代码:io.netty.channel.nio.AbstractNioChannel#AbstractNioChannel(io.netty.channel.Channel, java.nio.channels.SelectableChannel, int)
         * 用户层:服务端绑定端口
         * netty层:在AbstractNioChannel#AbstractNioChannel对ServerSocketChannel进行初始化设置
         */
        serverSocketChannel.configureBlocking(false);


        /**
         * 关键代码:io.netty.channel.nio.AbstractNioChannel#doRegister()
         * 关键对象:io.netty.channel.nio.AbstractNioChannel#selectionKey
         * 用户层:服务端绑定端口
         * netty层:
         * 主线程中调用io.netty.channel.AbstractChannel.AbstractUnsafe#register(io.netty.channel.EventLoop, io.netty.channel.ChannelPromise)
         */
        SelectionKey key = serverSocketChannel.register(unwrappedSelector, 0, null);
        /**
         * 关键代码:io.netty.channel.socket.nio.NioServerSocketChannel#doBind(java.net.SocketAddress)
         * io.netty.bootstrap.AbstractBootstrap#doBind(java.net.SocketAddress)
         * 用户层:服务端绑定端口
         * netty层:两次任务提交
         *      io.netty.util.concurrent.DefaultPromise#notifyListeners()
         *      io.netty.bootstrap.AbstractBootstrap#doBind0(io.netty.channel.ChannelFuture, io.netty.channel.Channel, java.net.SocketAddress, io.netty.channel.ChannelPromise)
         *      主线程中,绑定端口时会调用AbstractBootstrap#doBind注册listener,listener的目的是为了调用AbstractBootstrap#doBind0
         *      添加完listener之后会尝试触发所有listener(根据条件判断立刻执行or抛给loop),触发io.netty.util.concurrent.DefaultPromise#notifyListenersNow(),将一个新的任务添加到loop
         *      新的任务会调用AbstractBootstrap#doBind0将操作打包成任务抛给channel所属的loop(io.netty.channel.AbstractChannel#eventLoop)里
         */
        serverSocketChannel.bind(SocketUtils.socketAddress("127.0.0.1", 9004), 200);


        /**
         * 关键代码:io.netty.channel.nio.AbstractNioChannel#doBeginRead()
         * 用户层:服务端绑定端口
         * netty层:
         * 主线程中,调用io.netty.bootstrap.AbstractBootstrap#doBind(java.net.SocketAddress)添加listener,其中doBind0(regFuture, channel, localAddress, promise);是触发下一步逻辑的代码
         * loop任务中,调用io.netty.bootstrap.AbstractBootstrap#doBind0(io.netty.channel.ChannelFuture, io.netty.channel.Channel, java.net.SocketAddress, io.netty.channel.ChannelPromise)
         * loop任务中,调用io.netty.channel.AbstractChannel.AbstractUnsafe#bind(java.net.SocketAddress, io.netty.channel.ChannelPromise)
         * fireChannelActive事件中调用io.netty.channel.nio.AbstractNioChannel#doRegister()
         */
        key.interestOps(SelectionKey.OP_ACCEPT);


        while (true) {

            /**
             * 关键代码:io.netty.channel.nio.SelectedSelectionKeySetSelector#select()
             * netty层:调用selector前清空key的集合,避免之前处理过的key又重复触发selector
             */
            selectedKeys.reset();
            /**
             * 关键代码:io.netty.channel.nio.SelectedSelectionKeySetSelector#select()
             * netty层:loop中调用select(),若无待处理的channel,就阻塞,直到有新事件到达。新事件通过NioEventLoop#selectedKeys获取
             */
            int select = unwrappedSelector.select();
            System.out.println("asdaaa:" + select);

            /**
             * 关键代码:io.netty.channel.nio.NioEventLoop#processSelectedKeysOptimized()
             * netty层:遍历key集合,处理key
             */
            for (int i = 0; i < selectedKeys.size; ++i) {
                final SelectionKey k = selectedKeys.keys[i];
                selectedKeys.keys[i] = null;
                /**
                 * 关键代码:io.netty.channel.nio.NioEventLoop#processSelectedKey(java.nio.channels.SelectionKey, io.netty.channel.nio.AbstractNioChannel)
                 * netty层:NioEventLoop#processSelectedKeysOptimized中遍历key之后,会调用NioEventLoop#processSelectedKey处理key
                 */
                handleInput(k, unwrappedSelector);
            }
            System.out.println("over");
        }
    }


    private static void handleInput(SelectionKey key, Selector selector) throws IOException {

        if (key.isValid()) {
            // 处理新接入的请求消息
            if (key.isAcceptable()) {
                // Accept the new connection
                ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
                SocketChannel sc = ssc.accept();
                sc.configureBlocking(false);
                // Add the new connection to the selector
                sc.register(selector, SelectionKey.OP_READ);
            }
            if (key.isReadable()) {
                // Read the data
                SocketChannel sc = (SocketChannel) key.channel();
                ByteBuffer readBuffer = ByteBuffer.allocate(1024);
                int readBytes = sc.read(readBuffer);
                if (readBytes > 0) {
                    readBuffer.flip();
                    byte[] bytes = new byte[readBuffer.remaining()];
                    readBuffer.get(bytes);
                    String body = new String(bytes, "UTF-8");
                    System.out.println("The time server receive order : "
                            + body);
                } else if (readBytes < 0) {
                    // 对端链路关闭
                    key.cancel();
                    sc.close();
                } else {
                    // 读到0字节,忽略
                }

            }
        }
    }

    private static void doWrite(SocketChannel channel, String response)
            throws IOException {
        if (response != null && response.trim().length() > 0) {
            byte[] bytes = response.getBytes();
            ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length);
            writeBuffer.put(bytes);
            writeBuffer.flip();
            channel.write(writeBuffer);
        }
    }

}

以上代码包路径一定必须是io.netty.channel.nio,否则无法使用SelectedSelectionKeySet

客户端,负责链接服务端并发送消息

package io.netty.channel.nio;

import io.netty.util.internal.PlatformDependent;

import java.io.IOException;
import java.lang.reflect.Field;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.channels.spi.AbstractSelector;
import java.nio.channels.spi.SelectorProvider;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

class DemoApplicationTests2 {


    private static Thread thread = new Thread(() -> {
        try {
            while (true) {
                Thread.sleep(1000);
                doWrite(DemoApplicationTests2.socketChannel, "wjwljwjlj");
                System.out.println("send msg");
            }
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
    });
    private static SocketChannel socketChannel;

    public static void main(String[] args) throws Throwable {
        /**
         * 关键对象:io.netty.channel.nio.NioEventLoop#selectedKeys
         * 用户层:
         * netty层:netty中将NioEventLoop#selectedKeys以反射的方式设置到selector中,这样selector中的key就可以通过NioEventLoop#selectedKeys直接看到
         */
        SelectedSelectionKeySet selectedKeys = new SelectedSelectionKeySet();

        /**
         * 关键代码:io.netty.channel.nio.NioEventLoopGroup#NioEventLoopGroup(int, java.util.concurrent.Executor)
         * 用户层:初始化loopGroup
         * netty层:单例模式。
         */
        SelectorProvider provider = SelectorProvider.provider();
        /**
         * 关键代码:io.netty.channel.nio.NioEventLoop#openSelector()
         * 关键对象:io.netty.channel.nio.NioEventLoop#selector#delegate
         * 用户层:初始化loopGroup
         * netty层:初始化loopGroup的过程中会创建loop,创建loop的过程中会创建selector,并将其存储在NioEventLoop#selector#delegate。然后通过反射的方式修改selector中的selectedKeys
         */
        AbstractSelector unwrappedSelector = provider.openSelector();
        Object maybeSelectorImplClass = AccessController.doPrivileged(new PrivilegedAction<Object>() {
            @Override
            public Object run() {
                try {
                    return Class.forName(
                            "sun.nio.ch.SelectorImpl",
                            false,
                            PlatformDependent.getSystemClassLoader());
                } catch (Throwable cause) {
                    return cause;
                }
            }
        });
        Class<?> selectorImplClass = (Class<?>) maybeSelectorImplClass;
        Field selectedKeysField = selectorImplClass.getDeclaredField("selectedKeys");
        long selectedKeysFieldOffset = PlatformDependent.objectFieldOffset(selectedKeysField);
        PlatformDependent.putObject(
                unwrappedSelector, selectedKeysFieldOffset, selectedKeys);


        /**
         * 关键代码:io.netty.channel.socket.nio.NioSocketChannel#newChannel(java.nio.channels.spi.SelectorProvider, io.netty.channel.socket.InternetProtocolFamily)
         * 关键对象:io.netty.channel.nio.AbstractNioChannel#ch
         * 用户层:主线程中客户端创建链接
         * netty层:初始化NioSocketChannel时会初始化SocketChannelImpl,此时SocketChannelImpl处于unconnected状态。然后将SocketChannelImpl赋值到AbstractNioChannel#ch
         */
        SocketChannel socketChannel = provider.openSocketChannel();
        /**
         * 关键代码:io.netty.channel.nio.AbstractNioChannel#AbstractNioChannel(io.netty.channel.Channel, java.nio.channels.SelectableChannel, int)
         * 用户层:主线程中客户端创建链接
         * netty层:初始化SocketChannelImpl之后会进行一些简单的设置,比如设置阻塞状态
         */
        socketChannel.configureBlocking(false);


        /**
         * 关键代码:io.netty.channel.nio.AbstractNioChannel#doRegister()
         * 关键对象:io.netty.channel.nio.AbstractNioChannel#selectionKey
         * 用户层:主线程中客户端创建链接
         * netty层:
         *      添加任务io.netty.channel.AbstractChannel.AbstractUnsafe#register(io.netty.channel.EventLoop, io.netty.channel.ChannelPromise)
         *      主线程中客户端创建链接时会添加任务,之后会在loop线程中将生成的key保存在AbstractNioChannel#selectionKey中
         */
        SelectionKey key = socketChannel.register(unwrappedSelector, 0, null);
        // 如果直接连接成功,则注册到多路复用器上,发送请求消息,读应答
        if (socketChannel.connect(new InetSocketAddress("127.0.0.1", 9004))) {
            /**
             * 关键代码:io.netty.channel.nio.AbstractNioChannel#doBeginRead()
             * 用户层:
             * netty层:
             *      客户端接收到服务端返回的OP_CONNECT之后,
             *      在io.netty.channel.nio.NioEventLoop#processSelectedKey(java.nio.channels.SelectionKey, io.netty.channel.nio.AbstractNioChannel)
             *      触发active事件,然后在责任链io.netty.channel.DefaultChannelPipeline.HeadContext#channelActive(io.netty.channel.ChannelHandlerContext)中
             *      将OP_READ设置到key中
             *
             *      由于AbstractNioChannel#doBeginRead是增量修改,所以之前设置的SelectionKey.OP_CONNECT
             *      会在io.netty.channel.nio.NioEventLoop#processSelectedKey(java.nio.channels.SelectionKey, io.netty.channel.nio.AbstractNioChannel)
             *      清除
             */
            key.interestOps(SelectionKey.OP_READ);
        } else {
            /**
             * 关键代码:io.netty.channel.socket.nio.NioSocketChannel#doConnect(java.net.SocketAddress, java.net.SocketAddress)
             * 用户层:主线程中客户端创建链接
             * netty层:
             *      添加任务io.netty.channel.AbstractChannel.AbstractUnsafe#register(io.netty.channel.EventLoop, io.netty.channel.ChannelPromise)
             *      添加监听io.netty.bootstrap.Bootstrap#doResolveAndConnect(java.net.SocketAddress, java.net.SocketAddress)
             *      触发监听io.netty.util.concurrent.DefaultPromise#setValue0(java.lang.Object)
             *      添加任务io.netty.bootstrap.Bootstrap#doConnect(java.net.SocketAddress, java.net.SocketAddress, io.netty.channel.ChannelPromise)
             *
             */
            key.interestOps(SelectionKey.OP_CONNECT);
        }


        /**
         * 为了便于发送消息的线程可以获取到channel,所以将channel赋值到静态变量中
         */
        DemoApplicationTests2.socketChannel = socketChannel;

        while (true) {

            /**
             * 关键代码:io.netty.channel.nio.SelectedSelectionKeySetSelector#select()
             * netty层:调用selector前清空key的集合,避免之前处理过的key又重复触发selector
             */
            selectedKeys.reset();
            /**
             * 关键代码:io.netty.channel.nio.SelectedSelectionKeySetSelector#select()
             * netty层:loop中调用select(),若无待处理的channel,就阻塞,直到有新事件到达。新事件通过NioEventLoop#selectedKeys获取
             */
            int select = unwrappedSelector.select();
            System.out.println("asdaaa:" + select);

            /**
             * 关键代码:io.netty.channel.nio.NioEventLoop#processSelectedKeysOptimized()
             * netty层:遍历key集合,处理key
             */
            for (int i = 0; i < selectedKeys.size; ++i) {
                final SelectionKey k = selectedKeys.keys[i];
                selectedKeys.keys[i] = null;
                /**
                 * 关键代码:io.netty.channel.nio.NioEventLoop#processSelectedKey(java.nio.channels.SelectionKey, io.netty.channel.nio.AbstractNioChannel)
                 * netty层:NioEventLoop#processSelectedKeysOptimized中遍历key之后,会调用NioEventLoop#processSelectedKey处理key
                 */
                handleInput(k, unwrappedSelector);
            }
            System.out.println("over");

            /**
             * 开启线程,开始向服务端发送数据
             */
            if (!DemoApplicationTests2.thread.isAlive()) {
                System.out.println("thread start");
                DemoApplicationTests2.thread.start();
            }
        }
    }


    private static void handleInput(SelectionKey key, Selector selector) throws IOException {
        if (key.isValid()) {
            // 判断是否连接成功
            SocketChannel sc = (SocketChannel) key.channel();
            if (key.isConnectable()) {
                if (sc.finishConnect()) {
                    sc.register(selector, SelectionKey.OP_READ);
                    doWrite(sc, "connected");
                } else
                    System.exit(1);// 连接失败,进程退出
            }
            if (key.isReadable()) {
                ByteBuffer readBuffer = ByteBuffer.allocate(1024);
                int readBytes = sc.read(readBuffer);
                if (readBytes > 0) {
                    readBuffer.flip();
                    byte[] bytes = new byte[readBuffer.remaining()];
                    readBuffer.get(bytes);
                    String body = new String(bytes, "UTF-8");
                    System.out.println("Now is : " + body);
//                    this.stop = true;
                } else if (readBytes < 0) {
                    // 对端链路关闭
                    key.cancel();
                    sc.close();
                } else
                    ; // 读到0字节,忽略
            }
        }
    }

    private static void doWrite(SocketChannel channel, String response)
            throws IOException {
        if (response != null && response.trim().length() > 0) {
            byte[] bytes = response.getBytes();
            ByteBuffer writeBuffer = ByteBuffer.allocate(bytes.length);
            writeBuffer.put(bytes);
            writeBuffer.flip();
            channel.write(writeBuffer);
        }
    }

}


任务

添加任务时会调用io.netty.channel.nio.SelectedSelectionKeySetSelector#wakeup终止selector的阻塞状态。

  • 10
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值