昨天的'Netty从0到1的运作流程'文章中我们介绍了Netty如何启动以及如何接收客户端请求, 如果有疑惑可以给我发邮件promiss217@outlook.com或者去我的个人站点https://www.infuq.com/manual/feedback.html提反馈信息.
今天我们主要讲解Netty如何接收客户端请求.
当服务端启动以后, 此时有客户端发送请求过来, 那么服务端接收到此请求, 在服务端处理IO请求的过程.
if ((readyOps & (SelectionKey.OP_READ | SelectionKey.OP_ACCEPT)) != 0 || readyOps == 0) {
unsafe.read();
}
当是ACCEPT请求时, 读取客户端连接.
@Override
protected int doReadMessages(List<Object> buf) throws Exception {
SocketChannel ch = SocketUtils.accept(javaChannel());
if (ch != null) {
buf.add(new NioSocketChannel(this, ch));
return 1;
}
return 0;
}
读取的JDK-Channel并封装成NioSocketChannel. 在前面的文章我们说过, 此处默认读取16个客户端. 读取之后
for (int i = 0; i < size; i ++) {
pipeline.fireChannelRead(readBuf.get(i));
}
通过for循环依次'传给'Pipeline中. 而在NioServerSocketChannel关联的Pipeline中有个ServerBootstrapAcceptor, 它是一个处理器Handler.
public void channelRead(ChannelHandlerContext ctx, Object msg) {
final Channel child = (Channel) msg;
child.pipeline().addLast(childHandler);
setChannelOptions(child, childOptions, logger);
setAttributes(child, childAttrs);
childGroup.register(child).addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (!future.isSuccess()) {
forceClose(child, future.cause());
}
}
});
}
这个处理器会向客户端的Channel设置选项和属性.以及添加childHandler.之后会选择一个NioEventLoop, 并将Channel注册到这个NioEventLoop.
但实际的注册是以任务的形式添加到这个NioEventLoop对应的MpscQueue中.注册作为第一个任务, 也表示会启动这个NioEventLoop, 顺理成章会创建一个线程.
注册的过程会触发channelRegistered事件, channelActive事件,最后会向Selector设置READ事件. 至此客户端便可以读写IO操作了.