一次无法写完例子

  • 非阻塞模式下,无法保证把 buffer 中所有数据都写入 channel,因此需要追踪 write 方法的返回值(代表实际写入字节数)
  • 用 selector 监听所有 channel 的可写事件,每个 channel 都需要一个 key 来跟踪 buffer,但这样又会导致占用内存过多,就有两阶段策略
    • 当消息处理器第一次写入消息时,才将 channel 注册到 selector 上
    • selector 检查 channel 上的可写事件,如果所有的数据写完了,就取消 channel 的注册
    • 如果不取消,会每次可写均会触发 write 事件

      attachment 附件

      ```java package cn.itcast.nio.c4;

import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.*; import java.nio.charset.Charset; import java.util.Iterator;

public class WriteServer { public static void main(String[] args) throws IOException { ServerSocketChannel ssc = ServerSocketChannel.open(); ssc.configureBlocking(false); Selector selector = Selector.open(); ssc.register(selector, SelectionKey.OP_ACCEPT); ssc.bind(new InetSocketAddress(8080)); while (true) { selector.select(); Iterator 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 < 5000000; i++) { sb.append(“a”); } ByteBuffer buffer = Charset.defaultCharset().encode(sb.toString());

  1. // 2. 返回值代表实际写入的字节数
  2. int write = sc.write(buffer);
  3. System.out.println(write);
  4. // 3. 判断是否有剩余内容
  5. if (buffer.hasRemaining()) {
  6. // 4. 关注可写事件 1 4
  7. sckey.interestOps(sckey.interestOps() + SelectionKey.OP_WRITE);

// sckey.interestOps(sckey.interestOps() | SelectionKey.OP_WRITE); // 5. 把未写完的数据以附件的形式关联到到sckey 上 sckey.attach(buffer); } } else if (key.isWritable()) { ByteBuffer buffer = (ByteBuffer) key.attachment(); SocketChannel sc = (SocketChannel) key.channel(); int write = sc.write(buffer); System.out.println(write); // 6. 清理操作 if (!buffer.hasRemaining()) { key.attach(null); // 需要清除buffer key.interestOps(key.interestOps() - SelectionKey.OP_WRITE);//不需关注可写事件 } } } } } }

  1. ```java
  2. package cn.itcast.nio.c4;
  3. import java.io.IOException;
  4. import java.net.InetSocketAddress;
  5. import java.nio.ByteBuffer;
  6. import java.nio.channels.SocketChannel;
  7. public class WriteClient {
  8. public static void main(String[] args) throws IOException {
  9. SocketChannel sc = SocketChannel.open();
  10. sc.connect(new InetSocketAddress("localhost", 8080));
  11. // 3. 接收数据
  12. int count = 0;
  13. while (true) {
  14. ByteBuffer buffer = ByteBuffer.allocate(1024 * 1024);
  15. count += sc.read(buffer);
  16. System.out.println(count);
  17. buffer.clear();
  18. }
  19. }
  20. }