废话两句

这次更新拖了很长时间,第一是自己生病了,第二是因为最开始这篇想写的很大,然后构思了很久,发现不太合适把很多东西写在一起,所以做了点拆分,准备国庆前完成这篇博客。

目的&&期望

开门见山,先说下这篇文章能学到什么

  • netty中channel的实现类的实现方式和作用
  • 自己动手写netty框架的channel部分和bind方法,完成服务端的端口绑定

Bootstrap主要成员

上一篇我们看了Bootstrap类的大致的组成,也了解了它的一些配置方法,其实都是在设置它的成员变量,有些是ServerBootstrap的也有是它的父类AbstractBootstrap的,在这里,我觉得主要有下面几个。

  • EventLoopGroup
  • Channel
  • Handler

当然,这些只是它的一些接口,具体有多种实现,除了Nio,它也有Bio等其他版本的,继承链也很复杂,这里我们先看下channel的实现。

NioServerSocketChannel

继承链

说到channel,它是Java Nio中的一个重要的成员,不过在netty中,channel是自己封装的一个更抽象的东西,在Nio版本的实现中,它也维护了Java Nio的channel,不过这些都是后话了,我们先看一下它的继承链,这里我自己画了一个简单的。
图片描述


这里可以看到,它的继承有两个分支,准确的说一条线是继承抽象类,一条线是实现接口,这么做,可以说是netty设计上的考虑,一方面,它是nio的channel,但是同时它又是server端的channel,所以netty选择了这样做来实现。

与Nio的摩擦

看到这里你可能会疑问,这个channel是netty自己封装的,那他是怎么和Nio的channel发生碰撞的呢?
首先,它在构造方法里利用Java Nio的selectProvider 打开了一个socketChannel,然后把这个Nio的Channel和它的准备状态传给父类构造器,然后父类构造器里存储到成员变量中。下面无耻的贴点源码。

    public NioServerSocketChannel() {
        this(newSocket(DEFAULT_SELECTOR_PROVIDER));
    }
    //这个DEFAULT_SELECTOR_PROVIDER其实就是select的provide
    private static final SelectorProvider DEFAULT_SELECTOR_PROVIDER = SelectorProvider.provider();
    //然后是newSocket打开了一个Channel
    private static ServerSocketChannel newSocket(SelectorProvider provider) {
        try {
            return provider.openServerSocketChannel();
        } catch (IOException e) {
            throw new ChannelException(
                    "Failed to open a server socket.", e);
        }
    }
    //最后是调用父类的构造
     public NioServerSocketChannel(ServerSocketChannel channel) {
        super(null, channel, SelectionKey.OP_ACCEPT);
        config = new NioServerSocketChannelConfig(this, javaChannel().socket());
    }

看到这些,我想大家对Channel有所了解了吧,现在我们来开始实现自己的版本

自己动手

热身

在这之前先说下bootstrap的bind方法,这边netty实现的比较复杂,它是通过各种线程异步,还有它的一个内部类unsafe,最后调用到NioServerSocketChannel的doBind方法,doBind在调用Java Nio的bind方法。这里我们先实现简单版本,它的关于线程的一些设计,我们说到eventloop之后再说。


先来实现Channel和Channel工厂类,方便我们动态new出不同的Channel。

public interface Channel {
    void bind(InetSocketAddress inetSocketAddress);
}

public interface ChannelFactory<T extends Channel>{

    T newChannel();
}

public class  ReflectChannelFactory<T extends Channel> implements ChannelFactory<T> {

    private final Class<? extends T> clazz;

    public ReflectChannelFactory(Class clazz) {
        this.clazz = clazz;
    }

    public T newChannel() {
        try {
            return clazz.newInstance();
        } catch (Exception e) {
            throw new IllegalArgumentException("can not init");
        }
    }

}

启动类

我们也学netty,把启动类抽象成两层,方便以后写客户端。

public abstract class AbstractBootstrap<B extends AbstractBootstrap<B,C>,C extends Channel> {

    private ChannelFactory<C> channelFactory;

    public B channel(Class<? extends C> clazz){
        this.channelFactory = new ReflectChannelFactory<C>(clazz);
        return (B)this;
    }

    public B bind(Integer port){
        validate();
        doBind(port);
        return (B)this;
    }

    private  void doBind(Integer port){
        Channel channel = channelFactory.newChannel();
        channel.bind(new InetSocketAddress(port));
    }

    public void validate(){
        if(channelFactory == null){
            throw new IllegalArgumentException("channelFactory should be initialized");
        }
    }
}
//服务端暂时没有用到额外的方法,先直接继承就OK了
public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap,Channel>{


}

Channel实现类

最后我们实现我们的NioChannel,相比netty,我们目前只做了两层抽象。

public abstract class AbstractNioChannel  implements Channel{
    private final SelectableChannel ch;


    public AbstractNioChannel(SelectableChannel ch) {
        this.ch = ch;
    }

    protected SelectableChannel ch() {
        return ch;
    }

    protected abstract void doBind(SocketAddress localAddress) throws Exception;

    public void bind(InetSocketAddress inetSocketAddress) {
        try {
            doBind(inetSocketAddress);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

public class NioServerSocketChannel extends AbstractNioChannel {

    public NioServerSocketChannel() {

       super(newSocket());
    }

    private static ServerSocketChannel newSocket(){
        try {
            return SelectorProvider.provider().openServerSocketChannel();
        } catch (IOException e) {
            throw new IllegalArgumentException("can not open socket");
        }
    }

    @Override
    protected ServerSocketChannel ch(){
        return (ServerSocketChannel) super.ch();
    }

    @Override
    protected  void doBind(SocketAddress localAddress) throws Exception{
        ch().socket().bind(localAddress);
    }
}

测试

最后写个测试类,来测试下

public class Test {
    public static void main(String[] args) {
        ServerBootstrap sb = new ServerBootstrap();
        sb.channel(NioServerSocketChannel.class).bind(9999);

    }
}

最后

当然,这个连残缺版netty都算不上,在绑定完端口后就自动结束了。别着急,我们慢慢来,下一篇我们会了解EventLoopGroup以及他的成员EventLoop,然后,完善我们的程序,增加其接收数据的能力。
文章的源码我会同步更新到我的gayHub上,欢迎大家star,哈哈。


pcChao
45 声望11 粉丝

一个菜鸟,专职是Java和大数据开发,兴趣是编程语言原理和linux操作系统