# **回顾**
在[上一节](https://wenjie.store/archives/netty-handle-new-connections-1)中,我简单的记录了Netty获取一个新连接的过程,而在这个过程中,其实还有非常重要的一步没有展开,那就是创建客户端channel,即创建NioSocketChannel。
如果NioSocketChannel非常陌生,可以去看看我前面写的[小总结](https://wenjie.store/archives/netty-channel-unsafe-config-simple-classification),可以有一个大概的了解。
<blockquote><p>
Netty Version:4.1.6
</p></blockquote>
<br/>
# **大致流程**
- new NioSocketChannel(parent, socket) - 入口
- AbstractNioByteChannel(parent, ch) - super
- AbstractNioByteChannel(parent, sh , readInterestOp)
- AbstractChannel构造 - 创建id,pipeline,unsage等
- ch.configureBlocking(false) - 配置nio
- new NioSocketChannelConfig(channel, javaSocket) - 配置类
- setTcpNoDelay(true) - 关闭Nagle算法
<br/>
# **跟进源码**
首先,先找到这次跟进源码的入口,即:<code>io.netty.channel.socket.nio.NioServerSocketChannel#doReadMessages</code>,在[上一节](https://wenjie.store/archives/netty-handle-new-connections-1)文末处:
```java
@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;
}
```
<br/>
跟进上面的的new NioSocketChannel(this, ch),此处【坐标1】:
<code>io.netty.channel.socket.nio.NioSocketChannel#NioSocketChannel(io.netty.channel.Channel, java.nio.channels.SocketChannel)</code>
```java
public NioSocketChannel(Channel parent, SocketChannel socket) {
super(parent, socket);
config = new NioSocketChannelConfig(this, socket.socket());
}
```
- 这段代码还真是相当眼熟了,在[创建服务端channel](https://wenjie.store/archives/netty-boot-do-something-1)那一节也遇到过,只是这里换成客户端channel,逻辑大致相似。
<br/>
这里先进入super构造方法:
<code>io.netty.channel.nio.AbstractNioByteChannel#AbstractNioByteChannel</code>
```java
protected AbstractNioByteChannel(Channel parent, SelectableChannel ch) {
super(parent, ch, SelectionKey.OP_READ);
}
```
- 这里和[创建服务端channel](https://wenjie.store/archives/netty-boot-do-something-1)不同的是,**readInterestOp参数变为了OP_READ(值为1),也就是说客户端channel后面会关注一个读事件。**
<br/>
继续进入super:
<code>io.netty.channel.nio.AbstractNioChannel#AbstractNioChannel</code>
```java
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](https://wenjie.store/archives/netty-boot-do-something-1)不同的是,这里的parent不再为null,而是服务端channel(channel)。
- 
- 其它逻辑大致相同,核心同样是ch.configureBlocking(false)设置nio模型。
<br/>
继续进入super:
<code>io.netty.channel.AbstractChannel#AbstractChannel(io.netty.channel.Channel)</code>
```java
protected AbstractChannel(Channel parent) {
this.parent = parent;
id = newId();
unsafe = newUnsafe();
pipeline = newChannelPipeline();
}
```
- 这段除了parent存储的是NioServerSocketChannel和newUnsage()返回的是NioSocketChannelUnsafe外,其余和[创建服务端channel](https://wenjie.store/archives/netty-boot-do-something-1)都是相同逻辑。
<blockquote><p>
如果对客户端unsafe(NioSocketChannelUnsafe)和服务端unsafe(NioMessageUnsafe)感到非常陌生,可以参考之前的<a href="https://wenjie.store/archives/netty-channel-unsafe-config-simple-classification">小总结</a>
</p></blockquote>
<br/>
好了,上面追踪了一下构造函数,对比[创建服务端channel](https://wenjie.store/archives/netty-boot-do-something-1),发现也就是替换了一些实现。下面将视角重新拉回上面的【坐标1】,来对比下config有什么不同。
<br/>
跟进new NioSocketChannelConfig构造方法:
<code>io.netty.channel.socket.nio.NioSocketChannel.NioSocketChannelConfig#NioSocketChannelConfig</code>
```java
private NioSocketChannelConfig(NioSocketChannel channel, Socket javaSocket) {
super(channel, javaSocket);
}
```
<br/>
继续跟进super构造方法:
<code>io.netty.channel.socket.DefaultSocketChannelConfig#DefaultSocketChannelConfig</code>
```java
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:
<code>io.netty.channel.socket.DefaultServerSocketChannelConfig#DefaultServerSocketChannelConfig</code>
```java
public DefaultServerSocketChannelConfig(ServerSocketChannel channel, ServerSocket javaSocket) {
super(channel);
if (javaSocket == null) {
throw new NullPointerException("javaSocket");
}
this.javaSocket = javaSocket;
}
```
- 按照我当初Server.java的配置,其实就是初始化一些tcp的属性。
<br/>
对比服务端的config和客户端的config,很容易就发现客户端多了下面这一段:
```java
setTcpNoDelay(true);
```
- **这段代码的作用其实就是关闭Nagle算法,目的是让小数据包更快的发送出去。**
<br/>
当完成了NioSocketChannelConfig的构造后,整个NioSocketChannel就算创建完成了,后续跟创建服务端channel一样,也会初始化(注册、绑定等),关于初始化这里就不展开了,留到下一篇博客。
---
<be/>
# **小结**
- NioSocketChannel(客户端channel)是在NioServerSocketChannel(服务端channel)中构建的。
- NioSocketChannel的parent属性保存了NioServerSocketChannel的实例,并且其中的unsafe实现也与NioServerSocketChannel创建的不同。
- NioServerSocketChannel创建后关注的是OP_ACCEPT事件,而本节的NioSocketChannel关注的则是OP_READ事件。
- NioSocketChannel在构建NioSocketChannelConfig实例时,关闭了Nagle算法,目的是让小数据包更快发送,降低响应延迟。

【Netty】处理新连接(二):创建客户端channel(NioSocketChannel)