7.NIO-selector-写入内容过多

1.4.3、写入内容过多

服务端

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(50001));

        while (true) {
            selector.select();
            Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
            while (iterator.hasNext()) {
                SelectionKey key = iterator.next();
                iterator.remove();

                if (key.isAcceptable()) {
                    SocketChannel sc = ssc.accept();
                    sc.configureBlocking(false);

                    StringBuilder builder = new StringBuilder();
                    for (int i = 0; i < 30000000; i++) {
                        builder.append("a");
                    }
                    ByteBuffer buffer = Charset.defaultCharset().encode(builder.toString());

                    while (buffer.hasRemaining()) {
                        int write = sc.write(buffer);//实际写入的字节数
                        System.out.println(write);
                    }
                }
            }
        }
    }
}

客户端

public class WriteClient {
    public static void main(String[] args) throws IOException {
        SocketChannel sc = SocketChannel.open();
        sc.connect(new InetSocketAddress(50001));

        int count = 0;
        while (true) {
            ByteBuffer buffer = ByteBuffer.allocate(1024 * 1024);
            int read = sc.read(buffer);
            count += read;
            System.out.println(count);
            buffer.clear();
        }

    }
}

结果

# 服务端 中间好多0,就是buffer满了
.....
0
0
0
2621420
1703923
4310084
# 客户端
....
30000000

这样显然不合适,解决

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

    Selector selector = Selector.open();
    ssc.register(selector, SelectionKey.OP_ACCEPT);
    ssc.bind(new InetSocketAddress(50001));

    while (true) {
        selector.select();
        Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
        while (iterator.hasNext()) {
            SelectionKey key = iterator.next();
            iterator.remove();

            if (key.isAcceptable()) {
                SocketChannel sc = ssc.accept();
                sc.configureBlocking(false);

                SelectionKey scKey = sc.register(selector, SelectionKey.OP_READ);
                StringBuilder builder = new StringBuilder();
                for (int i = 0; i < 30000000; i++) {
                    builder.append("a");
                }
                ByteBuffer buffer = Charset.defaultCharset().encode(builder.toString());

                int write = sc.write(buffer);//实际写入的字节数
                System.out.println(write);
                if (buffer.hasRemaining()) {
                    //关注可写事件。把之前已有事件加上,防止覆盖
                    scKey.interestOps(scKey.interestOps() + SelectionKey.OP_WRITE);
                    //把未写完数据挂到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);
                if (!buffer.hasRemaining()) {
                    key.attach(null);//清除附件
                    key.interestOps(key.interestOps() - SelectionKey.OP_WRITE);//去掉事件
                }

            }
        }
    }
}
posted @   jpy  阅读(36)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
历史上的今天:
2021-10-12 阿里云的这群疯子
点击右上角即可分享
微信分享提示