netty 客户端连接过程分析
来源:互联网 发布:简单数据库设计实例 编辑:程序博客网 时间:2024/06/07 11:16
经过上面的各种分析后, 我们大致了解了 Netty 初始化时, 所做的工作, 那么接下来我们就直奔主题, 分析一下客户端是如何发起 TCP 连接的.
客户端通过调用 Bootstrap 的 connect 方法进行连接.
public ChannelFuture connect(String inetHost, int inetPort) { return connect(InetSocketAddress.createUnresolved(inetHost, inetPort)); } public ChannelFuture connect(InetAddress inetHost, int inetPort) { return connect(new InetSocketAddress(inetHost, inetPort)); } public ChannelFuture connect(SocketAddress remoteAddress) { if (remoteAddress == null) { throw new NullPointerException("remoteAddress"); } validate(); return doResolveAndConnect(remoteAddress, config.localAddress()); }
private ChannelFuture doResolveAndConnect(final SocketAddress remoteAddress, final SocketAddress localAddress) { final ChannelFuture regFuture = initAndRegister(); final Channel channel = regFuture.channel(); ............................. netty 初始化完成后,开始做解析并连接的相关工作 ............................. return doResolveAndConnect0(channel, remoteAddress, localAddress, channel.newPromise()); } }
在 connect 中, 会进行一些参数检查后, 最终调用的是 doConnect 方法, 其实现如下:
private static void doConnect( final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise connectPromise) { final Channel channel = connectPromise.channel(); channel.eventLoop().execute(new Runnable() { @Override public void run() { if (localAddress == null) { channel.connect(remoteAddress, connectPromise); } else { channel.connect(remoteAddress, localAddress, connectPromise); } connectPromise.addListener(ChannelFutureListener.CLOSE_ON_FAILURE); } }); }
在 doConnect 中, 会在 event loop 线程中调用 Channel 的 connect 方法, 而这个 Channel 的具体类型是什么呢? 我们在 Channel 初始化这一小节中已经分析过了, 这里 channel 的类型就是 NioSocketChannel.
进行跟踪到 channel.connect 中, 我们发现它调用的是 AbstractChannel#connect, 而 AbstractChannel 的 connect 代码如下:
@Override public ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise) { ....................................... 这里的 pipeline 为 DefaultChannelPipeline ....................................... return pipeline.connect(remoteAddress, promise); }
我们继续跟踪,发现调用的是DefaultChannelPipeline#connect,其代码如下:
@Override public final ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise) { return tail.connect(remoteAddress, promise); }
而 tail 字段, 我们已经分析过了, 是一个 TailContext 的实例, 而 TailContext 又是 AbstractChannelHandlerContext 的子类, 并且没有实现 connect 方法, 因此这里调用的其实是 AbstractChannelHandlerContext.connect, 我们看一下这个方法的实现:
@Override public ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise) { return connect(remoteAddress, null, promise); }
public ChannelFuture connect( final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise promise) { ........................................... 找到第一个outbound 为 true 的 ChannelHandlerContext, 实际上就是HeadContext ........................................... final AbstractChannelHandlerContext next = findContextOutbound(); EventExecutor executor = next.executor(); if (executor.inEventLoop()) { next.invokeConnect(remoteAddress, localAddress, promise); } else { safeExecute(executor, new Runnable() { @Override public void run() { next.invokeConnect(remoteAddress, localAddress, promise); } }, promise, null); } return promise; }
上面的代码中有一个关键的地方, 即 final AbstractChannelHandlerContext next = findContextOutbound(), 这里调用 findContextOutbound 方法, 从 DefaultChannelPipeline 内的双向链表的 tail 开始, 不断向前寻找第一个 outbound 为 true 的 AbstractChannelHandlerContext,
private AbstractChannelHandlerContext findContextOutbound() { AbstractChannelHandlerContext ctx = this; do { ctx = ctx.prev; } while (!ctx.outbound); return ctx; }
然后调用它的 invokeConnect 方法, 其代码如下:
private void invokeConnect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) { if (invokeHandler()) { try { ..................................................... handler() 实际上就是 HeadContext .................................... ((ChannelOutboundHandler) handler()).connect(this, remoteAddress, localAddress, promise); } catch (Throwable t) { notifyOutboundHandlerException(t, promise); } } else { connect(remoteAddress, localAddress, promise); } }
HeadContext.connect, 其代码如下
@Override public void connect( ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) throws Exception { unsafe.connect(remoteAddress, localAddress, promise); }
这个unsafe 其实就是 NioSocketChannelUnsafe,而NioSocketChannelUnsafe.connnect方法的实现是 AbstractNioUnsafe.connect,其代码实现:
@Overridepublic final void connect( final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise promise) { boolean wasActive = isActive(); if (doConnect(remoteAddress, localAddress)) { fulfillConnectPromise(promise, wasActive); } else { ... }}
AbstractNioUnsafe.connect 的实现如上代码所示, 在这个 connect 方法中, 调用了 doConnect 方法, 注意, 这个方法并不是 AbstractNioUnsafe 的方法, 而是 AbstractNioChannel 的抽象方法. doConnect 方法是在 NioSocketChannel 中实现的, 因此进入到 NioSocketChannel.doConnect 中:
@Overrideprotected boolean doConnect(SocketAddress remoteAddress, SocketAddress localAddress) throws Exception { if (localAddress != null) { javaChannel().socket().bind(localAddress); } boolean success = false; try { boolean connected = javaChannel().connect(remoteAddress); if (!connected) { selectionKey().interestOps(SelectionKey.OP_CONNECT); } success = true; return connected; } finally { if (!success) { doClose(); } }}
至此连接结束
- netty 客户端连接过程分析
- Netty源码分析:客户端连接
- 【Netty源码分析】客户端connect服务端过程
- netty 客户端初始化 and 注册过程分析
- Netty启动过程分析
- netty 服务端连接分析
- netty源码分析之客户端
- Netty学习心得 netty服务端和客户端的连接
- 基于Netty的长连接客户端
- netty5笔记-总体流程分析5-客户端连接过程
- 【Zookeeper源码二】Zookeeper 客户端创建连接过程分析
- Netty系列-客户端启动源码分析
- Netty(二):服务端客户端实例分析
- 【Netty源码分析】Netty服务端bind端口过程
- 【Netty源码分析】发送数据过程
- 【Netty源码分析】数据读取过程
- Netty 拆包 丢包 过程分析
- Netty的启动执行过程分析(一)
- POJ题目分类
- Piotr's Ants UVA
- CSS 块级元素/内联元素、继承与相关属性
- netty 客户端初始化 and 注册过程分析
- <c语言经典100例>c18 反弹距离
- netty 客户端连接过程分析
- Maven – 环境配置
- hdu-5861 线段树
- ThreadLocal源码解读
- 操作系统原理总结
- java调用系统命令
- Glide-You cannot start a load for a destroyed activity
- 虚拟内存
- xdoj 1243 ckj老师爱数学