连接源码
首先从客户端开始,编写一个客户端代码
public class EchoClient {
private final String host; //主机号
private final int port; //端口
public EchoClient(String host, int port) {
this.host = host;
this.port = port;
}
public static void main(String[] args) throws InterruptedException {
new EchoClient("127.0.0.1", 8080).start();
}
public void start() throws InterruptedException {
EventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap b = new Bootstrap(); //创建Bootstrap对象
b.group(group)
.channel(NioSocketChannel.class) //设置channel为适用于NIO传输的channel
.remoteAddress(new InetSocketAddress(host, port))
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast(new EchoClientHandler());
}
});
ChannelFuture f = b.connect().sync(); //连接到远程节点,阻塞等待知道连接完成
f.channel().closeFuture().sync(); //阻塞,关闭channel
} finally {
group.shutdownGracefully().sync();
}
}
}
上面的代码展示了netty客户端初始化所需的所有内容
(1)为初始化客户端,创建一个Bootstrap实例
(2)为进行事件处理分配一个NioEventLoopGroup实例,其中事件处理包括创建新的连接以及处理入站和出站数据
(3)为服务器连接创建一个InetSocketAddress实例
(4)当连接建立时,一个EchoClientHandle实例会被安装到ChannelPipeline中
(5)完成一切设置后,调用Bootstrap.connect方法连接到远程节点
NioSocketChannel的初始化过程
在 Netty 中,Channel 是一个 Socket 的抽象,它为用户提供了关于 Socket 状态(是否是连接还是断开) 以及对 Socket 的读写等操作. 每当 Netty 建立了一个连接后,都会有一个对应的 Channel 实例.
NioSocketChannel 的类层次结构如下:
下面我们着重分析一下Channel的初始化过程:
除了TCP协议外,Netty还支持很多其他的连接协议(如UDP,Sctp),并且每种协议还有NIO和OIO(Old-IO)版本的区别,下面给出一些常用的Channel类型:
- NioSocketChannel: 代表异步的客户端的TCP连接
- NioServerSocketChannel:代表异步的服务端的TCP连接
- NioDatagramChannel:异步的 UDP 连接
- NioSctpChannel:异步的客户端 Sctp 连接.
- NioSctpServerChannel:异步的 Sctp 服务器端连接.
- OioSocketChannel:同步的客户端 TCP Socket 连接.
- OioServerSocketChannel:同步的服务器端 TCP Socket 连接.
- OioDatagramChannel:同步的 UDP 连接
- OioSctpChannel:同步的 Sctp 服务器端连接
- OioSctpServerChannel:同步的客户端 TCP Socket 连接
如此多的Channel类型,那我们究竟如何选择我们的Channel呢?答案就是channel方法的调用
在上面客户端连接代码的初始化Bootstrap中,会调用channel方法,传入NioSocketChannel,这个方法其实就是初始化了一个ReflectiveChannelFactory
/**
* The {@link Class} which is used to create {@link Channel} instances from.
* You either use this or {@link #channelFactory(io.netty.channel.ChannelFactory)} if your
* {@link Channel} implementation has no no-args constructor.
*/
public B channel(Class<? extends C> channelClass) {
if (channelClass == null) {
throw new NullPointerException("channelClass");
}
return channelFactory(new ReflectiveChannelFactory<C>(channelClass));
}
ReflectiveChannelFactory实现了ChannelFactory接口,提供了一个newChannel来产生Channel的实例.而ChannelFactory一看就是工厂模式的体现,即一个产生Channel的工厂方法
@Override
public T newChannel() {
try {
return clazz.getConstructor().newInstance(); //通过反射来获取实例
} catch (Throwable t) {
throw new ChannelException("Unable to create Channel from class " + clazz, t);
}
}
根据上面客户端的连接代码,我们可以知道:
- Bootstrap中ChannelFactory的具体实现是ReflectiveChannelFactory
- Channel的实例化过程就是调用ChannelFactory#newChannel方法,而实例化Channel的具体类型又是在初始化Bootstrap 时传入的 channel() 方法的参数相关(客户端连接代码中是NioSocketChannel)
Channel的实例化时机
前面我们知道如何确定一个Channel的类型是通过ChannelFactory的newChannel来实例化,那newChannel这个方法究竟实在什么是调用的呢?
继续采用debug模式跟踪程序调用信息,我们发现调用链
Bootstrap.connect -> Bootstrap.doResolveAndConnect -> AbstractBootstrap.initAndRegister
在 AbstractBootstrap.initAndRegister中就调用了ChannelFactory.newChannel方法
/**
* Connect a {@link Channel} to the remote peer.
*/
public ChannelFuture connect() {
validate();
SocketAddress remoteAddress = this.remoteAddress;
if (remoteAddress == null) {
throw new IllegalStateException("remoteAddress not set");
}
return doResolveAndConnect(remoteAddress, config.localAddress());
}
/**
* @see #connect()
*/
private ChannelFuture doResolveAndConnect(final SocketAddress remoteAddress, final SocketAddress localAddress) {
final ChannelFuture regFuture = initAndRegister();
final Channel channel = regFuture.channel();
//省略了无关代码
}
final ChannelFuture initAndRegister() {
Channel channel = null;
try {
channel = channelFactory.newChannel(); //实例化channel
init(channel);
} catch (Throwable t) {
if (channel != null) {
channel.unsafe().closeForcibly();
return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);
}
return new DefaultChannelPromise(new FailedChannel(), GlobalEventExecutor.INSTANCE).setFailure(t);
}
ChannelFuture regFuture = config().group().register(channel);
if (regFuture.cause() != null) {
if (channel.isRegistered()) {
channel.close();
} else {
channel.unsafe().closeForcibly();
}
}
return regFuture;
}
在 newChannel 中, 通过类对象的 getConstructor().newInstance(); 来获取一个新 Channel 实例,,因而会调用NioSocketChannel 的默认构造器.
NioSocketChannel 默认构造器代码如下:
/**
* Create a new instance
*/
public NioSocketChannel() {
this(DEFAULT_SELECTOR_PROVIDER);
}
/**
* Create a new instance using the given {@link SelectorProvider}.
*/
public NioSocketChannel(SelectorProvider provider) {
this(newSocket(provider));
}
public NioSocketChannel(Channel parent, SocketChannel socket) {
super(parent, socket);
config = new NioSocketChannelConfig(this, socket.socket());
}
这里的newSocket会打开一个新的SocketChannel,接着会继续调用父类的构造器(AbstractNioByteChannel),
并传入参数parent为null,ch为刚才使用newSocket创建的Java NIO SocketChannel,因此生成的NioSocketChannel的parent channel为空
/**
* Create a new instance
*
* @param parent the parent {@link Channel} by which this instance was created. May be {@code null}
* @param ch the underlying {@link SelectableChannel} on which it operates
*/
protected AbstractNioByteChannel(Channel parent, SelectableChannel ch) {
super(parent, ch, SelectionKey.OP_READ); /NIO 中的读写事件
}
接着继续调用父类(AbstractNioChannel)的构造方法
protected AbstractNioChannel(Channel parent, SelectableChannel ch, int readInterestOp) {
super(parent);
this.ch = ch;
this.readInterestOp = readInterestOp;
try {
ch.configureBlocking(false); //NIO 中设置为非阻塞
}
}
然后继续调用父类AbstractChannel的构造方法
protected AbstractChannel(Channel parent) {
this.parent = parent;
id = newId();
unsafe = newUnsafe();
pipeline = newChannelPipeline();
}
到这里,一个完整的NioSocketChannel就初始化完成了
- 调用NioSocketChannel.newSocket(DEFAULT_SELECTOR_PROVIDER) 打开一个新的 Java NIO SocketChannel
- AbstactChannel中初始化AbstactChannel属性
- parent属性置为null
- pipeline是new DefaultChannelPipeline新创建的实例
- AbstractNioChannel中的属性
- SelectableChannel ch 被设置为 Java SocketChannel, 即 NioSocketChannel#newSocket 返回的 Java NIO SocketChannel.
- readInterestOp 被设置为 SelectionKey.OP_READ
- SelectableChannel ch 被配置为非阻塞的 ch.configureBlocking(false)
- NioSocketChannel中的属性
- SocketChannelConfig config = new NioSocketChannelConfig(this, socket.socket())
以上就是NioSocketChannel的初始化过程,因为Netty也是基于多路复用IO的,注意和Java NIO形成对比