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++ 深拷贝实例-改变原生数组
深拷贝 main.cpp #include <stdio.h> #include "IntArray.h" int main() { IntArray a(); ; i ...
- laravel框架实现发送邮件的功能
1.在config 下的mail.php中配置(配置后面的两个就行了) 'from' => [ 'address' => env('MAIL_FROM_ADDRESS', '7623018 ...
- vue里不同数据的循环,其中的数组对象
用产品的属性数据说明 页面里显示效果为:要把产品的属性显示到页面上,产品属性为后台自主上传产品的属性,产品的属性不同,所以需要把属性和属性值显示到页面上 产品属性数据为: properties: &q ...
- spring(三):BeanFactory
- Pacemaker+ISCSI实现Apache高可用-配置
一.配置文件系统 任意节点用ISCSI的共享磁盘创建LVM node1 pvcreate /dev/sdb vgcreate my_vg /dev/sdb lvcreate -L 1G -n web_ ...
- DuPan不限速教程
准备: 1.一个百度网盘链接 2.一个可以切换UA的浏览器, 手机版:via,极速浏览器,Kiwi浏览器(推荐)电脑版:未知 3.你的手和脑子
- 每天进步一点点------Xilinx IP 内核
ISE 设计套件 11.1 版本中提供了众多全新的 IP 内核.数学函数:Multiply Adder v2.0 —— 执行两个操作数的乘法,并采用 XtremeDSP™ 解决方案切片将完全精确的乘积 ...
- JavaWeb项目忘记添加依赖
有的时候我们建项目的时候忘记添加项目的依赖了,这里示范一个提示错误,就是 The superclass "javax.servlet.http.HttpServlet" was n ...
- js对象冒充实现的继承
//人类 function Person(name) { this.name = name; this.showName = function () { console.log("my na ...
- poj 2195 Going Home(最小费用流)
题目链接:http://poj.org/problem?id=2195 题目大意是给一张网格,网格中m代表人,h代表房子,网格中的房子和人数量相等,人可以向上向下走,每走1步花费加1,每个房子只能住一 ...