• netty入门02


    Netty4的代码比我想象的要复杂的多,不

    Netty4的代码比我想象的要复杂的多,不过Netty4很好的将这种复杂性隐藏了起来,暴露出来的,是一个相对容易使用的接口。Bootstrap就是Netty试图隐藏这种复杂性的一个例子。

    bootstrap包


    bootstrap包是Netty4代码里最简单的一个包,总共只有4个类:

    Bootstrap继承结构

    AbstractBootstrap是抽象类,有两个具体的实现,Bootstrap和ServerBootstrap:

    Bootstrap例子

    Netty4自带的例子里有一个EchoClient,看看它是如何使用Bootstrap启动一个客户端程序的:
    1. public class EchoClient {  
    2.   
    3.     private final String host;  
    4.     private final int port;  
    5.     private final int firstMessageSize;  
    6.   
    7.     public EchoClient(String host, int port, int firstMessageSize) {  
    8.         this.host = host;  
    9.         this.port = port;  
    10.         this.firstMessageSize = firstMessageSize;  
    11.     }  
    12.   
    13.     public void run() throws Exception {  
    14.         // Configure the client.  
    15.         EventLoopGroup group = new NioEventLoopGroup();  
    16.         try {  
    17.             Bootstrap b = new Bootstrap();  
    18.             b.group(group)  
    19.              .channel(NioSocketChannel.class)  
    20.              .option(ChannelOption.TCP_NODELAY, true)  
    21.              .handler(new ChannelInitializer<SocketChannel>() {  
    22.                  @Override  
    23.                  public void initChannel(SocketChannel ch) throws Exception {  
    24.                      ch.pipeline().addLast(  
    25.                              //new LoggingHandler(LogLevel.INFO),  
    26.                              new EchoClientHandler(firstMessageSize));  
    27.                  }  
    28.              });  
    29.   
    30.             // Start the client.  
    31.             ChannelFuture f = b.connect(host, port).sync();  
    32.   
    33.             // Wait until the connection is closed.  
    34.             f.channel().closeFuture().sync();  
    35.         } finally {  
    36.             // Shut down the event loop to terminate all threads.  
    37.             group.shutdownGracefully();  
    38.         }  
    39.     }  
    40.   
    41.     public static void main(String[] args) throws Exception {  
    42.     // ...  
    43.     }  
    44. }  

    Builder模式?

    看上面的例子,Bootstrap的使用很像Builder模式,Bootstrap就是Builder,EventLoopGroup、Channel和Handler等是各种Part。稍有不同的是,准备好各种Part后,并不是直接build出一个Product来,而是直接通过connect()方法使用这个Product。


    AbstractBootstrap

    为了弄清楚Bootstrap如何工作,我们先从AbstractBootstrap入手:

    1. public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, C extends Channel> implements Cloneable {  
    2.   
    3.     private volatile EventLoopGroup group;  
    4.     private volatile ChannelFactory<? extends C> channelFactory;  
    5.     private volatile SocketAddress localAddress;  
    6.     private final Map<ChannelOption<?>, Object> options = new LinkedHashMap<ChannelOption<?>, Object>();  
    7.     private final Map<AttributeKey<?>, Object> attrs = new LinkedHashMap<AttributeKey<?>, Object>();  
    8.     private volatile ChannelHandler handler;  
    9.     // ...  
    10. }  

    可以看到,AbstractBootstrap这个抽象Builder一共需要有6个Part,如下图所示:


    设置各个Part

    AbstractBootstrap有一组方法用来设置各个Part,例如下面这些:

    1. public B group(EventLoopGroup group)  
    2. public B channel(Class<? extends C> channelClass)  
    3. public B channelFactory(ChannelFactory<? extends C> channelFactory)  
    4. public B localAddress(SocketAddress localAddress)  
    5. public <T> B option(ChannelOption<T> option, T value)  
    6. public <T> B attr(AttributeKey<T> key, T value)  
    7. public B handler(ChannelHandler handler)  

    还有一组对应方法获得各个Part,如下:

    1. final SocketAddress localAddress()  
    2. final ChannelFactory<? extends C> channelFactory()  
    3. final ChannelHandler handler()  
    4. public final EventLoopGroup group()  
    5. final Map<ChannelOption<?>, Object> options()  
    6. final Map<AttributeKey<?>, Object> attrs()  

    ChannelFactory

    AbstractBootstrap通过ChannelFactory创建Channel实例,channel(channelClass)方法看起来好像是设置了一个Channel,但实际上只是设置了默认的ChannelFactory实现:

    1. public B channel(Class<? extends C> channelClass) {  
    2.     if (channelClass == null) {  
    3.         throw new NullPointerException("channelClass");  
    4.     }  
    5.     return channelFactory(new BootstrapChannelFactory<C>(channelClass));  
    6. }  

    默认的ChannelFactory实现使用反射创建Channel实例:

    1. private static final class BootstrapChannelFactory<T extends Channel> implements ChannelFactory<T> {  
    2.     private final Class<? extends T> clazz;  
    3.   
    4.     BootstrapChannelFactory(Class<? extends T> clazz) {  
    5.         this.clazz = clazz;  
    6.     }  
    7.   
    8.     @Override  
    9.     public T newChannel() {  
    10.         try {  
    11.             return clazz.newInstance();  
    12.         } catch (Throwable t) {  
    13.             throw new ChannelException("Unable to create Channel from class " + clazz, t);  
    14.         }  
    15.     }  
    16. }  

    Bootstrap.connect()

    再来看Bootstrap类的connect()方法:

    1. public ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress) {  
    2.     if (remoteAddress == null) {  
    3.         throw new NullPointerException("remoteAddress");  
    4.     }  
    5.     validate();  
    6.     return doConnect(remoteAddress, localAddress);  
    7. }  


    connect()方法调用validate()方法看各个Part是否准备就绪,然后调用doConnect()方法:


    1. private ChannelFuture doConnect(final SocketAddress remoteAddress, final SocketAddress localAddress) {  
    2.     final ChannelFuture regFuture = initAndRegister();  
    3.     final Channel channel = regFuture.channel();  
    4.     if (regFuture.cause() != null) {  
    5.         return regFuture;  
    6.     }  
    7.   
    8.     final ChannelPromise promise = channel.newPromise();  
    9.     if (regFuture.isDone()) {  
    10.         doConnect0(regFuture, channel, remoteAddress, localAddress, promise);  
    11.     } else {  
    12.         regFuture.addListener(new ChannelFutureListener() {  
    13.             @Override  
    14.             public void operationComplete(ChannelFuture future) throws Exception {  
    15.                 doConnect0(regFuture, channel, remoteAddress, localAddress, promise);  
    16.             }  
    17.         });  
    18.     }  
    19.   
    20.     return promise;  
    21. }  

    doConnect()方法首先调用了initAndRegister()方法,然后又调用了doConnect0()方法,方法调用示意图如下:


    AbstractBootstrap.initAndRegister()

    1. final ChannelFuture initAndRegister() {  
    2.     final Channel channel = channelFactory().newChannel();  
    3.     try {  
    4.         init(channel);  
    5.     } catch (Throwable t) {  
    6.         channel.unsafe().closeForcibly();  
    7.         return channel.newFailedFuture(t);  
    8.     }  
    9.   
    10.     ChannelPromise regPromise = channel.newPromise();  
    11.     group().register(channel, regPromise);  
    12.     // ...  
    13. }  
    initAndRegister()方法用ChannelFactory创建了一个Channel的实例,然后调用init()方法初始化Channel,最后将Channel注册到EventLoopGroup上:

    而Channel在实例化的时候已经自动关联了Pipeline,这点从AbstractChannel的构造函数可以看出:

    1. protected AbstractChannel(Channel parent) {  
    2.     this.parent = parent;  
    3.     unsafe = newUnsafe();  
    4.     pipeline = new DefaultChannelPipeline(this);  
    5. }  
     

    Bootstrap.init()方法

    1. void init(Channel channel) throws Exception {  
    2.     ChannelPipeline p = channel.pipeline();  
    3.     p.addLast(handler());  
    4.     // ...  
    5. }  
    Bootstrap.init()方法把Handler添加到了Pipeline的末尾,到这里,Channel就准备就绪了:

    继续Bootstrap.doConnect()

    initAndRegister()方法结束之后,doConnect()方法紧接着调用了doConnect0()方法,doConnect0()方法继而调用了Channel.connect()方法,这样Channel就接通服务器,可以收发消息了!


  • 相关阅读:
    [NOIP 2012] 疫情控制
    [HDU 6315] Naive Operations
    [BZOJ 3363] Cow Marathon
    单片机的模块化编程
    QT中定时器的使用方法
    47: error: undefined reference to `QWebView::QWebView(QWidget*)'
    如何分析一个QT类
    个人收集的一些库、工具、技术介绍
    点阵字体显示系列之二:汉字显示
    点阵字体显示系列之一:ASCII码字库的显示
  • 原文地址:https://www.cnblogs.com/xdrlczj/p/5845742.html
Copyright © 2020-2023  润新知