一分钟搞定Netty 三大组件,如果搞不定,再看3遍
1. 三大组件简介Channel 与 Buffer
Java NIO 系统的核心在于:通道 (Channel) 和缓冲区 (Buffer)。通道表示打开到 IO 设备 (例如:文件、套接字) 的连接。若需要使用 NIO 系统,需要获取用于连接 IO 设备的通道 以及用于容纳数据的缓冲区。然后操作缓冲区,对数据进行处理
简而言之,通道负责传输,缓冲区负责存储
常见的 Channel 有以下四种,其中 FileChannel 主要用于文件传输,其余三种用于网络通信
[*]FileChannel
[*]DatagramChannel
[*]SocketChannel
[*]ServerSocketChannel
Buffer 有以下几种,其中使用较多的是 ByteBuffer
[*]ByteBuffer
[*]MappedByteBuffer
[*]DirectByteBuffer
[*]HeapByteBuffer
[*]ShortBuffer
[*]IntBuffer
[*]LongBuffer
[*]FloatBuffer
[*]DoubleBuffer
[*]CharBuffer
https://img2023.cnblogs.com/other/2893049/202212/2893049-20221228150119648-488317266.png
1、Selector
在使用 Selector 之前,处理 socket 连接还有以下两种方法
使用多线程技术
为每个连接分别开辟一个线程,分别去处理对应的 socket 连接
https://img2023.cnblogs.com/other/2893049/202212/2893049-20221228150119875-621166407.png
这种方法存在以下几个问题
[*]内存占用高
[*]每个线程都需要占用一定的内存,当连接较多时,会开辟大量线程,导致占用大量内存
[*]线程上下文切换成本高
[*]只适合连接数少的场景
[*]连接数过多,会导致创建很多线程,从而出现问题
使用线程池技术
使用线程池,让线程池中的线程去处理连接
https://img2023.cnblogs.com/other/2893049/202212/2893049-20221228150120090-374076516.png
这种方法存在以下几个问题
[*]阻塞模式下,线程仅能处理一个连接
[*]线程池中的线程获取任务(task)后,只有当其执行完任务之后(断开连接后),才会去获取并执行下一个任务
[*]若 socke 连接一直未断开,则其对应的线程无法处理其他 socke 连接
[*]仅适合短连接场景
[*]短连接即建立连接发送请求并响应后就立即断开,使得线程池中的线程可以快速处理其他连接
使用选择器
selector 的作用就是配合一个线程来管理多个 channel(fileChannel 因为是阻塞式的,所以无法使用 selector),,获取这些 channel 上发生的事件,这些 channel 工作在非阻塞模式下,当一个 channel 中没有执行任务时,可以去执行其他channel 中的任务。适合连接数多,但流量较少的场景
https://img2023.cnblogs.com/other/2893049/202212/2893049-20221228150120300-912283552.png
若事件未就绪,调用 selector 的 select () 方法会阻塞线程,直到 channel 发生了就绪事件。这些事件就绪后,select 方法就会返回这些事件交给 thread 来处理
2、ByteBuffer
使用案例
使用方式
[*]向 buffer 写入数据,例如调用 channel.read (buffer)
[*]调用 flip () 切换至
读模式
[*]flip 会使得 buffer 中的 limit 变为 position,position 变为 0
[*]从 buffer 读取数据,例如调用 buffer.get ()
[*]调用 clear () 或者 compact () 切换至
写模式
[*]调用 clear () 方法时 position=0,limit 变为 capacity
[*]调用 compact () 方法时,会将缓冲区中的未读数据压缩到缓冲区前面
[*]重复以上步骤
使用 ByteBuffer 读取文件中的内容
public class TestByteBuffer {
public static void main(String[] args) {
try (FileChannel channel = new FileInputStream("stu.txt").getChannel()){
//给缓冲区 分配空间
ByteBuffer buffer = ByteBuffer.allocate(10);
int read = 0 ;
StringBuilder builder = new StringBuilder();
while ((read =channel.read(buffer))>0){
//切换成 读模式 limit = position; position=0
buffer.flip();
while (buffer.hasRemaining()){
builder.append((char)buffer.get());
}
//清空字节数组 切换成 写模式 position=0 ;limit = capacity
buffer.clear();
}
System.out.println(builder.toString());
} catch (Exception e) {
e.printStackTrace();
} finally {
}
}
}打印结果:
0123456789abcdef核心属性
字节缓冲区的父类 Buffer 中有几个核心属性,如下
// Invariants: mark > 4]; private static final String[] BYTE2HEX = new String; private static final String[] BYTEPADDING = new String; static { final char[] DIGITS = "0123456789abcdef".toCharArray(); for (int i = 0; i < 256; i++) { HEXDUMP_TABLE; HEXDUMP_TABLE[(i > 4; final int remainder = length & 0xF; // Dump the rows which have 16 bytes. for (int row = 0; row < fullRows; row++) { int rowStartIndex = (row
页:
[1]