热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

开发笔记:构建连接:NioSocketChannel是什么时候激活的

篇首语:本文由编程笔记#小编为大家整理,主要介绍了构建连接:NioSocketChannel是什么时候激活的相关的知识,希望对你有一定的参考价值。

篇首语:本文由编程笔记#小编为大家整理,主要介绍了构建连接:NioSocketChannel 是什么时候激活的相关的知识,希望对你有一定的参考价值。



构建连接:NiosocketChannel 是什么时候激活的
目录
  • 构建连接:NioSocketChannel 是什么时候激活的
    • 1. 主线分析
      • 1.1 主线
      • 1.2 知识点
    • 2. 源码分析
      • 2.1 接收连接
      • 3.2 初始化连接

Netty 系列目录(https://www.cnblogs.com/binarylei/p/10117436.html)

在上一节中,我们分析服务器的启动过程,接下来就是开门迎客。


1. 主线分析


1.1 主线

NioEventLoop 不断的轮询,接收 OP_ACCEPT 事件;ServerBootstrapAcceptor 接收到新的连接后初始化并注册到 childGroup 上。以上工作分别是在 boss thread 和 worker thread 两个线程上执行。



  1. boss thread 线程



    • NioEventLoop 中的 selector 轮询创建连接事件(OP_ACCEPT):



    • 创建 SocketChannel



    • 初始化 SocketChannel 并从 worker group 中选择一个 NioEventLoop





  2. worker thread 线程



    • 将 SocketChannel 注册到选择的 NioEventLoop 的 selector

    • 注册读事件(OP_READ)到 selector 上



NioEventLoop#run
-> processSelectedKeys
-> AbstractNioMessageChannel.NioMessageUnsafe#read
-> NioServerSocketChannel#doReadMessages
-> pipeline#fireChannelRead
ServerBootstrapAcceptor#channelRead
-> EventLoopGroup#register

1.2 知识点

(1)接受连接的本质



  • selector.select()/selectNow()/select(timeoutMillis) 发现OP_ACCEPT 事件,处理:



  • SocketChannel socketChannel = serverSocketChannel.accept()



  • selectiOnKey= javaChannel().register(eventLoop().unwrappedSelector(), 0, this);



  • selectionKey.interestOps(OP_READ);



(2)ServerBootstrapAcceptor

创建连接的初始化和注册是通过 pipeline.fireChannelRead 在 ServerBootstrapAcceptor 中完成的。


2. 源码分析

NioServerSocketChannel 注册到 eventLoop 后就会启动 NioEventLoop 线程,专门处理对应 channel 的网络 IO 事件。通过 OP_ACCEPT 事件接收客户端连接 NioSocketChannel,并进行初始化。


图1:接收客户端过程


2.1 接收连接

(1)OP_ACCEPT 事件处理

processSelectedKey 负责处理 channel 的 OP_CONNECT、OP_WRITE、OP_READ、OP_ACCEPT 事件。这里我们只关注 OP_ACCEPT 事件是如何处理的。

// 分别处理 OP_CONNECT、OP_WRITE、OP_READ、OP_ACCEPT 事件
private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {
// 省略...
final AbstractNioChannel.NioUnsafe unsafe = ch.unsafe();
int readyOps = k.readyOps();
// OP_CONNECT
if ((readyOps & SelectionKey.OP_CONNECT) != 0) {
int ops = k.interestOps();
ops &= ~SelectionKey.OP_CONNECT;
k.interestOps(ops);
unsafe.finishConnect();
}
// OP_WRITE
if ((readyOps & SelectionKey.OP_WRITE) != 0) {
ch.unsafe().forceFlush();
}
// OP_READ、OP_ACCEPT
if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
unsafe.read();
}
}

说明: 可以看到 OP_READ 和 OP_ACCEPT 都是调用 unsafe.read() 处理的。不同的 Channel 对应不同的 unsafe,比如 NioServerSocketChannel 对应 NioMessageUnsafe,而 NioSocketChannel 对应 NioByteUnsafe。当然,Netty 比较巧妙的将 OP_READ 和 OP_ACCEPT 事件统一处理,这也会给我们读源码造成一些混乱。

(2)接收连接

下面,我们看一下 NioMessageUnsafe 是如何接收客户端连接的。猜也能猜到,肯定需要调用 serverSocketChannel.accept() 获取客户端连接。

// NioMessageUnsafe
private final List readBuf = new ArrayList();
@Override
public void read() {
// 1. 接收客户端连接请求
do {
int localRead = doReadMessages(readBuf);
if (localRead == 0) {
break;
}
if (localRead <0) {
closed = true;
break;
}
allocHandle.incMessagesRead(localRead);
} while (allocHandle.continueReading());
// 2. 接收客户端连接请求
int size = readBuf.size();
for (int i = 0; i pipeline.fireChannelRead(readBuf.get(i));
}
...
}

说明: NioMessageUnsafe 的 read 方法完成了二件事:



  1. 通过 doReadMessages 接收客户端的 NioSocketChannel。当然这里的 doReadMessages 每次最多只能读一个 NioSocketChannel 对象。

  2. 触发 pipeline 的 fireChannelRead 事件完成 channel 的初始化工作 ,如有异常则触发 fireExceptionCaught。那肯定有一个 Handler 对应来处理这个 NioSocketChannel。

// NioServerSocketChannel:调用 NIO 底层接收客户连接
@Override
protected int doReadMessages(List buf) throws Exception {
SocketChannel ch = SocketUtils.accept(javaChannel());
if (ch != null) {
buf.add(new NioSocketChannel(this, ch));
return 1;
}
return 0;
}

说明: 真正接收客户端请求的操作则委托给了子类 NioServerSocketChannel#doReadMessages 方法完成。至此,NioServerSocketChannel 已经将请求的 NioSocketChannel 接收过来,但还未完成 channel 的初始化工作,如 handler 绑定,参数配置等。


3.2 初始化连接

上文提到 NioServerSocketChannel 在初始化的时候会绑定 ServerBootstrapAcceptor,这个 handler 完成了 channel 的初始化工作。NioServerSocketChannel 的 Pipeline 如下图:


我们直接看一下 ServerBootstrapAcceptor#channelRead 方法。主要完成 NioSocketChannel 的 TCP 参数、附加属性、Handler 配置等,基本上和 NioServerSocketChannel 一模一样。

public void channelRead(ChannelHandlerContext ctx, Object msg) {
final Channel child = (Channel) msg;
// 1. NioSocketChannel 绑定 handler 和相关配置参数
child.pipeline().addLast(childHandler);
// 2. 配置 Socket 的 TCP 参数和附加属性
setChannelOptions(child, childOptions, logger);
for (Entry, Object> e: childAttrs) {
child.attr((AttributeKey) e.getKey()).set(e.getValue());
}

// 3. NioSocketChannel 注册到 eventLoop 上
try {
childGroup.register(child).addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (!future.isSuccess()) {
forceClose(child, future.cause());
}
}
});
} catch (Throwable t) {
forceClose(child, t);
}
}

说明: 其实这段代码和 NioServerSocketChannel 初始化的代码大同小异,唯一需要注意的是 NioSocketChannel 的注册逻辑稍有区别。



  1. NioSocketChannel 注册的是 childGroup 线程。



  2. NioSocketChannel 注册到 Selector 成功后,会触发 pipeline.fireChannelActive() 事件,调用其 beginRead 方法注册 OP_READ 事件。而 NioServerSocketChannel 需要 bind 成功才会注册 OP_ACCEPT 事件。

    childGroup.register(child)

    注意:这里是 childGroup。Channel 是如何注册到 NioEventLoopGroup 是的详见:https://www.cnblogs.com/binarylei/p/10135712.html





每天用心记录一点点。内容也许不重要,但习惯很重要!



推荐阅读
  • 结对编程 地铁最短路径 张波朱新远
    结对编程地铁最短路径一、任务:实现一个帮助进行地铁出行路线规划的命令行程序。PSP2.1PersonalSoftwareProcessStagesTimePlanni ... [详细]
  • PIMPL 是 C++ 中的一个编程技巧,意思为指向实现的指针。具体操作是把类的实现细节放到一个单独的类中,并用一个指针进行访问 ... [详细]
  • 以下内容|尾部_quarkus实战之一:准备工作
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了quarkus实战之一:准备工作相关的知识,希望对你有一定的参考价值。欢迎访问我的GitHub ... [详细]
  • spotify engineering culture part 1
    原文,因为原视频说的太快太长,又没有字幕,于是借助youtube,把原文听&打出来了。中文版日后有时间再翻译。oneofthebigsucceessfactorshereatSpo ... [详细]
  • 在实际的工作流业务开发中,当用户完成当前用户任务时,需要指定下一个用户任务的审核人。此时我们需要获取下一个节点的一些信息,来确定下一个用户任务的审核人有哪些。在实际工 ... [详细]
  • socket8 [命名管道]
    ::命名管道不但能实现同一台机器上两个进程通信,还能在网络中不同机器上的两个进程之间的通信机制。与邮槽不同,命名管道是采用基于连接并且可靠的传输方式,所以命名管道传输数据只能一对一 ... [详细]
  • 这两天做了一个小项目,里面有个下载进度的进度条需要制作。先看呈现的效果:点击进度,然后依次递增,直到递增到百分之百。现在把这部分代码分享下来。<!DOCTYPEhtml><html ... [详细]
  • handler机制_Handler机制与原理
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了Handler机制与原理相关的知识,希望对你有一定的参考价值。 ... [详细]
  • 篇首语:本文由编程笔记#小编为大家整理,主要介绍了在单独的JVM上执行新的JavaFX应用程序相关的知识,希望对你有一定的参考价值。 ... [详细]
  • Java实现Kafka的生产者、消费者,Go语言社区,Golang程序员人脉社 ... [详细]
  • SSL协议、TLS协议,使用哪一种更安全?
    在金融银行业,保护机密信息的安全至关重要。由于财务记录完全通过在线数据库维护,因此实施保护客户、银行和金融机构免受黑客攻击的安全功能比以往任何时候都更加重要。安全套接字层(SSL) ... [详细]
  • 在实际开发中,现在安卓端和后台之间的数据交互,一般都是用JSON来传递数据信息。JSON大家一般都比较熟悉。我这边就以实际项目中的后台传过来的情况和大家分析下及如何处理。比如后台返 ... [详细]
  • 九宫格计算. ... [详细]
  • HTTP请求响应的步骤第一步:第二步:第三步:第四步:第五步第一步:1.客户端连接到Web服务器⼀个HTTP ... [详细]
  • ubuntu更新python3版本并安装scapy
    下载编译安装下载wgethttps:www.python.orgftppython3.7.3Python-3.7.3.tgz其他版本下载:https:www.pyt ... [详细]
author-avatar
张茂彪6
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有