转载自https://blog.csdn.net/zxhoo/article/details/17964353

Channel继承层次图
分析上面提到的三个状态的时候,会去看Channel继承层次里某些类的代码,为了方便参考,我画了一张(不太严格的)UML类图,如下所示:

open状态
先从isOpen()方法入手,isOpen()方法是在AbstractNioChannel抽象类里实现的,下面是这个类的关键代码:

public abstract class AbstractNioChannel extends AbstractChannel {
...
private final SelectableChannel ch;
...
    @Override
    public boolean isOpen() {
        return ch.isOpen();
    }
...
}
可以看出来,Netty的Channel是否open取决于Java的SelectableChannel是否open。换句话说,只要找出Netty何时open了这个SelectableChannel,就可以知道Channel何时到达了open状态。从Bootstrap的connect()方法开始顺藤摸瓜就能找出答案:
Bootstrap.connect(String inetHost, int inetPort)
-> Bootstrap.doConnect(final SocketAddress remoteAddress, final SocketAddress localAddress)
-> AbstractBootstrap.initAndRegister()
-> BootstrapChannelFactory.newChannel()
-> NioSocketChannel()
-> NioSocketChannel.newSocket()
-> SocketChannel.open()
重点看看initAndRegister()方法:
// AbstractBootstrap.java
final ChannelFuture initAndRegister() {
final Channel channel = channelFactory().newChannel();
try {
init(channel);
} catch (Throwable t) {
channel.unsafe().closeForcibly();
return channel.newFailedFuture(t);
}

ChannelPromise regPromise = channel.newPromise();
group().register(channel, regPromise);
...
        return regPromise;
    }
initAndRegister()方法先创建了Channel实例(此时Channel已经处于open状态),然后把它注册到group里,所以大概能够知道,Channel是在open之后进入registered状态的,如下图所示:

registered状态
为了证明上面的猜测,我们从NioEventLoopGroup.register()方法接着看代码。NioEventLoopGroup并没有实现register()方法,真正的实现是在它的超类MultithreadEventLoopGroup里:

// MultithreadEventLoopGroup.java
@Override
public ChannelFuture register(Channel channel, ChannelPromise promise) {
return next().register(channel, promise);
}
根据这篇文章的介绍,next()方法返回的是一个NioEventLoop,看代码后知道,register()方法是在NioEventLoop的超类,SingleThreadEventLoop里实现的:
// SingleThreadEventLoop.java
@Override
public ChannelFuture register(final Channel channel, final ChannelPromise promise) {
...
channel.unsafe().register(this, promise);
return promise;
}
好吧,继续看代码,知道调用的是AbstractChannel.AbstractUnsafe.register()方法,这个方法又调用了AbstractUnsafe.register0()方法,在register0()方法里,registered字段被设置为true。而AbstractChannel的isRegistered()方法正好是通过这个字段来判断是否是registered状态:
// AbstractChannel.java
@Override
public boolean isRegistered() {
return registered;
}
也就是说,上面的猜测是正确的,Channel先进入open状态,然后通过把自己注册到group进入registered状态。

active状态
还是先看看isActive()方法是如何实现的(在NioSocketChannel里):

// NioSocketChannel.java
@Override
public boolean isActive() {
SocketChannel ch = javaChannel();
return ch.isOpen() && ch.isConnected();
}
也就是说,NioSocketChannel的active状态取决于SocketChannel的状态。根据前面的分析知道,NioSocketChannel构造函数执行之后,SocketChannel已经处于open状态了,那么接下来就看SocketChannel的connect()方法是何时被调用的。回到Bootstrap类的doConnect()方法:
Bootstrap.connect(String inetHost, int inetPort)
-> Bootstrap.doConnect(final SocketAddress remoteAddress, final SocketAddress localAddress)
-> AbstractBootstrap.initAndRegister()
Bootstrap.doConnect0(...)
-> Channel.connect(SocketAddress remoteAddress, ChannelPromise promise
doConnect()方法在initAndRegister()之后又调用了doConnect0()方法,doConnect0()方法调用了Channel的connect()方法。在AbstractChannel里有connect()方法的实现:
// AbstractChannel.java
@Override
public ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise) {
return pipeline.connect(remoteAddress, promise);
}
也就是说,connect实际上是被当做事件交给pipeline去处理的,而且是个outbound事件,看DefaultChannelPipeline:
// DefaultChannelPipeline.java
@Override
public ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise) {
return tail.connect(remoteAddress, promise);
}
tail是DefaultChannelHandlerContext实例:
// DefaultChannelHandlerContext.java
@Override
public ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise) {
return connect(remoteAddress, null, promise);
}

@Override
public ChannelFuture connect(final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise promise) {
...
final DefaultChannelHandlerContext next = findContextOutbound();
EventExecutor executor = next.executor();
if (executor.inEventLoop()) {
next.invokeConnect(remoteAddress, localAddress, promise);
} else {
safeExecute(executor, new Runnable() {
@Override
public void run() {
next.invokeConnect(remoteAddress, localAddress, promise);
}
}, promise, null);
}

return promise;
}

private void invokeConnect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) {
        try {
            ((ChannelOutboundHandler) handler).connect(this, remoteAddress, localAddress, promise);
        } catch (Throwable t) {
            notifyOutboundHandlerException(t, promise);
        }
    }
三个参数版的connect()方法看起来很复杂,但无非就是做了两件事:先沿着pipeline往前找到第一个outbound类型的context,接着调用这个context的invokeConnect()方法。然后context又调用了handler的connect()方法,而pipeline里必定会有一个outbound类型的context/handler,这个context就是head,相应的handler是内部类HeadHandler:
// DefaultChannelPipeline.java
static final class HeadHandler implements ChannelOutboundHandler {
protected final Unsafe unsafe;

protected HeadHandler(Unsafe unsafe) {
        this.unsafe = unsafe;
    }
...
@Override
    public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) throws Exception {
        unsafe.connect(remoteAddress, localAddress, promise);
    }
...
}
HeadHandler只是调用了unsafe的connect()方法,unsafe是在构造函数里传进来的:
public DefaultChannelPipeline(AbstractChannel channel) {
...
HeadHandler headHandler = new HeadHandler(channel.unsafe());
head = new DefaultChannelHandlerContext(this, null, generateName(headHandler), headHandler);
...
}
Unsafe.connect()方法在AbstractNioChannel.AbstractNioUnsafe里实现,这个实现调用了AbstractNioChannel.doConnect()方法。doConnect()方法最终在NioSocketChannel里得以实现:
// NioSocketChannel.java
@Override
protected boolean doConnect(SocketAddress remoteAddress, SocketAddress localAddress) throws Exception {
if (localAddress != null) {
javaChannel().socket().bind(localAddress);
}

boolean success = false;
try {
boolean connected = javaChannel().connect(remoteAddress);
if (!connected) {
selectionKey().interestOps(SelectionKey.OP_CONNECT);
}
success = true;
return connected;
} finally {
if (!success) {
doClose();
}
}
}
结论
代码分析的很复杂,但结论很简单:被Bootstrap引导的NioSocketChannel在构造好之后就进入了open状态,之后通过把自己注册进EventLoop进入registered状态,接着连接服务器进入active状态。

---------------------
作者:zxh0
来源:CSDN
原文:https://blog.csdn.net/zxhoo/article/details/17964353
版权声明:本文为博主原创文章,转载请附上博文链接!

nioServerChannel的的状态的更多相关文章

  1. 【小程序分享篇 二 】web在线踢人小程序,维持用户只能在一个台电脑持登录状态

    最近离职了, 突然记起来还一个小功能没做, 想想也挺简单,留下代码和思路给同事做个参考. 换工作心里挺忐忑, 对未来也充满了憧憬与担忧.(虽然已是老人, 换了N次工作了,但每次心里都和忐忑). 写写代 ...

  2. Http状态码之:301、302重定向

    概念 301 Moved Permanently 被请求的资源已永久移动到新位置,并且将来任何对此资源的引用都应该使用本响应返回的若干个URI之一.如果可能,拥有链接编辑功能的客户端应当自动把请求的地 ...

  3. C# 利用性能计数器监控网络状态

    本例是利用C#中的性能计数器(PerformanceCounter)监控网络的状态.并能够直观的展现出来 涉及到的知识点: PerformanceCounter,表示 Windows NT 性能计数器 ...

  4. 无法向会话状态服务器发出会话状态请求。请确保 ASP.NET State Service (ASP.NET 状态服务)已启动,并且客户端端口与服务器端口相同。如果服务器位于远程计算机上,请检查。。。

    异常处理汇总-服 务 器 http://www.cnblogs.com/dunitian/p/4522983.html 无法向会话状态服务器发出会话状态请求.请确保 ASP.NET State Ser ...

  5. JavaScript var关键字、变量的状态、异常处理、命名规范等介绍

    本篇主要介绍var关键字.变量的undefined和null状态.异常处理.命名规范. 目录 1. var 关键字:介绍var关键字的使用. 2. 变量的状态:介绍变量的未定义.已定义未赋值.已定义已 ...

  6. 【.net 深呼吸】启动一个进程并实时获取状态信息

    地球人和火星人都知道,Process类既可以获取正在运行的进程,也可以启动一个新的进程.在79.77%应用场合,我们只需要让目标进程顺利启动就完事了,至于它执行了啥,有没有出错,啥时候退出就不管了. ...

  7. Android GridView 通过seletor 设置状态和默认状态

    Android中可以通过selector控制GridView Item 的状态,而省去使用代码控制 GridView View Selector Xml文件 <?xml version=&quo ...

  8. HTTP常用状态码分析

    不管是面试还是工作中,经常会碰到需要通过HTTP状态码去判断问题的情况,比如对于后台RD,给到前端FE的一个接口,出现502或者504 error错误,FE就会说接口存在问题,如果没有知识储备,那就只 ...

  9. 步入angularjs directive(指令)--点击按钮加入loading状态

    今天我终于鼓起勇气写自己的博客了,激动与害怕并存,希望大家能多多批评指导,如果能够帮助大家,也希望大家点个赞!! 用angularjs 工作也有段时间了,总体感觉最有挑战性的还是指令,因为没有指令的a ...

随机推荐

  1. 火题大战Vol.0 B 计数DP

    火题大战Vol.0 B 题目描述 \(n\) 个沙茶,被编号 \(1\)~$ n$.排完队之后,每个沙茶希望,自己的相邻的两人只要无一个人的编号和自己的编号相差为 \(1\)(\(+1\) 或\(-1 ...

  2. oracle replace的用法

    表数据里面有些数据是有换行或者特殊字符的,想要去掉,但是几千条记录要一条条改基本不可能. 后来想到了replace这个函数,具体用法如下: update 表1 t set t.列1=replace(( ...

  3. ansible实现批量建立互信

    Ansible:自动化运维工具 为什么要建立互信:ansible批量配置管理的前提是管理机和被管理机ssh互信,即通过将管理主机的公钥(id_rsa.pub)添加到目标主机上,实现管理机不通过交互式输 ...

  4. lx

    自我介绍:我是18软件技术5班 张震. -统计截止目前为止我自己写过代码3000行,我希望在本课结束后累计到10000行代码, 选择自测题17分,错题大多是运算符和运算符先 后级一类的题,编程题不会, ...

  5. 牛客网PAT练兵场-跟奥巴马一起编程

    题目地址: 题意:无 /** * *作者:Ycute *时间:2019-11-14-21.29.07 *题目题意简单描述:模拟题输出 */ #include<iostream> #incl ...

  6. 学生成绩管理系统(SSM+MySQL+JSP)

    开发工具:Eclipse前端技术:基础:html+css+JavaScript框架:JQuery+H-ui后端技术:Spring+SpringMVC+mybatis模板引擎:JSP数据库:mysql ...

  7. 深入了解Redis(3)-对象

    Redis主要的数据结构有简单动态字符串(SDS).双端链表.字典.压缩列表.整数集合,等等.但Redis并没有直接使用这些数据结构来实现键值对数据库, 而是基于这些数据结构创建了一个对象系统, 这个 ...

  8. Unity双开

    open -n /Applications/Unity/Unity.app

  9. C# OWC11

    public void OcwChart(int[] Data,string[] DataName,string Yname,string Xname,string ChartName,string ...

  10. Linux 基础指令2

    管道符| 先执行第一个命令后执行第二个 eg:查看文件中间几行: head -n1 文件名| tail -n2 :先查看文件前n1行,然后在n1行中的后n2行 tail -n1 文件名| head - ...