Netty
NIO
non-blocking io —— 非阻塞IO
NIO 三大组件
- channel
| 1 | // FileChannel | 
- buffer 
- selector 
ByteBuffer
- ByteBuffer 使用 - 向buffer写入数据,例如调用 channel.read(buffer)
- 调用flip()切换至读模式
- 从buffer读取数据,例如调用buffer.get()
- 调用clear()或compact()切换至写模式
- 重复1~4步骤
 
- ByteBuffer 结构 - capacity 容量
- position 起始位置
- limit 限制大小
 
- ByteBuffer 方法 - 分配空间 - allocate() -java 堆内存,读写效率较低,受GC影响 
- allocateDirect() -直接内存,读写效率高(少一次拷贝),不受GC影响,分配效率低 
 
- 向buffer写入数据 - 调用channel.read() 
- 调用buffer.put() 
 
- 从buffer读取数据 - 调用channel.write() 
- 调用buffer.get() - get()会让position指针后移,重复读取:调用rewind()重置position 为0或者get(i)获取索引i的内容
- get(i)不会改变position位置
- mark & reset:mark记录position位置,reset回到mark记录的位置
 
 
- 字符串转ByteBuffer 
 - 1 
 2
 3
 4
 5
 6
 7- // 1.字符串str转为 ByteBuffer 
 ByteBuffer buffer1 = ByteBuffer.allocate(16);
 buffer1.put(str.getBytes());
 // 2.Charset
 ByteBuffer buffer2 = StandardCharsets.UTF_8.encode(str);
 // 3.wrap 字节数组跟ByteBuffer转换
 ByteBuffer buffer3 = ByteBuffer.wrap(str.getBytes());- ByteBuffer转字符串
 - 1 
 2
 3
 4
 5- // ByteBuffer转为字符串 
 buffer1.flip();
 str = StandardCharsets.UTF_8.decode(buffer1).toString();
 // Charset类型:会自动重置position,不用切换读模式
 str = StandardCharsets.UTF_8.decode(buffer2).toString();
文件编程
FileChannel
FileChannel只能工作在阻塞模式下
- 获取
不能直接打开FileChannel,必须通过FilelnputStream、FileOutputStream或者RandomAccessFile来获取FileChannel,它们都有getChannel方法
- 通过FilelnputStream获取的channel只能读
- 通过FileOutputStream获取的channel只能写
- 通过RandomAccessFile是否能读写根据构造RandomAccessFile时的读写模式**(r / w)**决定
- 读取
会从channel读取数据填充ByteBuffer,返回值表示读到了多少字节,-1表示到达了文件的末尾
| 1 | int len = channel.read(buffer); | 
- 写入
写入的正确姿势如下,SocketChannel
| 1 | ByteBuffer buffer =... | 
在while中调用channel.write是因为write方法并不能保证一次将buffer中的内容全部写入channel
- 关闭
channel必须关闭,不过调用了FilelnputStream、FileOutputStream或者RandomAccessFile的close方法会间接地调用channel的close方法
- 位置
获取当前位置
| 1 | long pos = channel.position(); | 
设置当前位置
| 1 | long newPos = ...; | 
设置当前位置时,如果设置为文件的末尾
- 这时读取会返回-1
- 这时写入,会追加内容,但要注意如果p0sit0超过了文件末尾,再写入时在新内容和原末尾之间会有空洞(00)
- 大小
使用size方法获取文件的大小
- 强制写入
操作系统出于性能的考虑,会将数据缓存,不是立刻写入磁盘。可以调用force(true)方法将文件内容和元数据(文件的权限等信息)立刻写入磁盘
两个Channel传输数据
transferTo()
Path
Files
- 拷贝文件 (跟transferTo一样效率较高)
| 1 | Files.copy(source,target); | 
walkFileTree();
网络编程
阻塞模式 vs 非阻塞模式
阻塞方法,线程停止运行
- 阻塞
- 非阻塞
- 多路复用
单线程可以配合Selector完成对多个Channel可读写事件的监控,这称之为多路复用
- 多路复用仅针对网络IO、普通文件IO没法利用多路复用 
- 如果不用Selector的非阻塞模式,线程大部分时间都在做无用功,而Selector能够保证: - 有可连接事件时才去连接 - 有可读事件才去读取 - 有可写事件才去写入 
限于网络传输能力,Channel未必时时可写,一旦Channel可写,会触发Selector的可写事件
Selector
- accept -会在有连接请求时触发 
- connect -是客户端,连接建立后触发 
- read -可读事件 
- write -可写事件 
ServerSocketChannel是用来连接的,SocketChannel是用来处理客户端发的消息的
select():没有事件发生,阻塞,有事件,线程才会恢复运行。在事件未处理时,它不会阻塞。事件发生后要么处理要么取消。selectionKey .cancel()取消
Selector 最大的好处是可以通过阻塞方法select()拿到SelectionKey 判断时间类型从而做出不同的逻辑处理
