Netty-NIO
文章目录
- 一、NIO-Selector
- 1.处理accept
- 2.cancel
- 3.处理read
- 4.处理客户端断开
- 5. 处理消息的边界
- 6. 写入内容过多的问题
- 7. 处理可写事件
一、NIO-Selector
1.处理accept
//1.创建selector,管理多个channel
Selector selector = Selector.open();
ByteBuffer buffer = ByteBuffer.allocate(16);
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.configureBlocking(false);
//2.建立selector和channel的联系(注册)
//SelectionKey就是将来事件发生后,通过它可以知道事件和哪个channel的事件
//四个事件:
//accept 会在有连接请求时触发
//connect 是客户端,连接建立后触发
//read 可读事件
//write 可写事件
SelectionKey sscKey = ssc.register(selector, 0, null);
sscKey.interestOps(SelectionKey.OP_ACCEPT);
ssc.bind(new InetSocketAddress(8080));
while(true){//3.select方法,没有事件发生,线程阻塞,有事件,线程才会恢复运行selector.select();//4.处理事件,selectedKeys内部包含了所有发生的事件Iterator<SelectionKey> iter = selector.selectedKeys.iterator();while(iter.next()){SelectionKey key = iter.next();ServerSocketChannel channel = (ServerSocketChannel)key.channel();SocketChannel sc = channel.accept();}
}
2.cancel
//1.创建selector,管理多个channel
Selector selector = Selector.open();
ByteBuffer buffer = ByteBuffer.allocate(16);
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.configureBlocking(false);
//2.建立selector和channel的联系(注册)
SelectionKey sscKey = ssc.register(selector, 0, null);
sscKey.interestOps(SelectionKey.OP_ACCEPT);
ssc.bind(new InetSocketAddress(8080));
while(true){//3.select方法,没有事件发生,线程阻塞,有事件,线程才会恢复运行//select在事件未处理时,它不会阻塞,事件发生后要么处理,要么取消,不能置之不理selector.select();//4.处理事件,selectedKeys内部包含了所有发生的事件Iterator<SelectionKey> iter = selector.selectedKeys.iterator();while(iter.next()){SelectionKey key = iter.next();key.cancel();}
}
3.处理read
用完key必须要remove
//1.创建selector,管理多个channel
Selector selector = Selector.open();
ByteBuffer buffer = ByteBuffer.allocate(16);
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.configureBlocking(false);
//2.建立selector和channel的联系(注册)
SelectionKey sscKey = ssc.register(selector, 0, null);
sscKey.interestOps(SelectionKey.OP_ACCEPT);
ssc.bind(new InetSocketAddress(8080));
while(true){//3.select方法,没有事件发生,线程阻塞,有事件,线程才会恢复运行selector.select();//4.处理事件,selectedKeys内部包含了所有发生的事件//selector会在发生事件后,向集合中加入key,但不会删除Iterator<SelectionKey> iter = selector.selectedKeys.iterator();while(iter.next()){SelectionKey key = iter.next();//处理key时,要从selectedKeys集合中删除,否则下次处理就会有问题iter.remove();//5.区分事件类型if(key.isAcceptable()){ //如果是acceptServerSocketChannel channel = (ServerSocketChannel)key.channel();SocketChannel sc = channel.accept();sc.configureBlocking(false);SelectionKey sckey = sc.register(selector, 0, null);scKey.interestOps(SelectionKey.OP_READ);}elseif(key.isReadable()){//拿到触发事件的channelServerSocketChannel channel = (ServerSocketChannel)key.channel();ByteBuffer buffer = ByteBuffer.allocate(16);channel.read(buffer);buffer.flip();debugRead(buffer);}}
}
4.处理客户端断开
//1.创建selector,管理多个channel
Selector selector = Selector.open();
ByteBuffer buffer = ByteBuffer.allocate(16);
ServerSocketChannel ssc = ServerSocketChannel.open();
ssc.configureBlocking(false);
//2.建立selector和channel的联系(注册)
SelectionKey sscKey = ssc.register(selector, 0, null); sscKey.interestOps(SelectionKey.OP_ACCEPT);
ssc.bind(new InetSocketAddress(8080));
while(true){ //3.select方法,没有事件发生,线程阻塞,有事件,线程才会恢复运行 selector.select(); //4.处理事件,selectedKeys内部包含了所有发生的事件 //selector会在发生事件后,向集合中加入key,但不会删除 Iterator<SelectionKey> iter = selector.selectedKeys.iterator(); while(iter.next()){ SelectionKey key = iter.next(); //处理key时,要从selectedKeys集合中删除,否则下次处理就会有问题 iter.remove(); //5.区分事件类型 if(key.isAcceptable()){ //如果是accept ServerSocketChannel channel = (ServerSocketChannel)key.channel(); SocketChannel sc = channel.accept();sc.configureBlocking(false); SelectionKey sckey = sc.register(selector, 0, null); scKey.interestOps(SelectionKey.OP_READ); }elseif(key.isReadable()){ try{ //拿到触发事件的channel ServerSocketChannel channel = (ServerSocketChannel)key.channel(); ByteBuffer buffer = ByteBuffer.allocate(16); int read = channel.read(buffer);//如果是正常断开,read的方法的返回值是-1 if(read == -1){ key.cancel(); }else{ buffer.flip(); debugRead(buffer); } }catch(IOException e){ e.printStackTrace();//因为客户端断开了,因此需要将key取消(从selector 的keys集合中真正删除key) key.cancel();}}}
}
5. 处理消息的边界
- 固定消息长度,数据包大小一样,服务器按预定长度读取,缺点是浪费带宽
- 按分隔符拆分,缺点是效率低
- TLV格式,Type类型,Length长度,Value数据,可以方便获取消息大小,分配合适的buffer,缺点是buffer需要提前分配,如果内容过大,影响server吞吐量
- Http1.1是TLV格式
- Http2.0是LTV格式
private static void split(ByteBuffer source){source.flip();for(int i = 0; i < source.limit(); i++){//找到一条完整消息if(source.get(i) == '\n'){int length = i + 1 -source.position();//把这条完整消息存入新的ByteBufferByteBuffer target = ByteBuffer.allocate(length);//从source读,向target写for(int j = 0; j < length; j++){target.put(source.get());}debugAll(target);}}source.compact();
}public static void main(){//1.创建selector,管理多个channelSelector selector = Selector.open(); ByteBuffer buffer = ByteBuffer.allocate(16); ServerSocketChannel ssc = ServerSocketChannel.open(); ssc.configureBlocking(false);//2.建立selector和channel的联系(注册)SelectionKey sscKey = ssc.register(selector, 0, null); sscKey.interestOps(SelectionKey.OP_ACCEPT); ssc.bind(new InetSocketAddress(8080)); while(true){ //3.select方法,没有事件发生,线程阻塞,有事件,线程才会恢复运行 selector.select(); //4.处理事件,selectedKeys内部包含了所有发生的事件 //selector会在发生事件后,向集合中加入key,但不会删除 Iterator<SelectionKey> iter = selector.selectedKeys.iterator(); while(iter.next()){ SelectionKey key = iter.next(); //处理key时,要从selectedKeys集合中删除,否则下次处理就会有问题 iter.remove(); //5.区分事件类型 if(key.isAcceptable()){ //如果是accept ServerSocketChannel channel = (ServerSocketChannel)key.channel(); SocketChannel sc = channel.accept();sc.configureBlocking(false); ByteBuffer buffer = ByteBuffer.allocate(16); //attachment附件//将一个byteBuffer作为附件关联到selectionKey上SelectionKey sckey = sc.register(selector, 0, buffer); scKey.interestOps(SelectionKey.OP_READ); }elseif(key.isReadable()){ try{ //拿到触发事件的channel ServerSocketChannel channel = (ServerSocketChannel)key.channel(); //获取selectionKey上关联的附件ByteBuffer buffer = (ByteBuffer)key.attatchment();int read = channel.read(buffer);//如果是正常断开,read的方法的返回值是-1 if(read == -1){ key.cancel(); }else{ split(buffer);if(buffer.position() == buffer.limit()){//扩容ByteBuffer newBuffer = ByteBuffer.allocate(buffer.capacity()*2);buffer.flip();newBuffer.put(buffer);//复制key.attach(newbuffer);//替换掉key上原有的buffer}} }catch(IOException e){ e.printStackTrace();//因为客户端断开了,因此需要将key取消(从selector 的keys集合中真正删除key) key.cancel();}}}}
}
6. 写入内容过多的问题
//服务器
public static void main(){ServerSocketChannel ssc = ServerSocketChannrl.open();ssc.configureBlocking(false);Selector selector = Selector.open();ssc.register(selector, SelectionKey.OP_ACCEPT);ssc.bind(new InetSocketAddress(8080));while(trye){selector.select();Iterator<SelectionKey> iter = selector.selectedKeys.iterator();while(iter.hasNext()){SelectionKey key = iter.next();iter.remove();if(key.isAcceptable()){SocketChannel sc = ssc.accept();sc.configureBlocking(false);//1.向客户端发送大量数据StringBuilder sb = new StringBuilder();for(int i = 0; i < 3000000; i++){sb.append("a");}BytrBuffer buffer = Charset.defaultCharset().encode(sb.toString());//不符合非阻塞模式while(buffer.hasRemaining()){//2.返回值代表实际写入的字节数//不能一次性写完//write == 0 缓冲区满,写不了int write = sc.write(buffer);System.out.println(write):}}}}
}//客户端
public static void main(){SocketChannel sc = SocketChannel.open();sc.connect(new InetSocketAddress("localhost",8080));//3.接收数据int count = 0;while(true){ByteBuffer buffer = ByteBuffer.allocate(1024*1024);count += sc.read(buffer);System.out.println(count);buffer.clear();}
}
7. 处理可写事件
//服务器
public static void main(){ServerSocketChannel ssc = ServerSocketChannrl.open();ssc.configureBlocking(false);Selector selector = Selector.open();ssc.register(selector, SelectionKey.OP_ACCEPT);ssc.bind(new InetSocketAddress(8080));while(trye){selector.select();Iterator<SelectionKey> iter = selector.selectedKeys.iterator();while(iter.hasNext()){SelectionKey key = iter.next();iter.remove();if(key.isAcceptable()){SocketChannel sc = ssc.accept();sc.configureBlocking(false);SelectionKey sckey = sc.register(selector, 0, null);sckey.interestOps(SelectionKey.OP_READ);//1.向客户端发送大量数据StringBuilder sb = new StringBuilder();for(int i = 0; i < 3000000; i++){sb.append("a");}BytrBuffer buffer = Charset.defaultCharset().encode(sb.toString());//2.返回值代表实际写入的字节数//不能一次性写完//先写一次int write = sc.write(buffer);System.out.println(write)://3.判断是否有剩余内容while(buffer.hasRemaining()){//4.关注可写事件sckey.interestOps(sckey.interestOps() + SelectionKey.OP_WRITE);//sckey.interestOps(sckey.interestOps() | SelectionKey.OP_WRITE);//5.把未写完的数据挂到sckey上sckey.attach(buffer);}}elseif(key.isWritable())[ByteBuffer buffer = (ByteBuffer) key.attachment();SocketChannel sc = (SocketChannel)key.channel();int write = sc.write(buffer);System.out.println(write)://6.清理操作,内存释放if(!buffer.haeRemaining()){key.attach(null);//需要清除bufferkey.interestOps(key.interestOps() - SelectionKey.OP_WRITE);//不需关注可写事件}}}}
}
相关文章:
Netty-NIO
文章目录 一、NIO-Selector1.处理accept2.cancel3.处理read4.处理客户端断开5. 处理消息的边界6. 写入内容过多的问题7. 处理可写事件 一、NIO-Selector 1.处理accept //1.创建selector,管理多个channel Selector selector Selector.open(); ByteBuffer buffer ByteBuffer.…...
红外物理学习笔记 ——第三章
第三章 基尔霍夫定律:就是说物体热平衡条件下,发射的辐射功率要等于吸收的辐射功率 M α E M\alpha E MαE α \alpha α 是吸收率, M M M 是幅出度(发射出去的), E E E是辐照度(外面照过来的…...
使用 htmx 构建交互式 Web 应用
学习目标:了解htmx的基本概念、特点和用法,并能够运用htmx来创建交互式的Web应用程序。 学习内容: 1. 什么是htmx? - htmx是一种用于构建交互式Web应用程序的JavaScript库。 - 它通过将HTML扩展为一种声明性的交互式语言&a…...
S32K324芯片学习笔记
文章目录 Core and architectureDMASystem and power managementMemory and memory interfacesClocksSecurity and integrity安全与完整性Safety ISO26262Analog、Timers功能框图内存mapflash Signal MultiplexingPort和MSCR寄存器的mapping Core and architecture 两个Arm Co…...
htmx-使HTML更强大
本文作者是360奇舞团开发工程师 htmx 让我们先来看一段俳句: javascript fatigue: longing for a hypertext already in hand 这个俳句很有意思,是开源项目htmx文档中写的,意思是说,我们已经有了超文本,为什么还要去使用javascr…...
Java学习之序列化
1、引言 《手册》第 9 页 “OOP 规约” 部分有一段关于序列化的约定 1: 【强制】当序列化类新增属性时,请不要修改 serialVersionUID 字段,以避免反序列失败;如果完全不兼容升级,避免反序列化混乱,那么请…...
C++实现蜂群涌现效果(flocking)
Flocking算法0704_元宇宙中的程序员的博客-CSDN博客 每个个体的位置,通过计算与周围个体的速度、角度、位置,去更新位置。...
IDEA复制一个工程为多个并启动,测试负载均衡
1 找到服务按钮 2 选择复制配置 3 更改新的名称与虚拟机参数 复制下面的代码在VM参数中 -Dserver.port8082 4 最后启动即可...
001_C++语法基础
C语法基础 所有C语法要用英文区分大小写每个语句写完以分号结束 C标准输入输出头文件iostream 若想通过C实现数据的输入和输出,需要导入标准输入输出头文件 #include <iostream>标准输入输出头文件<iostream>中包含了cin输入语句和cout输出语句 标…...
对Excel表中归类的文件夹进行自动分类
首先把excel表另存为.txt文件(注意:刚开始可能是ANSI格式,需要转成UTF-8格式);再新建一个.txt文件,重命名成.bat文件(注意:直接创建的如果是是UTF-8格式,最好转成ANSI格式࿰…...
LabVIEW液压支架控制系统的使用与各种配置的预测模型的比较分析
LabVIEW液压支架控制系统的使用与各种配置的预测模型的比较分析 模型预测控制在工业中应用广泛。这种方法的优点之一是在求解最优控制问题时能够明确考虑对输入和输出状态施加的约束。控制对象模型用于有限时间范围内最优控制的实时计算。所使用的数学设备允许从具有单输入和单…...
C++中位运算符使用
& 与 只有都为1结果为1 0 & 0 00 & 1 01 & 0 01 & 1 1 | 或 只要一个为1结果为1 0|00 0|11 1|01 1|11 ^ 异或 两个相同的数字为0,其余为1 0^00 1^01 0^11 1^10 ~ 取反 将进制位数进行取反 ~1-2 //0000 0001-->代…...
微机原理 || 第2次测试:汇编指令(加减乘除运算,XOR,PUSH,POP,寻址方式,物理地址公式,状态标志位)(测试题+手写解析)
(一)测试题目: 1.数[X]补1111,1110B,则其真值为 2.在I/O指令中,可用于表示端口地址的寄存器 3. MOV AX,[BXSl]的指令中,源操作数的物理地址应该如何计算 4.执行以下两条指令后,标志寄存器FLAGS的六个状态…...
人员闯入检测告警算法
人员闯入检测告警算法通过yolov5网络模型识别检测算法,人员闯入检测告警算法对未经许可或非法进入的人员进行及时识别告警,确保对危险区域的安全管理和保护。YOLO系列算法是一类典型的one-stage目标检测算法,其利用anchor box将分类与目标定位…...
python中super()用法
super关键字的用法 一、概述二、作用三、语法四、使用示例1.通过super() 来调用父类的__init__ 构造方法:2.通过supper() 来调用与子类同名的父类方法2.1 单继承2.2 多继承 一、概述 super() 是python 中调用父类(超类)的一种方法࿰…...
jmeter While控制器
一种常见的循环控制语句,用于重复执行一段代码块,直到指定的条件不再满足。 参数: 空LASTJMeter变量、函数、属性或任意其他可用表达式 (jmeter提供的方法)。判断变量值count_num小于等于20,推荐简单的几…...
3D数字孪生技术助力港口全新升级,提供实时数据进行智能调度
港口3D数字孪生平台是一种基于数字技术的虚拟模型,它可以模拟真实的港口环境,并对港口的运营、管理、安全等方面进行实时监控和优化。该平台带来了许多智能化提升,包括以下几个方面: 一、自动化操作和智能调度 数字孪生平台可以通…...
Qt日历控件示例-QCalendarWidget
基本说明 QCalendarWidget介绍: QCalendarWidget 是 Qt 框架中提供的一个日期选择控件,用户可以通过该控件快速选择需要的日期,并且支持显示当前月份的日历。 这里,我们继承了QCalendarWidget,做了一些简单封装和样式调整 1.使用的IDE&…...
函数式编程(四)Stream流使用
一、概述 在使用stream之前,先理解Optional 。 Optional是Java 8引入的一个容器类,用于处理可能为空的值。它提供了一种优雅的方式来处理可能存在或不存在的值,避免了空指针异常。 Optional的主要特点如下: 可能为空ÿ…...
区块链面临六大安全问题 安全测试方案研究迫在眉睫
区块链面临六大安全问题 安全测试方案研究迫在眉睫 近年来,区块链技术逐渐成为热门话题,其应用前景受到各国政府、科研机构和企业公司的高度重视与广泛关注。随着技术的发展,区块链应用与项目层出不穷,但其安全问题不容忽视。近年…...
MusePublic Art Studio惊艳效果:动态种子演化生成同主题12张连贯艺术组图
MusePublic Art Studio惊艳效果:动态种子演化生成同主题12张连贯艺术组图 1. 引言:当AI成为艺术家的画笔 想象一下,你是一位艺术家,脑海中有一个绝妙的创意主题。你想围绕这个主题创作一个系列作品,比如“赛博朋克森…...
绝地求生罗技鼠标宏配置全攻略:从零到精通的压枪优化指南
绝地求生罗技鼠标宏配置全攻略:从零到精通的压枪优化指南 【免费下载链接】logitech-pubg PUBG no recoil script for Logitech gaming mouse / 绝地求生 罗技 鼠标宏 项目地址: https://gitcode.com/gh_mirrors/lo/logitech-pubg 还在为《绝地求生》中的枪口…...
告别吃灰!用Kindle打造唐诗宋词字帖屏保的完整避坑指南(含繁简转换技巧)
用Kindle打造唐诗宋词字帖屏保的完整指南 作为一个长期关注数字阅读与传统文化的深度用户,我发现Kindle的墨水屏特性非常适合展示书法字帖。这种将现代科技与传统艺术结合的方式,不仅能提升设备使用率,还能在日常碎片时间中培养书写习惯。本文…...
dbg-macro配置完全指南:禁用宏、强制颜色输出与警告控制
dbg-macro配置完全指南:禁用宏、强制颜色输出与警告控制 【免费下载链接】dbg-macro A dbg(…) macro for C 项目地址: https://gitcode.com/gh_mirrors/db/dbg-macro dbg-macro是一款轻量级C调试宏工具,通过简单的dbg(...)语法即可实现变量值、类…...
家庭实验室:树莓派控制OpenClaw调用远程Qwen3-32B
家庭实验室:树莓派控制OpenClaw调用远程Qwen3-32B 1. 为什么选择树莓派OpenClaw组合 去年冬天,我在整理家庭实验室设备时发现一个闲置的树莓派4B。这台信用卡大小的电脑曾经用来跑Home Assistant控制智能家居,但后来换了NUC主机就被束之高阁…...
把 SAP Fiori 后端授权模型讲透:从 PFCG、Catalog 到 SU24 的一条完整链路
很多团队在上线 SAP Fiori 应用时,会把注意力集中在前端目录、磁贴和页面配置上,结果到了联调或上线阶段才发现:用户明明能看到应用入口,点击之后却报错;或者应用能打开,但列表为空;再或者少数用户能看到不该看的业务数据。问题往往不在 UI 本身,而在后端授权模型没有真…...
百度快速排名优化技术(百度seo排名优化)
百度快速排名优化技术是一种针对搜索引擎结果页面(SERP)排名优化的技术手段,通过优化网站的内容、结构和用户体验等方面,提高网站在搜索引擎中的排名,从而获得更多的流量和潜在客户。下面,我将介绍百度快速…...
OpenClaw密码管理:nanobot安全存储与自动填充方案
OpenClaw密码管理:nanobot安全存储与自动填充方案 1. 为什么需要本地化的密码管理方案 去年的一次数据泄露事件让我彻底放弃了所有云端密码管理器。当时我使用的某知名商业工具突然弹出安全警报,提示"您的部分密码可能已被未授权访问"。虽然…...
SVPWM/AZSPWM的simulink仿真 AZSPWM(Advanced Zero Se...
SVPWM/AZSPWM的simulink仿真 AZSPWM(Advanced Zero Sequence Pulse Width Modulation,先进零序脉宽调制)是一种改进的脉宽调制技术,主要应用于三相逆变器中,通过引入零序分量来优化输出电压的波形和性能。 AZSPWM的目标…...
告别标注烦恼:用DINOv2自监督模型,在Intel Image数据集上3个epoch实现93%准确率
零标注成本实战:DINOv2自监督模型在Intel Image数据集上的高效迁移方案 当我在实验室第一次尝试用传统方法训练一个图像分类模型时,面对数千张需要手动标注的图片,几乎要放弃这个课题。直到发现了自监督学习这个宝藏领域——特别是DINOv2这样…...
