回顾
在上一节中,我简单的记录了Netty获取一个新连接的过程,而在这个过程中,其实还有非常重要的一步没有展开,那就是创建客户端channel,即创建NioSocketChannel。
如果NioSocketChannel非常陌生,可以去看看我前面写的小总结,可以有一个大概的了解。
Netty Version:4.1.6
大致流程
-
new NioSocketChannel(parent, socket) - 入口
-
AbstractNioByteChannel(parent, ch) - super
- AbstractNioByteChannel(parent, sh , readInterestOp)
- AbstractChannel构造 - 创建id,pipeline,unsage等
- ch.configureBlocking(false) - 配置nio
- AbstractNioByteChannel(parent, sh , readInterestOp)
-
new NioSocketChannelConfig(channel, javaSocket) - 配置类
- setTcpNoDelay(true) - 关闭Nagle算法
-
跟进源码
首先,先找到这次跟进源码的入口,即:io.netty.channel.socket.nio.NioServerSocketChannel#doReadMessages
,在上一节文末处:
@Override
protected int doReadMessages(List<Object> buf) throws Exception {
SocketChannel ch = javaChannel().accept();
try {
if (ch != null) {
buf.add(new NioSocketChannel(this, ch));
return 1;
}
} catch (Throwable t) {
logger.warn("Failed to create a new channel from an accepted socket.", t);
try {
ch.close();
} catch (Throwable t2) {
logger.warn("Failed to close a socket.", t2);
}
}
return 0;
}
跟进上面的的new NioSocketChannel(this, ch),此处【坐标1】:
io.netty.channel.socket.nio.NioSocketChannel#NioSocketChannel(io.netty.channel.Channel, java.nio.channels.SocketChannel)
public NioSocketChannel(Channel parent, SocketChannel socket) {
super(parent, socket);
config = new NioSocketChannelConfig(this, socket.socket());
}
- 这段代码还真是相当眼熟了,在创建服务端channel那一节也遇到过,只是这里换成客户端channel,逻辑大致相似。
这里先进入super构造方法:
io.netty.channel.nio.AbstractNioByteChannel#AbstractNioByteChannel
protected AbstractNioByteChannel(Channel parent, SelectableChannel ch) {
super(parent, ch, SelectionKey.OP_READ);
}
- 这里和创建服务端channel不同的是,readInterestOp参数变为了OP_READ(值为1),也就是说客户端channel后面会关注一个读事件。
继续进入super:
io.netty.channel.nio.AbstractNioChannel#AbstractNioChannel
protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
super(parent);
this.ch = ch;
this.readInterestOp = readInterestOp;
try {
ch.configureBlocking(false);
} catch (IOException e) {
try {
ch.close();
} catch (IOException e2) {
if (logger.isWarnEnabled()) {
logger.warn(
"Failed to close a partially initialized socket.", e2);
}
}
throw new ChannelException("Failed to enter non-blocking mode.", e);
}
}
- 和创建服务端channel不同的是,这里的parent不再为null,而是服务端channel(channel)。
- 其它逻辑大致相同,核心同样是ch.configureBlocking(false)设置nio模型。
继续进入super:
io.netty.channel.AbstractChannel#AbstractChannel(io.netty.channel.Channel)
protected AbstractChannel(Channel parent) {
this.parent = parent;
id = newId();
unsafe = newUnsafe();
pipeline = newChannelPipeline();
}
- 这段除了parent存储的是NioServerSocketChannel和newUnsage()返回的是NioSocketChannelUnsafe外,其余和创建服务端channel都是相同逻辑。
如果对客户端unsafe(NioSocketChannelUnsafe)和服务端unsafe(NioMessageUnsafe)感到非常陌生,可以参考之前的小总结
好了,上面追踪了一下构造函数,对比创建服务端channel,发现也就是替换了一些实现。下面将视角重新拉回上面的【坐标1】,来对比下config有什么不同。
跟进new NioSocketChannelConfig构造方法:
io.netty.channel.socket.nio.NioSocketChannel.NioSocketChannelConfig#NioSocketChannelConfig
private NioSocketChannelConfig(NioSocketChannel channel, Socket javaSocket) {
super(channel, javaSocket);
}
继续跟进super构造方法:
io.netty.channel.socket.DefaultSocketChannelConfig#DefaultSocketChannelConfig
public DefaultSocketChannelConfig(SocketChannel channel, Socket javaSocket) {
super(channel);
if (javaSocket == null) {
throw new NullPointerException("javaSocket");
}
this.javaSocket = javaSocket;
// Enable TCP_NODELAY by default if possible.
if (PlatformDependent.canEnableTcpNoDelayByDefault()) {
try {
setTcpNoDelay(true);
} catch (Exception e) {
// Ignore.
}
}
}
- 这段代码其实和服务端config的代码是不一样的,只是之前没贴,下面就贴出来对比一下。
服务端channel的config:
io.netty.channel.socket.DefaultServerSocketChannelConfig#DefaultServerSocketChannelConfig
public DefaultServerSocketChannelConfig(ServerSocketChannel channel, ServerSocket javaSocket) {
super(channel);
if (javaSocket == null) {
throw new NullPointerException("javaSocket");
}
this.javaSocket = javaSocket;
}
- 按照我当初Server.java的配置,其实就是初始化一些tcp的属性。
对比服务端的config和客户端的config,很容易就发现客户端多了下面这一段:
setTcpNoDelay(true);
- 这段代码的作用其实就是关闭Nagle算法,目的是让小数据包更快的发送出去。
当完成了NioSocketChannelConfig的构造后,整个NioSocketChannel就算创建完成了,后续跟创建服务端channel一样,也会初始化(注册、绑定等),关于初始化这里就不展开了,留到下一篇博客。
小结
- NioSocketChannel(客户端channel)是在NioServerSocketChannel(服务端channel)中构建的。
- NioSocketChannel的parent属性保存了NioServerSocketChannel的实例,并且其中的unsafe实现也与NioServerSocketChannel创建的不同。
- NioServerSocketChannel创建后关注的是OP_ACCEPT事件,而本节的NioSocketChannel关注的则是OP_READ事件。
- NioSocketChannel在构建NioSocketChannelConfig实例时,关闭了Nagle算法,目的是让小数据包更快发送,降低响应延迟。