netty(二)---客户端连接
概述
先了解一下 netty 大概框架图 ,可以看到客户端的创建和服务端最大的区别 - 服务端传入两个 EventLoopGroup,客户端传入一个 EventLoopGroup - channel 的类型也不同,服务端传入的是 NioServerSocketChannel ,客户端传入的是 NioSocketChannel - 服务端存在 childHandler 的设置,客户端没有,
客户端连接过程 : - 和服务端一样先创建 EventLoopGroup (只有一个,内部多个保持多个EventLoop线程,执行处理事务) - connect 方法 ,和服务端一样,使用group里的EventLoop创建一个 channel ,然后注册到 select 中去(这个过程都在channel 中进行) - (异步执行)当连接不上就会交给 EventLoop线程中执行监听的任务。 - 而一旦监听到了就交给 channel 执行。 - selectKey 可以attack一个object,刚好可以用来放channel ,然后在某个线程监听到某个实现的时候再把 channel 拿出来用
源码分析
实际上客户端只有一个 Reactor . 那么重点的逻辑就到了 connect 那里
/**
* Connect a {@link Channel} to the remote peer.
*/
public ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress) {
if (remoteAddress == null) {
throw new NullPointerException("remoteAddress");
}
validate();
return doConnect(remoteAddress, localAddress);
} /**
* @see {@link #connect()}
*/
private ChannelFuture doConnect(final SocketAddress remoteAddress, final SocketAddress localAddress) {
//创建channel ,注册到 select , initAndRegister方法是父类的方法我们在分析服务端
//的时候已经分析过了
final ChannelFuture regFuture = initAndRegister();
final Channel channel = regFuture.channel();
//一开始就连接上了,
if (regFuture.cause() != null) {
return regFuture;
} final ChannelPromise promise = channel.newPromise();
if (regFuture.isDone()) {
//链路成功后,异步连接 TCP
doConnect0(regFuture, channel, remoteAddress, localAddress, promise);
} else {
regFuture.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
doConnect0(regFuture, channel, remoteAddress, localAddress, promise);
}
});
} return promise;
} private static void doConnect0(
final ChannelFuture regFuture, final Channel channel,
final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise promise) { // This method is invoked before channelRegistered() is triggered. Give user handlers a chance to set up
// the pipeline in its channelRegistered() implementation.
// 到了执行连接的操作就转到了Netty 的 NIO线程执行,此刻客户端返回,连接异步执行。
channel.eventLoop().execute(new Runnable() {
@Override
public void run() {
if (regFuture.isSuccess()) {
if (localAddress == null) {
//没传 localAddress 会传到 TailHandler的connect方法
channel.connect(remoteAddress, promise);
} else {
//正常情况下到 HeaderHandler的connect 方法
channel.connect(remoteAddress, localAddress, promise);
}
promise.addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
} else {
promise.setFailure(regFuture.cause());
}
}
});
}
我们看一下 HeaderHandler的connect 方法。
@Override
public void connect(
ChannelHandlerContext ctx,
SocketAddress remoteAddress, SocketAddress localAddress,
ChannelPromise promise) throws Exception {
//执行 HeaderHandler内的unsafe字段的 connect 方法
unsafe.connect(remoteAddress, localAddress, promise);
}
unsafe会执行AbstractNioChannel(这个类是NioServerSocketChannel和NioSocketChannel的共同父类)的connect 方法
@Override
public void connect(
final SocketAddress remoteAddress, final SocketAddress localAddress, final ChannelPromise promise) {
if (!ensureOpen(promise)) {
return;
} try {
if (connectPromise != null) {
throw new IllegalStateException("connection attempt already made");
} boolean wasActive = isActive();
//doConnect 方法是个抽象方法
if (doConnect(remoteAddress, localAddress)) {
fulfillConnectPromise(promise, wasActive);
} else {
connectPromise = promise;
requestedRemoteAddress = remoteAddress; // Schedule connect timeout.
int connectTimeoutMillis = config().getConnectTimeoutMillis();
if (connectTimeoutMillis > 0) {
connectTimeoutFuture = eventLoop().schedule(new Runnable() {
@Override
public void run() {
ChannelPromise connectPromise = AbstractNioChannel.this.connectPromise;
ConnectTimeoutException cause =
new ConnectTimeoutException("connection timed out: " + remoteAddress);
if (connectPromise != null && connectPromise.tryFailure(cause)) {
close(voidPromise());
}
}
}, connectTimeoutMillis, TimeUnit.MILLISECONDS);
} promise.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isCancelled()) {
if (connectTimeoutFuture != null) {
connectTimeoutFuture.cancel(false);
}
connectPromise = null;
close(voidPromise());
}
}
});
}
} catch (Throwable t) {
if (t instanceof ConnectException) {
Throwable newT = new ConnectException(t.getMessage() + ": " + remoteAddress);
newT.setStackTrace(t.getStackTrace());
t = newT;
}
promise.tryFailure(t);
closeIfClosed();
}
}
NioSocketChannel 的 doConnect 方法
@Override
protected 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如何处理连接事件 上文讲了Netty如何绑定端口,现在我们来阅读下netty如何处理connect事件.上文我们说了NioEventLoop启动后不断去调用select的事件,当客户端连接时 ...
- 【ActiveMQ】之安全机制(二)客户端连接安全
配置完管控台的安全之后,我们还要配置客户端连接安全,否则大家都可以往MQ上发送消息,这样太危险! 根据官方文档,http://activemq.apache.org/security.html Act ...
- Netty 多客户端连接与通信
实现场景: 聊天 服务端,客户端A,客户端B,客户端C.当客户端发送消息给服务端后,服务端在将这条消息广播个所有客户端户端A,客户端B,客户端C. 需求1: 客户端上线后,会通知所有客户端上线. 如客 ...
- Netty源码分析 (六)----- 客户端连接接入accept过程
通读本文,你会了解到1.netty如何接受新的请求2.netty如何给新请求分配reactor线程3.netty如何给每个新连接增加ChannelHandler netty中的reactor线程 ne ...
- java socket通讯(二)处理多个客户端连接
通过java socket通讯(一) 入门示例,就可以实现服务端和客户端的socket通讯,但是上一个例子只能实现一个服务端和一个客户端之间的通讯,如果有多个客户端连接服务端,则需要通过多线程技术来实 ...
- Memcache的客户端连接系列(二) Python
关键词: Memcached Python 客户端 声明:本文并非原创,转自华为云帮助中心的分布式缓存服务(Memcached)的用户指南.客户端连接方法通用,故摘抄过来分享给大家. Python ...
- 一个I/O线程可以并发处理N个客户端连接和读写操作 I/O复用模型 基于Buf操作NIO可以读取任意位置的数据 Channel中读取数据到Buffer中或将数据 Buffer 中写入到 Channel 事件驱动消息通知观察者模式
Tomcat那些事儿 https://mp.weixin.qq.com/s?__biz=MzI3MTEwODc5Ng==&mid=2650860016&idx=2&sn=549 ...
- Redis基础知识之————如何处理客户端连接
redis 连接建立 Redis Redis 通过监听一个 TCP 端口或者 Unix socket 的方式来接收来自客户端的连接,当一个连接建立后,Redis 内部会进行以下一些操作: 首先,客户端 ...
- PostgreSQL数据库服务端监听设置及客户端连接方法教程
众所周知,PostgreSQL 是一个自由的对象-关系数据库服务器(数据库管理系统),是一个可以免费使用的开放源代码数据库系统.本文详细介绍了PostgreSQL数据库服务端监听设置及客户端连接方法, ...
随机推荐
- C# 后台调用http,post访问url,获取数据
1.封装post方法发送 using System; using System.Collections.Generic; using System.IO; using System.Linq; usi ...
- A New Recurrence-Network-Based Time Series Analysis Approach for Characterizing System Dynamics - Guangyu Yang, Daolin Xu * and Haicheng Zhang
Purpose: characterize the evolution of dynamical systems. In this paper, a novel method based on eps ...
- Hibernate的基本工作原理
Hibernate开发过程中会用到5个核心接口: 1.Configuration2.SessionFactory3.Session4.Transaction5.QueryHibernate就是通过这些 ...
- 使用鼠标左键事件实现VR中的Eye Gaze Input
1.光标以及光标动画的显示 using System.Collections; using System.Collections.Generic; using UnityEngine; using U ...
- 阿里巴巴手册之-Arrays.asList()数组转集合的问题
转载来源:https://blog.csdn.net/qq_36443497/article/details/79663663?utm_source=blogxgwz9 在使用工具类Arrays.as ...
- spring(一):思维导图
- testng的注解
今天又学了点testng的新知识.原来在testng执行用例时,同一个class中的各个method按照字母顺序执行.为了实现自定义顺序执行,怎么办呢? 加入注解priority,举例如下: http ...
- docker 初识1
学习网址 https://git.oschina.net/yangllsdev/docker-training https://docs.docker.com/engine/installation/ ...
- JS高级---原型和原型链
原型和原型链 原型链是一种关系, 实例对象和原型对象之间的关系,关系是通过实例对象中浏览器使用的原型(__proto__)来联系的 自定义构造函数,通过实例化,创建实例对象 实例对象中__proto_ ...
- navicat导入.csv表格
我本地的navicat不知道啥情况,导入不了表格,然后把表格转为.csv的,然后导入就好了 1.表格另存为.csv格式的 2.打开Navicat,选择要导入的表,然后右键->导入向导,选择.cs ...