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数据库服务端监听设置及客户端连接方法, ...
随机推荐
- Linux oracle安装 内核参数讲解
在安装Oracle的时候需要调整linux的内核参数,但是各参数代表什么含义呢,下面做详细解析. Linux安装文档中给出的最小值: fs.aio-max-nr = 1048576 fs.file-m ...
- Java基本语法--关键字&标识符
本篇博客主要介绍了Java基本语法中的关键字.保留字和标识符. 关键字与保留字 关键字(keyword的定义及特点) ✄ 定义:被Java 语言赋予了特殊含义,用做专门用途的字符串 ✄ 特点:关键字中 ...
- linux上安装git以及使用
用git --version命令检查是否已经安装 在CentOS5的版本,由于yum源中没有git,所以需要预先安装一系列的依赖包.在CentOS6的yum源中已经有git的版本了,可以直接使用yum ...
- UIgradients – 美丽的UI渐变色分享站 并可转成CSS代码
前期我们分享了「如何使用彩色滤镜创造奇妙的网页设计」这篇文章,通过渐变彩色滤镜实现很多漂亮的效果,然而用什么渐变颜色才好呢?可以看看今天为大家分享的 UIgradients 渐变色分享网站,里面有很多 ...
- 1032 Sharing (25分)
1032 Sharing (25分) 题目 思路 定义map存储所有的<地址1,地址2> 第一set存放单词1的所有地址(通过查找map) 通过单词二的首地址,结合map,然后在set中查 ...
- php设计模式之责任链模式实现举报功能实例代码
html <html> <head> <meta charset="UTF-8"> <title>责任链模式</title&g ...
- deepin linux 安装之后 引导错误 出现 grub>
deepin 安装之后 引导错误 ,,, 忙了一晚上 终于解决了 太辛苦了 不过明白了grub的工作原理也不亏,,,, 就是 整个过程满满的绝望 (哭 环境说明 华硕顽石4 笔记本 硬盘分区表GPT ...
- Docker - 最近的踩到的一些坑
概述 最近学习 docker 遇到的 坑 1. dockerfile: 安装命令 概述 安装命令 坑 选项参数里, 一定要 带 -y 不带的话, 基本会阻塞构建 2. 其他: 处理问题, 一定不能慌 ...
- testng的prioriy
todo: 同一个class中的priority: 1.不标priority的case和标注priority的case,谁先谁后? 2.标注相同priority的case,谁先谁后?是不是并发? 3. ...
- nginx 定义:响应头和请求头
1) 响应头 add_header 例如: add_header Cache-Control no-cache; add_header Access-Control-Allow-Origin *; a ...