Netty——网络编程 NIO(Selector处理write事件 写入内容过多问题)代码示例

Posted 小志的博客

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Netty——网络编程 NIO(Selector处理write事件 写入内容过多问题)代码示例相关的知识,希望对你有一定的参考价值。

目录

一、写入内容过多问题概述

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

二、写入内容过多问题的代码示例

2.1、服务端代码示例

  • 服务端代码

    package com.example.nettytest.nio.day3;
    
    import java.io.IOException;
    import java.net.InetSocketAddress;
    import java.nio.ByteBuffer;
    import java.nio.channels.SelectionKey;
    import java.nio.channels.Selector;
    import java.nio.channels.ServerSocketChannel;
    import java.nio.channels.SocketChannel;
    import java.nio.charset.Charset;
    import java.util.Iterator;
    
    /**
     * @description: NIO(Selector处理write事件 写入内容过多问题)代码示例
     * @author: xz
     * @create: 2022-09-18
     */
    public class Test6Server 
        public static void main(String[] args) throws IOException 
            writeServer1();
        
        /**
         * Selector写入内容过多问题 代码示例
         * */
        public static void writeServer1() 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<SelectionKey> 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 < 9000000; i++) 
                            sb.append("a");
                        
                        ByteBuffer buffer = Charset.defaultCharset().encode(sb.toString());
                        if (buffer.hasRemaining()) 
                            // 2. 返回值代表实际写入的字节数
                            int write = sc.write(buffer);
                            System.out.println("实际写入的字节数===="+write);
                        
                    
                
            
        
    
    

2.2、客户端代码示例

  • 客户端代码示例

    package com.example.nettytest.nio.day3;
    
    import java.io.IOException;
    import java.net.InetSocketAddress;
    import java.nio.ByteBuffer;
    import java.nio.channels.SocketChannel;
    
    /**
     * @description:
     * @author: xz
     * @create: 2022-09-18
     */
    public class Test6Client 
        public static void main(String[] args) throws IOException 
            SocketChannel sc = SocketChannel.open();
            sc.connect(new InetSocketAddress("localhost", 8080));
    
            // 3. 接收数据
            int count = 0;
            while (true) 
                ByteBuffer buffer = ByteBuffer.allocate(1024 * 1024);
                count += sc.read(buffer);
                System.out.println("实际读取到的字节数==="+count);
                buffer.clear();
            
        
    
    

2.3、启动服务端和客户端进行测试

  • 启动服务端,控制台输出如下:

  • 启动客户端,控制台输出如下:

三、解决写入内容过多问题的代码示例

3.1、修改服务端代码示例

  • 再处理write事件代码中,代码修如下:

     // 2. 返回值代表实际写入的字节数
    int write = sc.write(buffer);
    System.out.println("实际写入的字节数===="+write);
    // 3. 判断是否有剩余内容
    if (buffer.hasRemaining()) 
        /**
         * 4、关注事件
         *sckey.interestOps 表示关注原先注册的事件
         * SelectionKey.OP_WRITE 表示关注可写事件
         */
        sckey.interestOps(sckey.interestOps() + SelectionKey.OP_WRITE);
        // 5. 把未写完的数据挂到 sckey 上
        sckey.attach(buffer);
    else if (key.isWritable()) 
        ByteBuffer buffer1 = (ByteBuffer) key.attachment();
        SocketChannel sc1 = (SocketChannel) key.channel();
        int write1 = sc1.write(buffer1);
        System.out.println("实际写入的字节数:"+write1);
        // 6. 清理操作
        if (!buffer.hasRemaining()) 
            // 需要清除buffer
            key.attach(null);
            //不需关注可写事件
            key.interestOps(key.interestOps() - SelectionKey.OP_WRITE);
        
    
    

3.2、服务端修改后的完整代码

  • 修改后的完整代码示例

    package com.example.nettytest.nio.day3;
    
    import java.io.IOException;
    import java.net.InetSocketAddress;
    import java.nio.ByteBuffer;
    import java.nio.channels.SelectionKey;
    import java.nio.channels.Selector;
    import java.nio.channels.ServerSocketChannel;
    import java.nio.channels.SocketChannel;
    import java.nio.charset.Charset;
    import java.util.Iterator;
    
    /**
     * @description: NIO(Selector处理write事件 写入内容过多问题)代码示例
     * @author: xz
     * @create: 2022-09-18
     */
    public class Test6Server 
        public static void main(String[] args) throws IOException 
            writeServer2();
        
    
        /**
         * 解决 Selector写入内容过多问题的方式 代码示例
         * */
        public static void writeServer2() 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<SelectionKey> 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 < 9000000; i++) 
                            sb.append("a");
                        
                        ByteBuffer buffer = Charset.defaultCharset().encode(sb.toString());
                        // 2. 返回值代表实际写入的字节数
                        int write = sc.write(buffer);
                        System.out.println("实际写入的字节数===="+write);
                        // 3. 判断是否有剩余内容
                        if (buffer.hasRemaining()) 
                            /**
                             * 4、关注事件
                             *sckey.interestOps 表示关注原先注册的事件
                             * SelectionKey.OP_WRITE 表示关注可写事件
                             */
                            sckey.interestOps(sckey.interestOps() + SelectionKey.OP_WRITE);
                            // 5. 把未写完的数据挂到 sckey 上
                            sckey.attach(buffer);
                        else if (key.isWritable()) 
                            ByteBuffer buffer1 = (ByteBuffer) key.attachment();
                            SocketChannel sc1 = (SocketChannel) key.channel();
                            int write1 = sc1.write(buffer1);
                            System.out.println("实际写入的字节数:"+write1);
                            // 6. 清理操作
                            if (!buffer.hasRemaining()) 
                                // 需要清除buffer
                                key.attach(null);
                                //不需关注可写事件
                                key.interestOps(key.interestOps() - SelectionKey.OP_WRITE);
                            
                        
                    
                
            
        
    
    

3.3、启动服务端和客户端进行测试

  • 启动服务端,控制台输出如下:

  • 启动客户端,控制台输出如下:

以上是关于Netty——网络编程 NIO(Selector处理write事件 写入内容过多问题)代码示例的主要内容,如果未能解决你的问题,请参考以下文章

Netty——NIO(Selector)

Netty——NIO(Selector)

Netty——网络编程 NIO(Selector处理read事件 消息边界问题)代码示例

Netty——网络编程 NIO(Selector处理write事件 写入内容过多问题)代码示例

Netty——网络编程 NIO(Selector处理write事件 写入内容过多问题)代码示例

Netty——网络编程 NIO(Selector处理write事件 写入内容过多问题)代码示例