【本身讀源碼】Netty4.X系列(二) 啓動類成員Channel

廢話兩句

此次更新拖了很長時間,第一是本身生病了,第二是由於最開始這篇想寫的很大,而後構思了好久,發現不太合適把不少東西寫在一塊兒,因此作了點拆分,準備國慶前完成這篇博客。java

目的&&指望

開門見山,先說下這篇文章能學到什麼git

  • netty中channel的實現類的實現方式和做用
  • 本身動手寫netty框架的channel部分和bind方法,完成服務端的端口綁定

Bootstrap主要成員

上一篇咱們看了Bootstrap類的大體的組成,也瞭解了它的一些配置方法,其實都是在設置它的成員變量,有些是ServerBootstrap的也有是它的父類AbstractBootstrap的,在這裏,我以爲主要有下面幾個。github

  • EventLoopGroup
  • Channel
  • Handler

固然,這些只是它的一些接口,具體有多種實現,除了Nio,它也有Bio等其餘版本的,繼承鏈也很複雜,這裏咱們先看下channel的實現。bootstrap

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和它的準備狀態傳給父類構造器,而後父類構造器裏存儲到成員變量中。下面無恥的貼點源碼。socket

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有所瞭解了吧,如今咱們來開始實現本身的版本ide

本身動手

熱身

在這以前先說下bootstrap的bind方法,這邊netty實現的比較複雜,它是經過各類線程異步,還有它的一個內部類unsafe,最後調用到NioServerSocketChannel的doBind方法,doBind在調用Java Nio的bind方法。這裏咱們先實現簡單版本,它的關於線程的一些設計,咱們說到eventloop以後再說。oop


先來實現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,哈哈。

相關文章
相關標籤/搜索