黑马程序员Netty全套教程,全网最全Netty深入浅出教程,Java网络编程的王者_哔哩哔哩_bilibili

三大组件

Channel & Buffer

  • FileChannel
  • DatagramChannel
  • SocketChannel
  • ServerSocketChannel

ByteBuffer

  • MappedByteBuffer
  • DirectByteBuffer
  • HeapByteBuffer

    ServerSocketChannel & SocketChannel

    image.png

    selector

    image.png

    ByteBuffer

    image.png

  • flip()切换读模式

  • clear()切换写模式

ByteBuffer的指针

  • position:从哪开始
  • limit:到哪结束
  • capacity:总大小

    分配空间:allocate & allocateDirect

  • 一个HeapByteBuffer

  • 一个DirectByteBuffer

读取

  • channel.read(buffer)
  • buffer.get()
    • get(i):不改变指针
  • rewind():position=0
  • mark()/reset()

    半包读取

    image.png

    转换字符串

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

    FileChannel

  • FileChannel 只能工作在阻塞模式下

    获取

    image.png

    写入【必须循环】

    ByteBuffer buffer = …; buffer.put(…); // 存入数据 buffer.flip(); // 切换读模式

    while(buffer.hasRemaining()) {

    1. channel.write(buffer);**//向channel(socketChannel)写,不能保证一次写完。**

    }

transferTo()

  • 零拷贝,效率高
  • 一次最多2g数据

image.png

walkfiletree遍历文件夹

image.png

walkfiletree删除多级目录

image.png

walk拷贝多级目录

image.png

nio

阻塞模式:accept & read阻塞

image.png
每次必须新建一个链接accept才能往下执行,才能执行read,read所有的channel。
可以新建一个thread来处理read请求。

非阻塞模式

  • ServerSocketChannel设置为非阻塞:accept不再阻塞
    • 没有连接返回null
  • SocketChannel设置为非阻塞:read非阻塞
    • 没有读到数据返回0

image.png
单线程处理多个客户端连接,所以引出selector

SocketChannel & ServerSocketCHannel

image.png

  • 服务器必须先建立ServerSocket或者ServerSocketChannel 来等待客户端的连接
  • 客户端必须建立相对应的Socket或者SocketChannel来与服务器建立连接
  • 服务器接受到客户端的连接受,再生成一个Socket或者SocketChannel与此客户端通信

    selector

    多路复用:单线程使用selector对多个channel进行事件监控
    channel注册到selector:

  • SelectionKey sscKey = ssc.register(selector, 0, null);

设置SelectionKey关注的事件

  • sscKey.interestOps(SelectionKey.OP_ACCEPT);
    • accept:有连接请求
    • connect:客户端连接建立
    • read:可读
    • write:可写

      Selector工作流程

  1. ServerSocketChannel注册到selector

ssc.register(selector, SelectionKey.OP_ACCEPT);

  1. 检查是否有事件

select = selector.select(timeout)

  1. 从selector获取就绪key

Iterator iter = selector.selectedKeys().iterator();

  1. 判断连接事件

key.isAcceptable()

  1. 从key中获取channel

SocketChannel sc = (SocketChannel) key.channel();

  1. 处理channel

SocketChannel sc = serverSocketChannel.accept() 处理连接请求
int write = SocketChannel.write(buffer); 处理写请求

监听channel:

  • selector.select():一直阻塞,直到事件发生
    • select为了让非阻塞io在没有事件时阻塞
  • selector.select(timeout)

    Selector(所有key) & SelectedKeys(发生事件的key)

  • SelectedKeys:Selector中发生事件的key。

  • Selector发生事件后,只会往SelectedKeys中加key,不会删除。

NIO之坑:完全理解NIO Selector - 掘金
image.png

  • 不论客户端正常断开、异常断开,都会产生一个read事件
    • 判断正常断开:read()返回-1
    • 异常断开:try catch
  • key.cancel()表示这个key已处理。

image.png

处理消息边界

  1. 固定长度消息
  2. 按分隔符
  3. TLV格式,type/length/value。

长消息会触发两次read事件。

attachment():关联ByteBuffer到Channel

  • 绑定buffer到channel:sc.register()
  • 获取:key.attachment();

image.png

写出

错误写法:
while循环阻塞
image.png
改进:
添加writable事件
image.png

多线程

  • boss负责建立链接
  • work负责读写

    Stream & channel

  • stream不会自动缓冲数据,channel会利用系统的缓冲区。

  • stream只阻塞api,channel有阻塞、非阻塞。

io模型

  • 主要是分为两个步骤,等待数据,复制数据。

    阻塞io

    image.png

    非阻塞io

  • 非阻塞的 read,指的是在数据到达前,即数据还未到达网卡,或者到达网卡但还没有拷贝到内核缓冲区之前,这个阶段是非阻塞的。

image.png

多路复用

  • 一个线程处理了多个客户端连接。

NIO - 图20
【好文】IO多路复用到底是不是异步的? - 知乎

同步,异步区别:复制数据是自己线程复制(从内核空间复制到用户空间)还是内核线程复制。

select函数

IO多路复用到底是不是异步的? - 知乎
image.png
poll和 select 的主要区别就是,去掉了 select 只能监听 1024 个文件描述符的限制。

epoll

IO多路复用到底是不是异步的? - 知乎

零拷贝