Netty基础入门——NIO【1】

news2024/11/27 10:32:08

Netty基础入门——NIO【1】

1 NIO

1.1 三大组件

1.1.1 Channel && Buffer

  1. Channle

channel类似于stream,是读写数据的双向通道,而stream要么是输入要么是输出

channel
buffer

常见channel:

* FileChannel
* DatagramChannel
* SocketChannel
* ServerSocketChannel
  1. Buffer

用来缓冲读写数据

常见Buffer:

* ByteBuffer
* ShortBuffer
* IntBuffer
* LongBuffer
* FloatBuffer
* DoubleBuffer
* CharBuffer

1.1.2 Selector

服务器设计演变过程:
①多线程版设计

多线程版
socket1
thread
socket2
thread
socket3
thread
  • 内存占用高
  • 线程上下文切换成本高
  • 只适合连接数少的场景

②线程池版

线程池版
socket1
thread
socket2
thread
socket3
socket4
  • 阻塞模式下,线程仅能处理一个 socket 连接
  • 仅适合短连接场景
    ③selector版

selector 的作用就是配合一个线程来管理多个 channel,获取这些 channel 上发生的事件,这些 channel 工作在非阻塞模式下,不会让线程吊死在一个 channel 上。适合连接数特别多,但流量低的场景(low traffic)

selector 版
selector
thread
channel
channel
channel

调用 selector 的 select() 会阻塞直到 channel 发生了读写就绪事件,这些事件发生,select 方法就会返回这些事件交给 thread 来处理

1.2 ByteBuffer

使用FileChannel来读取文件内容

@Slf4j
public class ChannelDemo1 {
    public static void main(String[] args) {
    	//twr写法
        try (RandomAccessFile file = new RandomAccessFile("data.txt", "rw")) {
            FileChannel channel = file.getChannel();
            ByteBuffer buffer = ByteBuffer.allocate(10);//缓冲区大小
            do{
                //channel读,向buffer写入
                int len = channel.read(buffer);;
                log.debug("读到字节数:{}", len);
                if(len == -1){
                    break;
                }
                //切换buffer读模式
                buffer.flip();
                while(buffer.hasRemaining()){
                    log.debug("{}", (char)buffer.get());
                }
                //切换buffer写模式
                buffer.clear();
            }while (true);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

1.2.1 ByteBuffer使用步骤

  1. 向buffer写入数据,例如:channel.read(buffer)

文件获取channel - 通过channel向buffer写入

  1. 调用flip()切换至读模式
  2. 从buffer读取数据,例如:buffer.get()
  3. 调用clear()或compact()切换至写模式
  4. 重复步骤1-4

1.2.2 ByteBuffer结构

  • capacity
  • position
  • limit
  1. 初始状态
    在这里插入图片描述
  2. 写入4个字节后

写模式下,position 是写入位置,limit 等于容量

在这里插入图片描述

  1. flip动作发生,切换读写

flip 动作发生后,position 切换为读取位置,limit 切换为读取限制

在这里插入图片描述

  1. clear动作发生后
    在这里插入图片描述
  2. compact 方法,是把未读完的部分向前压缩,然后切换至写模式
    在这里插入图片描述

1.2.3 ByteBuffer常见方法

  1. allocate为ByteBuffer分配空间,其他buffer类也有该方法
Bytebuffer buf = ByteBuffer.allocate(16);
  1. 向buffer写数据
  • channel的read【从channel读取到然后向buffer写】
int readBytes = channel.read(buf);
  • buffer自己的put方法
buf.put((byte)127);
  1. 从buffer读取数据
  • 调用 channel 的 write 方法【从buf读,向channel写】
int writeBytes = channel.write(buf);
  • 调用 buffer 自己的 get 方法
byte b = buf.get();

get 方法会让 position 读指针向后走,如果想重复读取数据

* 可以调用 rewind 方法将 position 重新置为 0
* 或者调用 get(int i) 方法获取索引 i 的内容,它不会移动读指针

1.2.4 调试工具类

1.2.5 mark、字符与ByteBuffer互转、批量读写

  • mark 和 reset

mark 是在读取时,做一个标记,即使 position 改变,只要调用 reset 就能回到 mark 的位置

注意

rewind 和 flip 都会清除 mark 位置

  • 字符与ByteBuffer互转

字符转ByteBuffer

ByteBuffer buffer1 = StandardCharsets.UTF_8.encode("你好");
ByteBuffer buffer2 = Charset.forName("utf-8").encode("你好");

ByteBuffer转字符

ByteBuffer byteBuffer = StandardCharsets.UTF_8.encode("hello");
String s = StandardCharsets.UTF_8.decode(byteBuffer).toString();
//hello
System.out.println(s);
  • 批量操作byteBuffer

少搬运一次,速度更快

1. Scattering Reads(分散读取)
    public static void main(String[] args) {
        //test.txt文件内容:onetwothree
        try (FileChannel channel = new RandomAccessFile("test.txt", "rw").getChannel()) {
            //向ByteBuffer中批量写入
            ByteBuffer a = ByteBuffer.allocate(3);
            ByteBuffer b = ByteBuffer.allocate(3);
            ByteBuffer c = ByteBuffer.allocate(5);
            channel.read(new ByteBuffer[]{a,b,c});
            a.flip();
            b.flip();
            c.flip();
        } catch (IOException e) {
        }
    }


2.Gathering Writes(聚集写入)
   try (FileChannel channel = new RandomAccessFile("test.txt", "rw").getChannel()) {
         ByteBuffer buffer = ByteBuffer.allocate(5);
         ByteBuffer buffer2 = ByteBuffer.allocate(6);
         buffer.put("hello".getBytes(StandardCharsets.UTF_8));
         buffer2.put("你好".getBytes(StandardCharsets.UTF_8));
         //切换为读模式
         buffer.flip();
         buffer2.flip();
         channel.write(new ByteBuffer[]{buffer, buffer2});
     } catch (IOException e) {
     }

1.3 生产案例【粘包半包问题解决】

网络上有多条数据发送给服务端,数据之间使用 \n 进行分隔
但由于某种原因这些数据在接收时,被进行了重新组合,例如原始数据有3条为

  • Hello,world\n
  • I’m zhangsan\n
  • How are you?\n

变成了下面的两个 byteBuffer (黏包,半包)

  • Hello,world\nI’m zhangsan\nHo
  • w are you?\n

现在要求你编写程序,将错乱的数据恢复成原始的按 \n 分隔的数据

public static void main(String[] args) {
        /*原本数据:
         * Hello,world\n
         * I'm zhangsan\n
         * How are you?\n
         *
         * 特殊原因出现:粘包、半包问题
         * Hello,world\nI'm zhangsan\nHo
         * w are you?\n
         */
        ByteBuffer source = ByteBuffer.allocate(32);
        source.put("Hello,world\nI'm zhangsan\nHo".getBytes());
        split(source);

        source.put("w are you?\nhaha!\n".getBytes());
        split(source);
    }

    //处理粘包、半包问题
    public static void split(ByteBuffer source){
        //切换为读模式
        source.flip();
        //原来的长度:Hello,world\nI'm zhangsan\nHo【粘包、半包后一条数据的长度】
        int oldLimit = source.limit();
        //limit:byteBuffer最大范围
        for(int i = 0; i < oldLimit; i++){
            if(source.get(i) == '\n'){
                //定义新ByteBuffer,内存利用率最大化【粘包、半包前真实数据长度】
                ByteBuffer tar = ByteBuffer.allocate(i + 1 - source.position());
                // 0 ~ limit【假如i为1,此时有两个数据要读,因此最大限制为2 -> i+1】
                source.limit(i + 1);
                tar.put(source);//从source读,向tar写
                ByteBufferUtil.debugAll(tar);
                //还原长度,继续向下读
                source.limit(oldLimit);
            }
        }
        //没有读完的数据,压缩
        source.compact();
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/143180.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

YACC移进规约冲突案例分析

总结 总结&#xff1a; bison给出的用例是发现冲突的最便捷方法。 第一种用例&#xff1a;明确用例&#xff08;一个Example&#xff09;&#xff0c;直接反应问题。第二种用例&#xff1a;混淆用例&#xff08;两个Example&#xff09;&#xff0c;解析器无法区分两条语句。…

jenkins 节点部署

1、节点注册 登陆jenkins master界面 路径&#xff1a;首页-->系统管理--> 节点管理-->新建节点&#xff08;New Node&#xff09; 插曲&#xff1a;我在新的服务器部署master节点&#xff0c;显示剩余交换空间为0B 处理方式请查看&#xff1a;Jenkins - Free Swap…

关于MCU的BootLoader的一些理解

一、关于STM32单片机IAP升级中if(((*(__IO uint32_t*)Addr_App) & 0x2FFE0000) 0x20000000)语句的理解 参考自&#xff1a;https://blog.csdn.net/weixin_45394120/article/details/122732203?spm1001.2014.3001.5502 疑问&#xff1a; 1、为什么要用Addr_App里的数据…

Web操作系统漏洞发现——工具使用总结

目录 &#xff08;一&#xff09;web层面 1、信息收集 0x01 网站源码自己开发 0x02 网站源码使用开源CMS 2、可维护Poc 0x01 pocassist 0x02 afrog 3、APP渗透 0x01 在BP上添加转发端口 0x02 Xray进行监听 0x03 触发数据 4、Goby &#xff08;二&#xff09;操作系统层…

xss.haozi靶场通关

做完xss-labs靶场后&#xff0c;再继续做这个靶场&#xff0c;感觉这个不是很难&#xff0c;毕竟在第一个靶场也获取了一些经验&#xff0c;但是这个靶场偏向技巧&#xff0c;所以还是以了解为主。 0x00: 分析&#xff1a;对我们的代码未作出限制&#xff0c;因此这里可以使用…

如何用 nodejs 进行 sha1 加密验证,微信公众号开发验证

如何用 nodejs 进行 sha1 加密验证&#xff0c;微信公众号开发验证 一、问题 今天在摆弄微信公众号的时候&#xff0c;遇到这样一个问题&#xff1a; 我的后台是 nodejs 写的&#xff0c;express 框架&#xff0c;官方开发接入的验证代码是 php 写的&#xff0c;其中有一个部…

C语言之蓝桥杯习题(3)☞暴力求解版(思路写在解题过程中)

第一题.1.问题&#xff1a;小蓝数字卡片题小蓝有很多数字卡片&#xff0c;每张卡片上都是数字0到9。 小蓝准备用这些卡片来拼一些数&#xff0c;他想从1开始拼现在小蓝手里有0到9的卡片各2021张&#xff0c;共20210张&#xff0c;请问小蓝可以从1拼到多少?2.解题过程&#xff…

【Docker】(三)使用registry远程镜像仓库管理镜像

1.前言 本系列文章记录了从0开始学习Docker的过程&#xff0c;Docker系列历史文章&#xff1a; &#xff08;一&#xff09;基本概念与安装使用 &#xff08;二&#xff09;如何使用Docker发布一个SpringBoot服务 在上一篇中留下了一个问题&#xff0c;使用Docker发布服务的方…

【开源代码 | MATLAB线性阵列仿真】

本文编辑&#xff1a;调皮哥的小助理 1、16阵元均匀线阵方向图 %8阵元均匀线阵方向图&#xff0c;来波方向为0度 clc; clear all; close all; element_num16;%阵元数为16 d_lamda1/2;%阵元间距d与波长lamda的关系 thetalinspace(-pi/2,pi/2,200); theta0[0.2 0.1];%来波方向 w…

systemd wsl 测试笔记

文章目录systemd 简介WSL systemdsystemctljournalctlhello serviceSleep 与 Timeout 测试Requires 测试After 测试systemd 简介 Linux 从关闭到运行, 完整的启动和启动过程有三个主要部分: 硬件启动(Hardware boot): 初始化系统硬件Linux 引导(Linux boot): 加载 Linux 内核&…

基于ERNIELayoutPDFplumber-UIEX的多方案学术论文信息抽取

本项目链接&#xff1a;https://aistudio.baidu.com/aistudio/projectdetail/5196032?contributionType1 0.问题描述 可以参考issue&#xff1a; ERNIE-Layout在&#xff08;人名和邮箱&#xff09;信息抽取的诸多问题阐述#4031 ERNIE-Layout因为看到功能比较强大就尝试了一…

Linux安装mongodb集群整合SpringBoot

一、Mongodb集群安装 本文介绍基于mongodb的副本机制搭建集群 192.168.139.186CentOS Linux release 7.7.1908 (Core)192.168.139.187CentOS Linux release 7.7.1908 (Core)192.168.139.188CentOS Linux release 7.7.1908 (Core) 准备工作 关闭selinux&#xff0c;关闭防火墙…

近场通信到2027年将达到467.81亿美元

2020年&#xff0c;全球近场通信市场规模为178.75亿美元&#xff0c;预计到2027年将达到467.81亿美元&#xff0c;2021年至2027年的CAGR为14.8%。这是根据Market Statsville Group (MSG)的一份新报告得出的。 近场通信(NFC)是基于无线接口的一系列协议&#xff0c;使得通信设备…

Linux系统的优缺点

相比 Windows 系统&#xff0c;Linux 系统有更好的稳定性&#xff0c;那么除此之外&#xff0c;Linux 系统还有那些优点&#xff08;或者不足&#xff09;呢&#xff1f;一、大量的可用软件及免费软件Linux 系统上有着大量的可用软件&#xff0c;且绝大多数是免费的&#xff0c…

2021年第十二届蓝桥杯软件类省赛python组“回路计算“问题

说明 这一题我不会做&#xff0c;看了官方给出的标准答案之后才明白&#xff0c;我把我学到的思路写下来。 题目 蓝桥学院由21栋教学楼组成&#xff0c;教学楼编号1到21。对于两栋教学楼a和b,当a和b互质时&#xff0c;a和b之间有一条走廊直接相连&#xff0c;两个方向皆可通…

CRM之线索管理的demo搭建方法

1、简介 1.1、案例简介 本文将介绍&#xff0c;如何搭建CRM-线索管理。 1.2、应用场景 CRM-线索管理应用完整记录所有线索资料&#xff0c;合理的对线索进行领取、分配、退回、跟进&#xff0c;实现线索管理智能化。 2、设置方法 2.1、表单搭建 1&#xff09;新建主表【新…

【日常系列】LeetCode《25·贪心2》

数据规模->时间复杂度 <10^4 &#x1f62e;(n^2) <10^7:o(nlogn) <10^8:o(n) 10^8<:o(logn),o(1) 内容 lc 976 &#xff1a;三角形的最大周长 https://leetcode.cn/problems/largest-perimeter-triangle/ 提示&#xff1a; 3 < nums.length < 10^4 1 &l…

Dubbo优雅启动(附源码分析)

Dubbo优雅启动 1. 启动有什么问题 我们知道&#xff0c;应用在运行了一段时间后&#xff0c;执行速度会比刚启动的时候要快。这是因为在 Java 里面&#xff0c;在运行过程中&#xff0c;JVM 虚拟机会把高频的代码编译成机器码&#xff0c;被加载过的类也会被缓存到 JVM 缓存中…

主数据和元数据、数据标准、数据质量有什么关系

企业数据治理涉及的工作很广&#xff0c;包括数据标准、数据质量、数据安全、数据共享机制、元数据管理、主数据管理等。主数据作为企业的黄金数据&#xff0c;对于企业信息化管理具有重要意义。本文将对主数据的概念及主数据与数据治理体系中的几个核心部分的关系和大家做一个…

oracle 查询到的结果在快捷地写入到excel过程中标题部分正确的处理方式

点击上方“Python爬虫与数据挖掘”&#xff0c;进行关注回复“书籍”即可获赠Python从入门到进阶共10本电子书今日鸡汤羌笛何须怨杨柳&#xff0c;春风不度玉门关。大家好&#xff0c;我是皮皮。一、前言前几天在Python最强王者交流群【粉丝】问了一个pandas数据处理的问题&…