转载自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. Vue Axios 的封装使用

    目录 Axios 说明 安装 Axios 请求配置 响应结构 常用请求方法 默认值配置 全局的 请求配置项 自定义实例默认值 配置的优先顺序 拦截器 个人完整 axios 配置 Axios 说明 Ax ...

  2. JavaScript学习系列博客_33_JavaScript String对象

    String对象 在底层,字符串是以数组的形式保存的.比如说一个字符串"String"以["H","t","r",&qu ...

  3. 【测试技术分享】Liunx常用操作命令集合

    Linux命令 ls 查看文件目录内容 ls -lha  l:详细信息 h:人性化显示 a:查看隐藏目录 ls -目录名  查看指定目录 d rwx rwx rwx d:文件夹 -:文件 rwx:拥有 ...

  4. Clickhouse单机及集群部署详解

    一.ClickHouse简介 ClickHouse是近年来备受关注的开源列式数据库,主要用于数据分析(OLAP)领域.目前国内社区火热,各个大厂纷纷跟进大规模使用: 今日头条 内部用ClickHous ...

  5. undertow配置

    # 设置IO线程数, 它主要执行非阻塞的任务,它们会负责多个连接, 默认设置每个CPU核心一个线程 # 不要设置过大,如果过大,启动项目会报错:打开文件数过多 #server.undertow.io- ...

  6. 第七篇Scrum冲刺博客--Interesting-Corps

    第七篇Scrum冲刺博客 站立式会议 1.会议照片 2.队友完成情况 团队成员 昨日完成 今日计划 鲍鱼铭 各界面数据请求云函数设计及实现 代码交接及整体架构搭建 叶学涛 进行代码优化 和队友进行交接 ...

  7. ASP.NET Core3.1使用IdentityServer4中间件系列随笔(四):创建使用[ResourceOwnerPassword-资源所有者密码凭证]授权模式的客户端

    配套源码:https://gitee.com/jardeng/IdentitySolution 本篇将创建使用[ResourceOwnerPassword-资源所有者密码凭证]授权模式的客户端,来对受 ...

  8. Android-PullToRefresh上拉下拉刷新加载更多,以及gridview刷新功能的Library下载地址

    作者:程序员小冰,CSDN博客:http://blog.csdn.net/qq_21376985,转载请说明出处. 首先大家应该都听说过此开源框架的强大之处,支持单列以及双列的 上拉加载以及下拉刷新功 ...

  9. 关于action的使用在firefox报错的问题

    现在的网站有很多都是鼠标移到上面去才会显示出相应的一些标签之类的东西,然后再进行操作,但是因为要操作的元素一开始是隐藏的,就没办法直接定位,只能调用action来模拟鼠标悬停操作,也就是下面这句代码: ...

  10. 【jmespath】—4. 进阶 Flatten Projections

    这里要怎么翻呢?扁平投影? 一.Flatten Projections 1. 核心1 这个可以暂时忽略,一个重要的核心是:对于列表/对象投影,在投影中创建投影时,原始文档的结构将保留. 说人话就是,比 ...