Netty之第一次 TCP 连接时发生了什么

1,617 阅读3分钟

「我正在参与掘金会员专属活动-源码共读第一期,点击参与

前言

在上一篇文章中我们详细的走了一遍bind()方法的启动流程, channel的初始化和注册, 并在初始化和注册的时候如果是第一次连接的话会执行pipeline.fireChannelActive();方法进行回调, 这次我们就对回调进行一次详细的讲解

Netty源码分析往期文章:

从回调开始

前情回顾

重新带大家回顾一下上一篇文章末尾的知识, 我们进入了AbstractChannel类的register0方法, 这个方法是我们的channel调用注册和回调通知的地方

image.png

register0方法中重点关注上图中三个红框的地方:

  • doRegister(): 实际注册的方法
  • pipeline.fireChannelRegistered(); 回调方法
  • pipeline.fireChannelActive(); tcp 首次连接时调用, 也是本章的重点

tcp客户端首次连接服务端

image.png

我以debug模式启动Netty项目, 并在该方法上打了断点, 等待一会跳过断点, 可以看到控制台选中部分的时间和上一条打印日志时间间隔近30秒, 在放开debug之后执行了该方法, 调用回调打印

十二月 07, 2022 10:38:37 上午 io.netty.handler.logging.LoggingHandler channelActive

剖析pipeline.fireChannelActive方法

一路Ctrl+左键查看方法详情, 下面注意一下图上的标记类名.方法名

DefaultChannelPipeline.fireChannelActive

image.png

AbstractChannelHandlerContext.invokeChannelActive

image.png

在该方法中, 我们可以看到不论最后执行结果是什么都会执行一个方法next.invokeChannelActive();, 那我们就进入这个方法看一下

AbstractChannelHandlerContext.invokeChannelActive

image.png

最后可以看到不论怎么执行他都会执行到channelActive()方法

  • 这个方法继承于ChannelInboundHandlerAdapter
  • ChannelInboundHandlerAdapter类中的channelActive()方法又继承于ChannelInboundHandler接口
  • 同时, 我们最开始DefaultChannelPipeline类下面还写了一个内部类HeadContext
  • HeadContext类中继承了ChannelInboundHandler也实现了该方法, 咱就说隐藏的真深, 我找半天...如下图

HeadContext类和channelActive()方法详情 image.png

image.png 这个类的继承图如下所示 image.png

所以回调到这里就差不多了, 别问我最后怎么理清的, debug大法好, 可能稍微有点乱, 大家最好自己debug走一遍, 再挨个类点开看一下

回调详情

DefaultChannelPipeline image.png

image.png

**channel.read();**方法是Channel类的, 因为调用的channelNioServerSocketChannel, 所以我们直接去他的父类AbstractChannel中查看

NioServerSocketChannel 类的继承图 image.png

image.png

image.png

AbstractChannelread()方法进入到DefaultChannelPipelineread()方法

image.png

这样我们就找到了真正的read()方法, 到这里我们继续分析

findContextOutbound()

顾名思义, 这个方法是查找出栈的

image.png

这个方法详情流程如下:

  • 先是获取当前ChannelHandler
  • 获取当前ChannelHandlerexecutor
  • 获取前一个ChannelHandler
  • 判断执行while内的skipContext方法
  • skipContext方法的作用: 判断前一个ChannelHandler是否具有响应Write事件的资格

executor.inEventLoop()方法

判断当前线程是不是在当前的EventLoop中对应的那个线程

image.png

next.invokeRead()

直接进入这个方法查看一下, 神秘的面纱即将揭晓

image.png

有没有很熟悉的感觉, 在剖析pipeline.fireChannelActive方法的时候我们就碰到过类似的方法, 那个是invokeChannelActive()方法, 也是一个回调

image.png

因为handler == headContext, 所以最后就是调用的 handler.read()方法

回调执行结果 image.png

发现 doBeginRead() 方法

image.png

image.png

image.png

这里注意一下, 看到this.selectionKey了吗, 如果看我上一篇文章的小伙伴肯定会发现, 这不就是我们之前设置过得值吗

image.png

image.png

这一步就是重新为我们的selectionKey进行设置

结尾

本来想完成一下任务三的, 但是实在是搞不动了, 下一篇再说吧...




本文内容到此结束了

如有收获欢迎点赞👍收藏💖关注✔️,您的鼓励是我最大的动力。

如有错误❌疑问💬欢迎各位大佬指出。

我是 宁轩 , 我们下次再见