这一节我们来一起看下,一个客户端接入进来是什么情况。首先我们根据之前的分析,先启动服务端,然后打一个断点。

这个断点打在哪里呢?就是NioEventLoop上的select方法上。

然后我们启动一个客户端。

然后我们debug看到,selectedKey的数量 = 1,说明有accept或者读写等事件发生。

接下就会进 processSelectedKeys()

我们上一节讲到,这里的attach就是NioServerSocketChannel, 我们进入 processSelectedKey() 方法

重点来了,这里对各种事件进行分发,debug看我们现在是16,也就是说是accept事件。

继续跟进去F5进去

发现进入到了AbstractNioMessageChannel的read方法。这里进入的是 AbstractNioMessageChannel !!!;

为了加深印象,我们先暂停,重新跑一个读事件

发现当前的selectKey = 1,也就是读事件

F5 进入

发现,读事件进入到AbstractNioByteChannel中,

那么也就是accept进入  AbstractNioMessageChannel , 而 read 进入AbstractNioByteChannel 中。

好了,我们回过头来继续看 AbstractNioMessageChannel 中的read 方法。

关于这个分配器,这里我先不看,待后面分析内存模型的时候再说。我们直接看下面的代码:

我们发现这个是一个循环,不断的调用doReadMessages方法,并且传入了一个readBuf,而且这个readBuf是一个ArrayList, 那这里我们猜测可能是把读取到的客户端放到List集合中保存,然后再循环处理客户端连接。

进入doReadMessage方法一探究竟。

通过工具类调用 NioServerSocketChannel 内部封装的 serverSocketChannel 的 accept 方法,获取一个SocketChannel, 如果大家还记得我第一篇讲NIO的地方Netty源码分析--NIO(一),这里应该会有印象,我这里贴出来:

这里就是获取到了客户端的socketChannel

然后这里将SocketChannel封装成了一个NioSocketChannel,然后添加到了readBuf这个ArrayList中存储。

封装这里,这里不想多说了,跟创建NioServerSocketChannel类似,大家可以去回顾一下 Netty源码分析--创建Channel(三)

继续往下看,这里就是循环readBuf,链式执行 管道中的 handler 的 ChannelRead 方法。

根据前面几篇的分析,我们知道 , pipeline 里面又 4 个 handler ,分别是 Head,LoggingHandler,ServerBootstrapAcceptor,Tail,链式调用其中的 ChannelRead  方法,这里我们着重看  ServerBootstrapAcceptor 中个的 ChannelRead  方法。

这里为刚刚的客户端channel 添加了handler,设置了options和childAttrs,注意这里的addLast方法,并没有调用initChannel方法,具体这里添加了什么,我前面几篇有提及,大家可以再回顾一下。

接下来这里呢,就是把客户端channel注册到多路复用器上,跟服务端channel注册的流程是一样的。大家可以去看

Netty源码分析--Channel注册(上)(五)

Netty源码分析--Channel注册(中)(六)

Netty源码分析--Channel注册&绑定端口(下)(七)

我们直接说重点:

注册完成之后,就进入到了 pipeline.invokeHandlerAddedIfNeeded() 方法,我们跟下这个代码。

跟下去我们会进入上图这个execute()方法,如上图,我们看下  ctx

那么链式结构也就是 HeadContext -> NettyServer【ChannelInitializer】(我的启动类) -> TailContext

不断的跟下去,我们发现其实就是去调用当初我们在NettyServer中的initChannel。

那么也就是说,这里才是真正往pipeline中添加handler的过程!!!

神奇的是,后面还有一个remove方法。

那么这个是啥意思呢?我们再来看下ctx的链式结构

也就是说变成了 HeadContext - > NettyServer ->  IdleStateHandler ...等 -> TailContext

大家发现了吗? NettyServer 还在, 也就是  ChannelInitializer 这个handler 还在链上,但是它的作用已经结束了,没错,这里删除的就是它。

怎么删的就不说了,无非就是把 ChannelInitializer 两段的链表直接连接起来,把 ChannelInitializer  剔除就可以了。

接下来就是  pipeline.fireChannelRegistered(); 和 pipeline.fireChannelActive();

就是在所有的handler中链式调用channelRegister 和 channelActive方法。

总结一下:ServerBootstrapAcceptor 才是那个负责接收客户端连接,并且将其注册到多路复用器上的核心类

那么到这里,客户端的接入就完成了,下一篇我们来看,客户端的读写过程以及Netty的内存模型是什么样子的。

Netty源码分析-- 处理客户端接入请求(八)的更多相关文章

  1. zookeeper源码分析之三客户端发送请求流程

    znode 可以被监控,包括这个目录节点中存储的数据的修改,子节点目录的变化等,一旦变化可以通知设置监控的客户端,这个功能是zookeeper对于应用最重要的特性,通过这个特性可以实现的功能包括配置的 ...

  2. 【Netty源码分析】客户端connect服务端过程

    上一篇博客[Netty源码分析]Netty服务端bind端口过程 我们介绍了服务端绑定端口的过程,这一篇博客我们介绍一下客户端连接服务端的过程. ChannelFuture future = boos ...

  3. Netty源码分析之客户端启动过程

    一.先来看一下客户端示例代码. public class NettyClientTest { public void connect(int port, String host) throws Exc ...

  4. 【Netty源码分析】发送数据过程

    前面两篇博客[Netty源码分析]Netty服务端bind端口过程和[Netty源码分析]客户端connect服务端过程中我们分别介绍了服务端绑定端口和客户端连接到服务端的过程,接下来我们分析一下数据 ...

  5. Netty源码分析第3章(客户端接入流程)---->第1节: 初始化NioSockectChannelConfig

    Netty源码分析第三章: 客户端接入流程 概述: 之前的章节学习了server启动以及eventLoop相关的逻辑, eventLoop轮询到客户端接入事件之后是如何处理的?这一章我们循序渐进, 带 ...

  6. Netty源码分析第3章(客户端接入流程)---->第2节: 处理接入事件之handle的创建

    Netty源码分析第三章: 客户端接入流程 第二节: 处理接入事件之handle的创建 上一小节我们剖析完成了与channel绑定的ChannelConfig初始化相关的流程, 这一小节继续剖析客户端 ...

  7. Netty源码分析第3章(客户端接入流程)---->第3节: NioSocketChannel的创建

    Netty源码分析第三章: 客户端接入流程 第三节: NioSocketChannel的创建 回到上一小节的read()方法: public void read() { //必须是NioEventLo ...

  8. Netty源码分析第3章(客户端接入流程)---->第4节: NioSocketChannel注册到selector

    Netty源码分析第三章: 客户端接入流程 第四节: NioSocketChannel注册到selector 我们回到最初的NioMessageUnsafe的read()方法: public void ...

  9. Netty源码分析第3章(客户端接入流程)---->第5节: 监听读事件

    Netty源码分析第三章: 客户端接入流程 第五节: 监听读事件 我们回到AbstractUnsafe的register0()方法: private void register0(ChannelPro ...

随机推荐

  1. WPF 自定义的图表(适用大量数据绘制)

    原文:WPF 自定义的图表(适用大量数据绘制) 在WPF中绘制图表比较简单,有很多的第三方控件,但是在绘制大量数据的时候,就显得有些吃力,即便是自己用StreamGeometry画也达不到理想的效果, ...

  2. 漫谈 KVC 与 KVO

    KVC 与 KVO 无疑是 Cocoa 提供给我们的一个非常强大的特性,使用熟练可以让我们的代码变得非常简洁并且易读.但 KVC 与 KVO 提供的 API 又是比较复杂的,绝对超出我们不经深究之前所 ...

  3. shell脚本自动化安装LAMP

    #!/bin/bash#auto make install LAMP#by authors yehailun #arp和apr-util依赖APR_FILES=apr-1.6.2.tar.gz APR ...

  4. Qt SQLite 批量插入优化(SQLite默认将每条语句看成单独的事务)good

    使用SQLite存储数据时发现插入速度太慢,程序跑了将近五分钟才插入了不到三千条.上网查资料才发现,SQLite这种文件数据库与MySql机制不一样,每条事务都有打开和关闭文件的步骤,SQLite默认 ...

  5. Effection Go

    Introduction: 新语言, 新思维 Formatting Indentation: 默认tab Line Length: 无限制, 会自动换行 Parentheses: 圆括号, 无限制, ...

  6. Spring Web Flow 的优缺点

    # 前言 Spring Web Flow = SWF 最近学习了<Spring实战>的第八章,Spring Web Flow.感觉是个不错的东西.无奈发现网上的资料少之又少.后来发现根本没 ...

  7. 调用API函数减少c#内存占用(20+m减至1m以下)

    原文:调用API函数减少c#内存占用(20+m减至1m以下) c#虽然内置垃圾回收机制,但是并不能解决程序占用内存庞大的问题,如果我们仔细观察任务管理器,我们会发现一个程序如果最小化的时候,它所占用的 ...

  8. js获取当前时间戳,仿PHP函数模式

    函数: /** * 获取时间戳函数 * 仿PHP函数模式 */ function time(){ var this_time = Date.parse(new Date()); this_time = ...

  9. git 创建一个空分支

    创建一个分支 使用参数 --orphan,这个参数的主要作用有两个,一个是拷贝当前所在分支的所有文件,另一个是没有父结点,可以理解为没有历史记录,是一个完全独立背景干净的分支. 参考git的帮助文档, ...

  10. ELINK编程器典型场景之远程镜像

    当不想直接提供Hex/Bin等二进制程序文件给用户时,通过生成远程镜像功能将程序文件加密后,再提供给用户自行脱机下载来达到远程更新的目的. 远程镜像生成的一般步骤为由客户端提供SN码,本地依据SN码加 ...