前面经过channel初始化、注册,所需要的数据结构(epoll_event)基本上准备好了,serverSocket也处于监听状态,可以接收来自客户端的请求了。NioServerSocketChannel注册在了NioEventLoop#selector,在注册过程中启动了NioEventLoop,run方法会循环执行,每次循环都会执行select和执行所有的task。如果select有事件,则会处理收到的事件。

private void processSelectedKeys() {
if (selectedKeys != null) {
// 是否使用优化过的selectionKey
processSelectedKeysOptimized();
} else {
processSelectedKeysPlain(selector.selectedKeys());
}
}

前面在NioEventLoop初始化的时候说过关于selectionKey优化的问题,这里不再赘述。两种方式主要是遍历selectionKey的方式不同,具体处理事件的调用是一样的。这里以processSelectedKeysOptimized为例。

accept

private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {
// channel是NioServerSocketChannel
// unsafe是NioMessageUnsafe
final AbstractNioChannel.NioUnsafe unsafe = ch.unsafe();
// 省略中间代码...
if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
// 调用NioMessageUnsafe.read
unsafe.read();
}
} catch (CancelledKeyException ignored) {
unsafe.close(unsafe.voidPromise());
} public void read() {
// 省略中间代码...
// 由于是ServerSocket,只负责accept,如果有IO事件说明就是有新的客户端连接,所以这里就是创建NioSocketChannel
int localRead = doReadMessages(readBuf);
if (localRead == 0) {
break;
}
if (localRead < 0) {
closed = true;
break;
} allocHandle.incMessagesRead(localRead);
} while (allocHandle.continueReading());
} catch (Throwable t) {
exception = t;
} int size = readBuf.size();
for (int i = 0; i < size; i ++) {
readPending = false;
// 注册刚刚创建的NioSocketChannel
pipeline.fireChannelRead(readBuf.get(i));
}
readBuf.clear();
allocHandle.readComplete();
pipeline.fireChannelReadComplete();
// 省略中间代码...
}
} protected int doReadMessages(List<Object> buf) throws Exception {
// 调用java.nio.channels.ServerSocketChannel#accept来创建SocketChannel
SocketChannel ch = SocketUtils.accept(javaChannel()); try {
if (ch != null) {
// 创建NioSocketChannel
buf.add(new NioSocketChannel(this, ch));
return 1;
}
} catch (Throwable t) {
// 省略中间代码...
} return 0;
}

上面创建了NioSocketChannel之后,接下来注册所有客户端连接的NioSocketChannel,调用的是DefaultChannelPipeline#fireChannelRead方法,接下来是执行pipeline中的handler,在初始化的时候添加了LoggingHandler (如果启动的时候配置了的话),那么目前pipeline中的handler有

  • io.netty.channel.DefaultChannelPipeline$HeadContext:pipeline创建的时候默认的第一个handler
  • io.netty.handler.logging.LoggingHandler:启动的时候用户配置的handler
  • io.netty.bootstrap.ServerBootstrap$ServerBootstrapAcceptor
  • io.netty.channel.DefaultChannelPipeline$TailContext:pipeline创建的时候默认的最后一个handler

下面看下ServerBootstrap$ServerBootstrapAcceptor是什么时候添加到handler的

// io.netty.bootstrap.ServerBootstrap#init
// 这个方法是NioServerSocketChannel初始化的时候调用的
void init(Channel channel) throws Exception {
// 省略中间代码...
p.addLast(new ChannelInitializer<Channel>() {
@Override
public void initChannel(final Channel ch) throws Exception {
final ChannelPipeline pipeline = ch.pipeline();
ChannelHandler handler = config.handler();
if (handler != null) {
pipeline.addLast(handler);
} ch.eventLoop().execute(new Runnable() {
@Override
public void run() {
// 在pipeline中添加ServerBootstrapAcceptor
pipeline.addLast(new ServerBootstrapAcceptor(
ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
}
});
}
});
}

之所以说ServerBootstrapAcceptor,是因为NioSocketChannel的register过程是这个handler的channelRead方法开始的

public void channelRead(ChannelHandlerContext ctx, Object msg) {
final Channel child = (Channel) msg; child.pipeline().addLast(childHandler); setChannelOptions(child, childOptions, logger); // 配置NioSocketChannel
for (Entry<AttributeKey<?>, Object> e: childAttrs) {
child.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());
} try {
// 这里childGroup就是一开始我们配置的workerGroup
// 所以调用的是io.netty.channel.MultithreadEventLoopGroup#register(io.netty.channel.Channel)
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的注册过程是类似的,创建socket,创建SelectionKeyImpl等。只不过NioSocketChannel不监听accept事件。

read

上面在接收到来自客户端的连接请求后,将NioSocketChannel注册到selector上,这个selector也是在NioEventLoop里面的,后面和这个客户端的通信都会通过这个channel进行,如果客户端发送来数据,也是selector收到读事件通知,然后调用processSelectedKey来处理read事件。

private void processSelectedKey(SelectionKey k, AbstractNioChannel ch) {
// channel是NioSocketChannel
// unsafe是NioSocketChannelUnsafe
final AbstractNioChannel.NioUnsafe unsafe = ch.unsafe();
// 省略中间代码...
if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
// 调用NioByteUnsafe.read
unsafe.read();
}
} catch (CancelledKeyException ignored) {
unsafe.close(unsafe.voidPromise());
} public final void read() {
final ChannelConfig config = config();
if (shouldBreakReadReady(config)) {
clearReadPending();
return;
}
final ChannelPipeline pipeline = pipeline();
// PooledByteBufAllocator,默认的内存申请管理器
final ByteBufAllocator allocator = config.getAllocator();
// AdaptiveRecvByteBufAllocator$HandleImpl
final RecvByteBufAllocator.Handle allocHandle = recvBufAllocHandle();
allocHandle.reset(config); ByteBuf byteBuf = null;
boolean close = false;
try {
do {
// 申请内存
byteBuf = allocHandle.allocate(allocator);
// 读取数据
allocHandle.lastBytesRead(doReadBytes(byteBuf));
if (allocHandle.lastBytesRead() <= 0) {
// nothing was read. release the buffer.
byteBuf.release();
byteBuf = null;
close = allocHandle.lastBytesRead() < 0;
if (close) {
// There is nothing left to read as we received an EOF.
readPending = false;
}
break;
} allocHandle.incMessagesRead(1);
readPending = false;
// 执行pipeline中的handler
pipeline.fireChannelRead(byteBuf);
byteBuf = null;
} while (allocHandle.continueReading());
// 省略中间代码
}
}

一般来说NioSocketChannel中的handler包括

  • io.netty.channel.DefaultChannelPipeline$HeadContext
  • org.lep.test.netty.protocol.custom.codec.NettyMessageDecoder:自定义的解码器
  • org.lep.test.netty.protocol.custom.codec.NettyMessageEncoder:自定义的编码器
  • org.lep.test.netty.protocol.custom.server.LoginAuthRespHandler:自定义的handler
  • org.lep.test.netty.protocol.custom.server.HeartBeatRespHandler:自定义的handler
  • io.netty.channel.DefaultChannelPipeline$TailContext

netty提供了一些基本的编解码功能,自己可以根据实际需要扩展使用,然后自定义自己的逻辑处理handler。

上面还涉及到内存的分配部分留在下一节介绍。

总结

read事件处理过程:

  1. 接收到read事件
  2. 分配内存,初始化buffer
  3. 调用channel.read将数据读取到buffer中
  4. 执行pipeline中的handler,包括了编解码的handler,自定义的handler来处理数据

Netty源码—四、事件处理的更多相关文章

  1. Netty 源码 ChannelHandler(四)编解码技术

    Netty 源码 ChannelHandler(四)编解码技术 Netty 系列目录(https://www.cnblogs.com/binarylei/p/10117436.html) 一.拆包与粘 ...

  2. Netty 源码解析(四): Netty 的 ChannelPipeline

    今天是猿灯塔“365篇原创计划”第四篇. 接下来的时间灯塔君持续更新Netty系列一共九篇 Netty 源码解析(一): 开始 Netty 源码解析(二): Netty 的 Channel Netty ...

  3. Netty源码分析第4章(pipeline)---->第1节: pipeline的创建

    Netty源码分析第四章: pipeline 概述: pipeline, 顾名思义, 就是管道的意思, 在netty中, 事件在pipeline中传输, 用户可以中断事件, 添加自己的事件处理逻辑, ...

  4. EventLoop(netty源码死磕4)

    精进篇:netty源码  死磕4-EventLoop的鬼斧神工 目录 1. EventLoop的鬼斧神工 2. 初识 EventLoop 3. Reactor模式回顾 3.1. Reactor模式的组 ...

  5. Pipeline inbound(netty源码7)

    netty源码死磕7  Pipeline 入站流程详解 1. Pipeline的入站流程 在讲解入站处理流程前,先脑补和铺垫一下两个知识点: (1)如何向Pipeline添加一个Handler节点 ( ...

  6. Netty源码分析之NioEventLoop(三)—NioEventLoop的执行

    前面两篇文章Netty源码分析之NioEventLoop(一)—NioEventLoop的创建与Netty源码分析之NioEventLoop(二)—NioEventLoop的启动中我们对NioEven ...

  7. 【Netty源码解析】NioEventLoop

    上一篇博客[Netty源码学习]EventLoopGroup中我们介绍了EventLoopGroup,实际说来EventLoopGroup是EventLoop的一个集合,EventLoop是一个单线程 ...

  8. netty源码解析目录

    第一章 java nio三大组件与使用姿势 二.netty使用姿势 三.netty服务端启动源码 四.netty客户端启动源码 五.NioEventLoop与netty线程模型 六.ChannelPi ...

  9. netty源码解解析(4.0)-10 ChannelPipleline的默认实现--事件传递及处理

    事件触发.传递.处理是DefaultChannelPipleline实现的另一个核心能力.在前面在章节中粗略地讲过了事件的处理流程,本章将会详细地分析其中的所有关键细节.这些关键点包括: 事件触发接口 ...

随机推荐

  1. QT中对内存的管理

    在QT中,一切继承自QT自有类的类,如果存在parent指针,那么当parent指针delete时,该类中的指针(它们都属于parent指针对应的child指针)也会被delete.综上,如果我们的窗 ...

  2. Django REST framework+Vue 打造生鲜超市(十三)

    目录 生鲜超市(一)    生鲜超市(二)    生鲜超市(三) 生鲜超市(四)    生鲜超市(五)    生鲜超市(六) 生鲜超市(七)    生鲜超市(八)    生鲜超市(九) 生鲜超市(十) ...

  3. 网络-tcp

    1.TCP:面向连接可靠的传输协议,全拼:Transmission Control Protocol   2.UDP:用户数据报协议 全拼:User Datagram protocol 不是面向连接的 ...

  4. HTTP协议、Ajax请求

    今天这篇文章呢,主要讲的就是关于HTTP协议.Ajax请求以及一些相关的小知识点.虽然内容不算多,可是是很重点的东西~ HTTP协议 1. http:超文本传输协议.简单.快速.灵活.无状态.无连接. ...

  5. Python_回调函数

    import os import stat def remove_readonly(func,path): #定义回调函数 os.chmod(path,stat.S_IWRITE) #删除文件的只读文 ...

  6. java ArrayList集合

    ArrayList集合是程序中最常见的一种集合,它属于引用数据类型(类).在ArrayList内部封装了一个长度可变的数组,当存入的元素超过数组长度时,ArrayList会在内存中分配一个更大的数组来 ...

  7. VS2010+OpenCV3.4.1+zbar 64位

    1. OpenCV3.4.1和zbar文件夹放到指定的路径下,我把它们放在了"D:\二维码\环境"中. zbar:链接:https://pan.baidu.com/s/11eCDV ...

  8. Java 面试知识点解析(三)——JVM篇

    前言: 在遨游了一番 Java Web 的世界之后,发现了自己的一些缺失,所以就着一篇深度好文:知名互联网公司校招 Java 开发岗面试知识点解析 ,来好好的对 Java 知识点进行复习和学习一番,大 ...

  9. 如何识别企业内的“千里马”?

    很多企业主都在感叹无法找到合适的优秀管理人才,却忽视了一条获得管理人才的捷径:内部培养.在员工中挑选具有领导潜质的人才,加以培养,必成企业栋梁,而且这样选拔的人才与企业的契合度.忠诚度方面都相对较高. ...

  10. java并发之ReentrantLock学习理解

    简介 java多线程中可以使用synchronized关键字来实现线程间同步互斥,但在jdk1.5中新增加了ReentrantLock类也能实现同样的效果,并且在扩展功能上也更加强大,比如具有嗅探锁定 ...