我在前面说过了server的启动,差不多可以看到netty nio主要的东西包括了:nioEventLoop,nioMessageUnsafe,channelPipeline,channelHandler等。比较绕的就是handler的顺序:head---->tail,这个也是netty链式管理复杂的地方。这里再说下accept如何接受客户端的connect请求(connect请求跟server启动分析差不多,我就不说了)。
入口就在nioEventLoop中:
private static void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {
NioUnsafe unsafe = ch.unsafe();
if(!k.isValid()) {
unsafe.close(unsafe.voidPromise());
} else {
try {
int ignored = k.readyOps();
if((ignored & 17) != 0 || ignored == 0) {
unsafe.read();
if(!ch.isOpen()) {
return;
}
} if((ignored & 4) != 0) {
ch.unsafe().forceFlush();
} if((ignored & 8) != 0) {
int ops = k.interestOps();
ops &= -9;
k.interestOps(ops);
unsafe.finishConnect();
}
} catch (CancelledKeyException var5) {
unsafe.close(unsafe.voidPromise());
} }
}
当OP_ACCEPT事件到达时,就会执行nioMessageUnsafe.read()方法。
一直走下去,最终会执行NioServerSocketChannel中的doReadMessages()方法。
protected int doReadMessages(List<Object> buf) throws Exception {
SocketChannel ch = this.javaChannel().accept(); try {
if(ch != null) {
// 这里就创建了nioScoketChannel,创建过程类似NioServerSocketChannel,并最终添加到buf list中。
buf.add(new NioSocketChannel(this, ch));
return 1;
}
} catch (Throwable var6) {
logger.warn("Failed to create a new channel from an accepted socket.", var6); try {
ch.close();
} catch (Throwable var5) {
logger.warn("Failed to close a socket.", var5);
}
} return 0;
} 我截取一段代码看看添加socketChannel到list集合中发生了什么?
try {
int size;
try {
do {
size = AbstractNioMessageChannel.this.doReadMessages(this.readBuf);
if(size == 0) {
break;
} if(size < 0) {
closed = true;
break;
}
} while(config.isAutoRead() && this.readBuf.size() < maxMessagesPerRead);
} catch (Throwable var11) {
exception = var11;
} AbstractNioMessageChannel.this.setReadPending(false);
size = this.readBuf.size();
int i = 0; while(true) {
if(i >= size) {
this.readBuf.clear();
pipeline.fireChannelReadComplete();
if(exception != null) {
if(exception instanceof IOException && !(exception instanceof PortUnreachableException)) {
closed = !(AbstractNioMessageChannel.this instanceof ServerChannel);
} pipeline.fireExceptionCaught(exception);
} if(closed && AbstractNioMessageChannel.this.isOpen()) {
this.close(this.voidPromise());
}
break;
} // 看到这个方法了吧,这是一个inbound事件,当前pipeline的执行顺序是:head---->serverBootstrapAcceptor---->tail
// 先看看serverBootstrapAcceptor跳转到1
pipeline.fireChannelRead(this.readBuf.get(i));
++i;
}
} finally {
if(!config.isAutoRead() && !AbstractNioMessageChannel.this.isReadPending()) {
this.removeReadOp();
} } 1.这个channelRead是serverBootstrapAcceptor执行的channelRead()
public void channelRead(ChannelHandlerContext ctx, Object msg) {
final Channel child = (Channel)msg;
// 这个地方添加了childHandler,还记得server启动时指定的childHannel吧?
child.pipeline().addLast(new ChannelHandler[]{this.childHandler});
Entry[] t = this.childOptions;
int len$ = t.length; int i$;
Entry e;
for(i$ = 0; i$ < len$; ++i$) {
e = t[i$]; try {
if(!child.config().setOption((ChannelOption)e.getKey(), e.getValue())) {
ServerBootstrap.logger.warn("Unknown channel option: " + e);
}
} catch (Throwable var10) {
ServerBootstrap.logger.warn("Failed to set a channel option: " + child, var10);
}
} t = this.childAttrs;
len$ = t.length; for(i$ = 0; i$ < len$; ++i$) {
e = t[i$];
child.attr((AttributeKey)e.getKey()).set(e.getValue());
} try {
// 进行注册,注意下这里注册的是childGroup,终于worker线程池开始启动了,且正在执行register任务,跳转到2
this.childGroup.register(child).addListener(new ChannelFutureListener() {
public void operationComplete(ChannelFuture future) throws Exception {
if(!future.isSuccess()) {
ServerBootstrap.ServerBootstrapAcceptor.forceClose(child, future.cause());
} }
});
} catch (Throwable var9) {
forceClose(child, var9);
} } 2.AbstractChannel中的register0()方法
这个方法跟之前分析server的register方法一致,我说下大概思路:
2.1 将socketChannel注册到selector中;
2.2 通过触发pipeline.fireChannelRegistered();方法将之前main函数中设置的处理器(还记得上一篇netty源码分析之一中唯一的图片吧,就是那个childHandler,本例中就是MessageRecvChannelInitializer这个handler添加到管道中,并将channelInitializer移除)
2.3 通过调用channelActive,ops设置为READ事件。
2.4 worker线程开始监听我们的read事件了。
2.5 boss线程继续执行OP_ACCEPT事件
private void register0(ChannelPromise promise) {
try {
if(!promise.setUncancellable() || !this.ensureOpen(promise)) {
return;
} boolean t = this.neverRegistered;
AbstractChannel.this.doRegister();
this.neverRegistered = false;
AbstractChannel.this.registered = true;
this.safeSetSuccess(promise);
AbstractChannel.this.pipeline.fireChannelRegistered();
if(t && AbstractChannel.this.isActive()) {
AbstractChannel.this.pipeline.fireChannelActive();
}
} catch (Throwable var3) {
this.closeForcibly();
AbstractChannel.this.closeFuture.setClosed();
this.safeSetFailure(promise, var3);
} } 一个完整的ACCEPT事件执行完毕!
netty源码分析之二:accept请求的更多相关文章
-
Netty源码分析-- 处理客户端接入请求(八)
这一节我们来一起看下,一个客户端接入进来是什么情况.首先我们根据之前的分析,先启动服务端,然后打一个断点. 这个断点打在哪里呢?就是NioEventLoop上的select方法上. 然后我们启动一个客 ...
-
Netty源码分析 (二)----- ServerBootstrap
BootStrap在netty的应用程序中负责引导服务器和客户端.netty包含了两种不同类型的引导: 1. 使用服务器的ServerBootStrap,用于接受客户端的连接以及为已接受的连接创建子通 ...
-
Netty源码分析(二):服务端启动
上一篇粗略的介绍了一下netty,本篇将详细介绍Netty的服务器的启动过程. ServerBootstrap 看过上篇事例的人,可以知道ServerBootstrap是Netty服务端启动中扮演着一 ...
-
Netty源码分析 (三)----- 服务端启动源码分析
本文接着前两篇文章来讲,主要讲服务端类剩下的部分,我们还是来先看看服务端的代码 /** * Created by chenhao on 2019/9/4. */ public final class ...
-
Tomcat源码分析(二)------ 一次完整请求的里里外外
Tomcat源码分析(二)------ 一次完整请求的里里外外 前几天分析了一下Tomcat的架构和启动过程,今天开始研究它的运转机制.Tomcat最本质就是个能运行JSP/Servlet的Web ...
-
Netty 源码分析系列(二)Netty 架构设计
前言 上一篇文章,我们对 Netty做了一个基本的概述,知道什么是Netty以及Netty的简单应用. Netty 源码分析系列(一)Netty 概述 本篇文章我们就来说说Netty的架构设计,解密高 ...
-
netty源码分析之揭开reactor线程的面纱(二)
如果你对netty的reactor线程不了解,建议先看下上一篇文章netty源码分析之揭开reactor线程的面纱(一),这里再把reactor中的三个步骤的图贴一下 reactor线程 我们已经了解 ...
-
Netty源码分析--内存模型(上)(十一)
前两节我们分别看了FastThreadLocal和ThreadLocal的源码分析,并且在第八节的时候讲到了处理一个客户端的接入请求,一个客户端是接入进来的,是怎么注册到多路复用器上的.那么这一节我们 ...
-
一个普通的 Zepto 源码分析(二) - ajax 模块
一个普通的 Zepto 源码分析(二) - ajax 模块 普通的路人,普通地瞧.分析时使用的是目前最新 1.2.0 版本. Zepto 可以由许多模块组成,默认包含的模块有 zepto 核心模块,以 ...
随机推荐
-
Flink &ndash; window operator
参考, http://wuchong.me/blog/2016/05/25/flink-internals-window-mechanism/ http://wuchong.me/blog/201 ...
-
forward内部跳转 和redirect重定向跳转的区别
1.从地址栏显示来说 forward是服务器请求资源,服务器直接访问目标地址的URL,把那个URL的响应内容读取过来,然后把这些内容再发给浏览器.浏览器根本不知道服务器发送的内容从哪里来的,所以它的地 ...
-
OOA、OOD、OOP
复习 OOA.OOD.OOP OOA Object-Oriented Analysis:面向对象分析方法 是在一个系统的开发过程中进行了系统业务调查以后,按照面向对象的思想来分析问题.OOA与结构 ...
-
使用jeesite org.springframework.beans.NotReadablePropertyException: Invalid property &#39;tfxqCmsAccount.id&#39; of bean class
一对多 对子表添加时在form表单 path="tfxqCmsAccount.id"页面报错,对比了下其他可行的,发现其自动生成的子类少了个构造方法 加上 public TfxqC ...
-
js 特效 手风琴效果
$(document).ready(function(){ //定义展开的块 var lastBlock = $('#a1'); //展开的块的宽度 var maxWidth = 406; //折叠的 ...
-
图论中DFS与BFS的区别、用法、详解?
DFS与BFS的区别.用法.详解? 写在最前的三点: 1.所谓图的遍历就是按照某种次序访问图的每一顶点一次仅且一次. 2.实现bfs和dfs都需要解决的一个问题就是如何存储图.一般有两种方法:邻接矩阵 ...
-
Eclipse中Hibernate插件的安装
在使用Hibernate开发时,大多数情况下涉及到其XML配置文件的编辑,尤其是.cfg.xml(配置文件)和hbm.xml(关系映射文件)这两种.为了更方便的使用此框架,其插件的安装是很有必要的. ...
-
sqlite3使用总结(转并且修改)
此文来自http://blog.csdn.net/shengfang666/article/details/7937200,我在这儿重新发一下,备份参考. 前序:一. 版本二. ...
-
Linux登录那点事
跨平台系列汇总:http://www.cnblogs.com/dunitian/p/4822808.html#linux 我们登录linux的时候基本上不太关注上面的这个提示,其实这个还是有点文章的 ...
-
《DSP using MATLAB》Problem 7.10
代码: %% ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ %% Output In ...