当前位置: 首页 > news >正文

Spring架构篇--2.5.2 远程通信基础Select 源码篇--window--sokcet.register

前言:通过Selector.open() 获取到Selector 的选择器后,服务端和客户的socket 都可以通过register 进行socker 的注册;

服务端 ServerSocketChannel 的注册:

  ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();serverSocketChannel.configureBlocking(false);// 设置非阻塞serverSocketChannel.socket().bind(new InetSocketAddress(8080));// 连接事件注册到多路复用serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

ServerSocketChannel 的类关系:
1)ServerSocketChannelImpl extends ServerSocketChannel implements SelChImpl;
2)ServerSocketChannel extends AbstractSelectableChannel implements NetworkChannel;
3) SelChImpl extends Channe;

先看下ServerSocketChannel 实例的初始化: ServerSocketChannel.open()
ServerSocketChannel 类:

public static ServerSocketChannel open() throws IOException {return SelectorProvider.provider().openServerSocketChannel();
}

可以看到先拿到了Select.open() 时创建的SelectorProvider 实例,然后在调用openServerSocketChannel;
SelectorProviderImpl 类:

 public ServerSocketChannel openServerSocketChannel() throws IOException {// 创建ServerSocketChannelImpl 实例return new ServerSocketChannelImpl(this);
}public SocketChannel openSocketChannel() throws IOException {return new SocketChannelImpl(this);
}

ServerSocketChannelImpl 类:

 private static NativeDispatcher nd;private final FileDescriptor fd;private int fdVal;private volatile long thread = 0L;private final Object lock = new Object();private final Object stateLock = new Object();private static final int ST_UNINITIALIZED = -1;private static final int ST_INUSE = 0;private static final int ST_KILLED = 1;private int state = -1;private InetSocketAddress localAddress;private boolean isReuseAddress;ServerSocket socket;ServerSocketChannelImpl(SelectorProvider var1) throws IOException {// 传递创建的 SelectorProvider 实例super(var1);this.fd = Net.serverSocket(true);this.fdVal = IOUtil.fdVal(this.fd);this.state = 0;}

可以看到 ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();中 serverSocketChannel 实际指向的是其子类(ServerSocketChannelImpl)对象。
要说明的是:ServerSocketChannel ssc = ServerSocketChannel.open()创建的这个新的Channel中的Socket是最初的,必须对这个Socket通过bind方法绑定指定的地址之后才能接收连接;

再来看服务端socket 的建立: serverSocketChannel.socket().bind(new InetSocketAddress(8080));
serverSocketChannel.socket():
ServerSocketChannelImpl 类中:

public ServerSocket socket() {synchronized(this.stateLock) {// 加锁保证 同一个ServerSocketChannel socket 的唯一性if (this.socket == null) {this.socket = ServerSocketAdaptor.create(this);}return this.socket;}
}

serverSocketChannel.socket().bind() 绑定要监听的端口;

接下来看serverSocketChannel实例的注册:
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
SelectableChannel 类中register 方法返回注册的SelectionKey:

 public final SelectionKey register(Selector sel, int ops)throws ClosedChannelException{return register(sel, ops, null);}

调用: AbstractSelectableChannel 类中register 方法:

 public final SelectionKey register(Selector sel, int ops,Object att)throws ClosedChannelException
{synchronized (regLock) {// 获取锁if (!isOpen())// serverSocketChannel 通道是否打开throw new ClosedChannelException();if ((ops & ~validOps()) != 0)// serverSocketChannel 的操作行验证throw new IllegalArgumentException();if (blocking)// 如果建立的serverSocketChannel 通道是阻塞的直接抛出异常throw new IllegalBlockingModeException();//  当前通道有没在 Selector进行过注册,如果已经注册则修改属性后直接返回SelectionKey k = findKey(sel);if (k != null) {k.interestOps(ops);k.attach(att);}// 当前通道没有在当前selector 注册if (k == null) {// New registrationsynchronized (keyLock) {if (!isOpen())throw new ClosedChannelException();// 当前管道注册到selector 选择器上k = ((AbstractSelector)sel).register(this, ops, att);addKey(k);}}return k;}
}

validOps:

public final int validOps() {return SelectionKey.OP_ACCEPT;
}

再来看下 ((AbstractSelector)sel).register(this, ops, att):
实现将当前管道注册到当前的Selector 上并返回SelectionKey

protected final SelectionKey register(AbstractSelectableChannel var1, int var2, Object var3) {if (!(var1 instanceof SelChImpl)) {throw new IllegalSelectorException();} else {// 新建 SelectionKeyImpl 赋值属性SelectionKeyImpl var4 = new SelectionKeyImpl((SelChImpl)var1, this);var4.attach(var3);synchronized(this.publicKeys) {// 防止并发多个管道同时 向selector 注册的安全性问题this.implRegister(var4);}var4.interestOps(var2);// 返回新建的SelectionKey 对新return var4;}
}

SelectionKeyImpl的构造方法:可以看出将当前selector 和要注册的管道封装到了SelectionKeyImpl 对象中

final SelChImpl channel;
public final SelectorImpl selector;private int index;private volatile int interestOps;private int readyOps;SelectionKeyImpl(SelChImpl var1, SelectorImpl var2) {this.channel = var1;this.selector = var2;}

继续看 this.implRegister(var4) 具体的注册方法:
WindowsSelectorImpl类

 protected void implRegister(SelectionKeyImpl var1) {// 传入上一步封装的SelectionKeyImpl synchronized(this.closeLock) {// 获取selector 的关闭锁,防止正在注册的时候 ,selector关闭if (this.pollWrapper == null) {throw new ClosedSelectorException();} else {// SelectionKeyImpl  管道数组是否需要进行扩容this.growIfNeeded();// 当前管道放入channelArray数组中this.channelArray[this.totalChannels] = var1;var1.setIndex(this.totalChannels);this.fdMap.put(var1);// publicKeys 增加注册的管道SelectionKeythis.keys.add(var1);// 管道增加到 pollWrapperthis.pollWrapper.addEntry(this.totalChannels, var1);// 管道数量增加++this.totalChannels;}}
}

将管道放入到channelArray 和pollWrapper 中,并增加管道的数量;

growIfNeeded 扩容方法:

private void growIfNeeded() {if (this.channelArray.length == this.totalChannels) {// 2 倍扩容int var1 = this.totalChannels * 2;SelectionKeyImpl[] var2 = new SelectionKeyImpl[var1];System.arraycopy(this.channelArray, 1, var2, 1, this.totalChannels - 1);this.channelArray = var2;this.pollWrapper.grow(var1);}// 辅助线程的数量,当注册到selector 的数量每次达到1024个,就将辅助线程+1if (this.totalChannels % 1024 == 0) {this.pollWrapper.addWakeupSocket(this.wakeupSourceFd, this.totalChannels);++this.totalChannels;++this.threadsCount;}}

通过register 可以看出,它的作用就是将管道注册到selector上,selector 中的publicKeys 记录所有注册的SelectionKey;

再来看下客户端的注册:

  SocketChannel socketChannel = SocketChannel.open();socketChannel.configureBlocking(false);socketChannel.connect(new InetSocketAddress(8080));// 连接事件注册到多路复用socketChannel.register(selector, SelectionKey.OP_CONNECT);

SocketChannel.open() 打开连接:

public static SocketChannel open() throws IOException {return SelectorProvider.provider().openSocketChannel();
}

SelectorProviderImpl 类中获取socket:

public SocketChannel openSocketChannel() throws IOException {return new SocketChannelImpl(this);
}

SocketChannelImpl 的构造方法:

private static NativeDispatcher nd;private final FileDescriptor fd;private final int fdVal;private volatile long readerThread = 0L;private volatile long writerThread = 0L;private final Object readLock = new Object();private final Object writeLock = new Object();private final Object stateLock = new Object();private boolean isReuseAddress;private static final int ST_UNINITIALIZED = -1;private static final int ST_UNCONNECTED = 0;private static final int ST_PENDING = 1;private static final int ST_CONNECTED = 2;private static final int ST_KILLPENDING = 3;private static final int ST_KILLED = 4;private int state = -1;private InetSocketAddress localAddress;private InetSocketAddress remoteAddress;private boolean isInputOpen = true;private boolean isOutputOpen = true;private boolean readyToConnect = false;private Socket socket;SocketChannelImpl(SelectorProvider var1) throws IOException {super(var1);this.fd = Net.socket(true);this.fdVal = IOUtil.fdVal(this.fd);this.state = 0;}

可以看到 new SocketChannelImpl(this)也仅仅是做了初始化,所以返回的SocketChannel 要有自己建立连接的操作;
继续看 socketChannel.register(selector, SelectionKey.OP_CONNECT);
同样调用AbstractSelectableChannel 的register 完成注册:

 public final SelectionKey register(Selector sel, int ops,Object att)throws ClosedChannelException{synchronized (regLock) {if (!isOpen())//  检查SocketChannel  是否建立连接throw new ClosedChannelException();if ((ops & ~validOps()) != 0)// 检查SocketChannel 的操作符throw new IllegalArgumentException();if (blocking)// 检查SocketChannel  通道是否阻塞throw new IllegalBlockingModeException();// 当前SocketChannel 通道是否已经注册到 当前的selector 对象上SelectionKey k = findKey(sel);if (k != null) {k.interestOps(ops);k.attach(att);}if (k == null) {// New registrationsynchronized (keyLock) {if (!isOpen())throw new ClosedChannelException();k = ((AbstractSelector)sel).register(this, ops, att);addKey(k);}}return k;}}

总结:
1 服务端 ServerSocketChannel.open() 和客户端的SocketChannel.open() 只是做了初始化,它们的连接并没有建立;
2 register 方法用来将当前的通道注册到selector对象上,selector 的WindowsSelectorImpl 实例记录了注册的通道信息;

相关文章:

Spring架构篇--2.5.2 远程通信基础Select 源码篇--window--sokcet.register

前言:通过Selector.open() 获取到Selector 的选择器后,服务端和客户的socket 都可以通过register 进行socker 的注册; 服务端 ServerSocketChannel 的注册: ServerSocketChannel serverSocketChannel ServerSocketChannel.open(…...

ISIS协议

ISIS协议基础简介应用场景路由计算过程地址结构路由器分类邻居Hello报文邻居关系建立DIS及DIS与DR的类比链路状态信息的载体链路状态信息的交互路由算法网络分层路由域![在这里插入图片描述](https://img-blog.csdnimg.cn/9027c43b614a4399ae1f54e87a37f047.png)区域间路由简介…...

CRM系统哪种品牌的好?这五款简单好用!

CRM系统哪种品牌的好?这五款简单好用! CRM系统是指利用软件、硬件和网络技术,为企业建立一个客户信息收集、管理、分析和利用的信息系统。CRM系统的基础功能主要包括营销自动化、客户管理、销售管理、客服管理、报表分析等,选择合…...

QT_dbus(ipc进程间通讯)

QT_dbus(ipc进程间通讯) 前言: 参考链接: https://www.cnblogs.com/brt3/p/9614899.html https://blog.csdn.net/weixin_43246170/article/details/120994311 https://blog.csdn.net/kchmmd/article/details/118605315 一个大型项目可能需要多个子程序同…...

华为OD机试 - 数组排序(C++) | 附带编码思路 【2023】

刷算法题之前必看 参加华为od机试,一定要注意不要完全背诵代码,需要理解之后模仿写出,通过率才会高。 华为 OD 清单查看地址:https://blog.csdn.net/hihell/category_12199283.html 华为OD详细说明:https://dream.blog.csdn.net/article/details/128980730 华为OD机试题…...

字符串转换为二进制-课后程序(JAVA基础案例教程-黑马程序员编著-第五章-课后作业)

【案例5-4】 字符串转换为二进制 【案例介绍】 1.任务描述 本例要求编写一个程序,从键盘录入一个字符串,将字符串转换为二进制数。在转换时,将字符串中的每个字符单独转换为一个二进制数,将所有二进制数连接起来进行输出。 案…...

SpringIOC

一、为什么要使用Spring? Spring 是个java企业级应用的开源开发框架。Spring主要用来开发Java应用,但是有些扩展是针对构建J2EE平台的web应用。Spring 框架目标是简化Java企业级应用开发,并通过POJO为基础的编程模型促进良好的编程习惯。 为…...

Debezium系列之:基于数据库信号表和Kafka信号Topic两种技术方案实现增量快照incremental技术的详细步骤

Debezium系列之:基于数据库信号表和Kafka信号Topic两种技术方案实现增量快照incremental技术的详细步骤 一、需求背景二、增量快照技术实现的两种方案三、基于数据库信号表实现增量快照技术的原理1.基于水印的快照2.信令表3.增量快照4.连接起重启四、基于数据库信号表实现增量…...

华为OD机试 - 第 K 个最小码值的字母(Python) | 机试题+算法思路+考点+代码解析 【2023】

第 K 个最小码值的字母 题目 输入一个由n个大小写字母组成的字符串 按照 ASCII 码值从小到大进行排序 查找字符串中第k个最小 ASCII 码值的字母(k>=1) 输出该字母所在字符串中的位置索引(字符串的第一个位置索引为 0) k如果大于字符串长度则输出最大 ASCII 码值的字母所在…...

PointNet++训练自己的数据集(附源码)

本文针对PointNet强大的三维点云分类功能,详细讲解怎么训练自己的数据集,在此之前,需要确保已经能够跑通源码的训练和测试,如果没有,请参考PointNet的源码运行。数据放置1.1. 在mytensor_shape_names.txt中配置自己的分…...

ROS2可视化利器---Foxglove Studio

0. 简介 之前作者已经讲了《ROS1可视化利器—Webviz》,然后就有读者问,ROS2有没有可以使用的可视化工具呢,答案是肯定的,除了plotjuggler这种ROS1和ROS2通用的可视化利器,还有一种全平台通用的软件FoxgloveStudio&…...

python实战应用讲解-【语法基础篇】流程控制-控制流的元素及语句(附示例代码)

目录 控制流的元素 条件 代码块 程序执行 代码块嵌套 控制流语句 if 语句...

[蓝桥杯 2019 省 A] 外卖店优先级

蓝桥杯 2019 年省赛 A 组 G 题题目描述“饱了么”外卖系统中维护着 N家外卖店,编号 1 ∼ N。每家外卖店都有一个优先级,初始时 (0 时刻)优先级都为0。每经过 1 个时间单位,如果外卖店没有订单,则优先级会减少 1&#x…...

Jetson Xavier nx(ubuntu18.04)安装rtl8152网卡驱动和8192网卡驱动

含义 Bus 002 : 指明设备连接到哪条总线。 Device 003 : 表明这是连接到总线上的第二台设备。 ID : 设备的ID,包括厂商的ID和产品的ID,格式 厂商ID:产品ID。 Realtek Semiconductor Corp. RTL8153 Gigabit Ethernet Adapter:生产商名字和设备…...

Rocky 9.1操作系统实现zabbix6.0的安装部署实战

文章目录前言一. 实验环境二. 安装zabbix过程2.1. 安装zabbix源2.2 安装zabbix相关的软件2.3 安装数据库并启动2.4 开始初始化数据库:2.5 创建数据库实例及对应的用户2.6 导入官网提供的数据2.7 配置zabbix 服务的配置文件2.8. 启动服务2.9 从网页进行安装2.10 登陆…...

AQS-ReentrantLock

一、AQS 在 Lock 中,用到了一个同步队列 AQS,全称 AbstractQueuedSynchronizer,它是一个同步工具,也是 Lock 用来实现线程同步的核心组件。 1.AQS 的两种功能 独占和共享。 独占锁:每次只能有一个线程持有锁&#x…...

SpringCloud+Dubbo3 = 王炸 !

前言 全链路异步化的大趋势来了 随着业务的发展,微服务应用的流量越来越大,使用到的资源也越来越多。 在微服务架构下,大量的应用都是 SpringCloud 分布式架构,这种架构总体上是全链路同步模式。 全链路同步模式不仅造成了资源…...

机器学习主要内容的思维导图

机器学习 机器学习: 定义:能够从经验中学习从而能够 把事情不断做好的计算机程序 人工智能的一个分支和 实现方式 理论基础:概率论 数理统计 线性代数 数学分析 数值逼近 最优化理论 计算复杂理论 核心要素:数据 算法 模型 机器…...

嵌套走马灯Carousel

Carousel 的应用很广泛,基础用法这里不多做阐述,感兴趣的可以去element-gui了解Carousel 组件。 今天主要是梳理嵌套走马灯的逻辑,背景如下: 需要对项目做一个展示,项目可能有一个或多个,同时一个项目可能…...

实战——缓存的使用

文章目录前言概述实践一、缓存数据一致1.更新缓存类2.删除缓存类二、项目实践(商城项目)缓存预热双缓存机制前言 对于我们日常开发的应用系统。由于MySQL等关系型数据库读写的并发量是有一定的上线的,当请求量过大时候那数据库的压力一定会上…...

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...

以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:

一、属性动画概述NETX 作用&#xff1a;实现组件通用属性的渐变过渡效果&#xff0c;提升用户体验。支持属性&#xff1a;width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项&#xff1a; 布局类属性&#xff08;如宽高&#xff09;变化时&#…...

23-Oracle 23 ai 区块链表(Blockchain Table)

小伙伴有没有在金融强合规的领域中遇见&#xff0c;必须要保持数据不可变&#xff0c;管理员都无法修改和留痕的要求。比如医疗的电子病历中&#xff0c;影像检查检验结果不可篡改行的&#xff0c;药品追溯过程中数据只可插入无法删除的特性需求&#xff1b;登录日志、修改日志…...

centos 7 部署awstats 网站访问检测

一、基础环境准备&#xff08;两种安装方式都要做&#xff09; bash # 安装必要依赖 yum install -y httpd perl mod_perl perl-Time-HiRes perl-DateTime systemctl enable httpd # 设置 Apache 开机自启 systemctl start httpd # 启动 Apache二、安装 AWStats&#xff0…...

页面渲染流程与性能优化

页面渲染流程与性能优化详解&#xff08;完整版&#xff09; 一、现代浏览器渲染流程&#xff08;详细说明&#xff09; 1. 构建DOM树 浏览器接收到HTML文档后&#xff0c;会逐步解析并构建DOM&#xff08;Document Object Model&#xff09;树。具体过程如下&#xff1a; (…...

【HTTP三个基础问题】

面试官您好&#xff01;HTTP是超文本传输协议&#xff0c;是互联网上客户端和服务器之间传输超文本数据&#xff08;比如文字、图片、音频、视频等&#xff09;的核心协议&#xff0c;当前互联网应用最广泛的版本是HTTP1.1&#xff0c;它基于经典的C/S模型&#xff0c;也就是客…...

实现弹窗随键盘上移居中

实现弹窗随键盘上移的核心思路 在Android中&#xff0c;可以通过监听键盘的显示和隐藏事件&#xff0c;动态调整弹窗的位置。关键点在于获取键盘高度&#xff0c;并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...

Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理

引言 Bitmap&#xff08;位图&#xff09;是Android应用内存占用的“头号杀手”。一张1080P&#xff08;1920x1080&#xff09;的图片以ARGB_8888格式加载时&#xff0c;内存占用高达8MB&#xff08;192010804字节&#xff09;。据统计&#xff0c;超过60%的应用OOM崩溃与Bitm…...

iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈

在日常iOS开发过程中&#xff0c;性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期&#xff0c;开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发&#xff0c;但背后往往隐藏着系统资源调度不当…...

人工智能(大型语言模型 LLMs)对不同学科的影响以及由此产生的新学习方式

今天是关于AI如何在教学中增强学生的学习体验&#xff0c;我把重要信息标红了。人文学科的价值被低估了 ⬇️ 转型与必要性 人工智能正在深刻地改变教育&#xff0c;这并非炒作&#xff0c;而是已经发生的巨大变革。教育机构和教育者不能忽视它&#xff0c;试图简单地禁止学生使…...