• netty-lean1


    IO四大模型

    1、同步阻塞IO(Blocking IO)
    2、同步非阻塞IO(Non-blocking IO)
    3、IO多路复用模型(IO Multiplexing)
    4、异步IO模型(Asynchronous IO)

    BIO(blocking I/O) 同步阻塞

    • 问题分析
      1-每个请求创建独立线程,与对应的客户端进行数据Read,业务处理,数据Write
      2-当并发数较大时,需要创建大量线程来处理连接,系统资源占用较大。
      3-连接建立后,如果当前线程暂时没有数据可读,则线程就阻塞在Read操作上,造成线程资源浪费。
    ----BIO进行通信
    
    public class BIOServer {
        
        public static void main(String[] args) throws Exception {
            // 线程池
            // 如果有客户端连狙就创建一个线程与之通信
            // 创建一个线程池
            ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
            // 创建serversocket
            ServerSocket serverSocket = new ServerSocket(6666);
            System.out.println("服务器启动了。。。");
    
            while (true){
                System.out.println("等待连接。。。");
                // 监听等待客户端连接
                final Socket socket = serverSocket.accept();
                System.out.println("----连接:------------------------------------------");
                System.out.println("连接到了一个客户端");
    
                //创建一个线程与之通信
                newCachedThreadPool.execute(new Runnable() {
                    @Override
                    public void run() {
                        // 可以和客户端通讯
                        handler(socket);
                    }
                });
            }
        }
    
        // 编写一个handler方法 和客户端通讯
        public static void handler(Socket socket){
            try {
                System.out.println("线程信息 id="+Thread.currentThread().getId());
                System.out.println("线程信息 name="+Thread.currentThread().getName());
                System.out.println("等待读取。。。");
    
                byte[] bytes = new byte[1024];
                // 通过socket获取一个输入流
                InputStream inputStream = socket.getInputStream();
                // 循环读取客户端发送的数据
                while (true){
                    System.out.println("----读取信息-------------------------------------");
                    System.out.println("线程信息 id="+Thread.currentThread().getId());
                    System.out.println("线程信息 name="+Thread.currentThread().getName());
                    int read = inputStream.read(bytes);
                    if (read != -1){
                        System.out.println(new String(bytes,0,read));
                    }else {
                        break;
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }finally {
                System.out.println("关闭cliant连接");
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    

    NIO(non-blocking I/O) 同步非阻塞

    • 三大核心部分
      1-Selector(选择器)
      2-Channel(通道)
      3-Buffer(缓冲区)

    • Selector、Channel、Buffer的关系说明:
      1-每个channel都会对应一个Buffer
      2-Selector对应一个线程,一个线程对应多个channel
      3-程序切换哪个channel由事件决定,Event就是一个重要概念
      4-selector会根据不同事件在各个通道切换
      5-buffer就是要给内存块,底层是有一个数组
      6-数据读取写入通过Buffer
      ,NIO的Buffer是可以读也可以写,需要flip方法切换。
      ,BIO中要么是输入流,或者是输出流,不能双向。
      7-channerl是双向的,可以返回底层操作系统的情况,如linux底层的操作系统通道是双向的。

    Buffer

    • 重要属性
      【capsity】容量大小
      【limit】缓冲区当前终点
      【position】位置
      【mark】标志

    • buffer方法:
      【flip()】就是把position的位置改变回0,以实现读写转换,读写实质就是position位置改变。
      【clear()】把标志位重置一下

    Channel

    channel是Nio中的一个接口
    【SoketChannel】 类似 Soket
    【ServerSoketChannel】类似ServerSoket
    【FileChannel】用于文件的数据读写,
    【DatagramChannel】用于UDP的数据读写,
    【ServerSocketChannel和 SocketChannel】用于TCP的数据读写。

    • Channel方法:
      【.writer()】buffer内容写入channel
      【.read()】buffer读取channel内容
      【.transferFrom(oherChannel,startPosition,endPosition)】把其他channel数据复制到当前channel
      【.transferTo(oherChannel,startPosition,endPosition)】把当前channel数据复制到其他channel

    • 文件拷贝

    ----通过buffer进行copy
    public class NIOFileChannel03copy {
        
        public static void main(String[] args) throws Exception{
            FileInputStream fileInputStream = new FileInputStream("D:\Users\xiaoaiying\IDEAProjects\netty\NettyPro\1.txt");
            FileChannel fileChannel_in = fileInputStream.getChannel();
    
            FileOutputStream fileOutputStream = new FileOutputStream("D:\Users\xiaoaiying\IDEAProjects\netty\NettyPro\2.txt");
            FileChannel fileChannel_out = fileOutputStream.getChannel();
    
            //buffer
            ByteBuffer byteBuffer = ByteBuffer.allocate(512);
    
            while (true){
    
                // 重要步骤,把buffer标志位恢复到初始状态,否则循环读写时如果一次没读完可能会发生错误
                byteBuffer.clear();
    
                // 循环将读取到的数据放入buffer,
                int read = fileChannel_in.read(byteBuffer);
                if (read == -1){
                    break;
                }
    
                // 将buffer内数据写出
                // 记得先反转buffer
                byteBuffer.flip();
    
                fileChannel_out.write(byteBuffer);
            }
        }
    }
    
    ----通过channel封装好的方法copy
    public class NIOFileChannel04copy {
        
        public static void main(String[] args) throws Exception{
            FileInputStream fileInputStream = new FileInputStream("D:\Users\xiaoaiying\IDEAProjects\netty\NettyPro\1.txt");
            FileChannel fileChannel_in = fileInputStream.getChannel();
    
            FileOutputStream fileOutputStream = new FileOutputStream("D:\Users\xiaoaiying\IDEAProjects\netty\NettyPro\2.txt");
            FileChannel fileChannel_out = fileOutputStream.getChannel();
    
    
            fileChannel_in.transferTo(0, fileChannel_in.size(),fileChannel_out);
            // 或者
    //        fileChannel_out.transferFrom(fileChannel_in,0,fileChannel_in.size());
    
            fileChannel_in.close();
            fileChannel_out.close();
            fileInputStream.close();
            fileOutputStream.close();
        }
    }
    
    • buffer只读
      【.asReadonlyBuffer()】buffer方法,返回一个只读buffer

    • MappedByteBuffer

    public class MappedByteBufferTest {
        
        public static void main(String[] args) throws Exception{
            RandomAccessFile randomAccessFile = new RandomAccessFile("D:\Users\xiaoaiying\IDEAProjects\netty\NettyPro\1.txt","rw");
            FileChannel channel = randomAccessFile.getChannel();
    
            /**
             * MappedByteBuffer,可以让文件直接在内存(堆外的内存)中进行修改,而如何同步到文件由NIO来完成
             * 参数1:Filechannel.MapMode . READ_wRITE使用的读写模式
             * 参数2:可以直接修改的起始位置
             * 参数3:是映射到内存的大小,即将文件的多少个字节映射到内存
             * 可以直接修改的范围就是0-5,不包括5
             * 实际类型DirectByteBuffer
             */
            MappedByteBuffer mappedByteBuffer = channel.map(
                    FileChannel.MapMode.READ_WRITE,
                    0,
                    5);
    
            // 修改内容
            mappedByteBuffer.put(0,(byte)'H');
            mappedByteBuffer.put(3,(byte)'9');
        }
    }
    
    • buffer的 分散和聚集
      【Scattering】[分散] 将数据写入到buffer时,可以采用buffer数组,依次写入
      【Gathering】[聚集] 从buffer读取数据时,可以采用buffer数组,依次读
    public class ScatteringAndGatheringTest {
    
        public static void main(String[] args) throws Exception {
            // 使用
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            InetSocketAddress inetSocketAddress = new InetSocketAddress(7000);
    
            // 绑定端口到socket,并启动
            serverSocketChannel.socket().bind(inetSocketAddress);
    
            // 创建buffer数组
            ByteBuffer[] byteBuffers = new ByteBuffer[2];
            byteBuffers[0] = ByteBuffer.allocate(5);
            byteBuffers[1] = ByteBuffer.allocate(3);
    
            // 等待客户端连接
            SocketChannel socketChannel = serverSocketChannel.accept();
    
            // 假定从客户端接收8字节
            int messageLenght = 8;
            // 循环读取
            while (true){
                int byteRead = 0;
                while (byteRead < messageLenght){
                    long l = socketChannel.read(byteBuffers);
                    byteRead += l; // 累计读取的字节数
                    System.out.println("byteRead=" + byteRead);
                    // 使用流打印,看看当前这个buffer的position和limit
                    Arrays.asList(byteBuffers)
                            .stream()
                            .map(buffer -> "postion="+buffer.position()+",limit="+buffer.limit())
                            .forEach(System.out::println);
    
                    // 将所有buffer进行flip
                    Arrays.asList(byteBuffers).forEach(buffer -> buffer.flip());
    
                    // 将数据读出显示到客户端
                    long byteWrite = 0;
                    while (byteWrite < messageLenght){
                        long wlen = socketChannel.write(byteBuffers);
                        byteWrite += wlen;
                    }
    
                    // 将所有buffer进行flip
                    Arrays.asList(byteBuffers).forEach(buffer -> buffer.clear());
                    System.out.println("byteRead="+byteRead +"byteWrie="+byteWrite + ",messagelenght="+messageLenght);
                }
            }
        }
    }
    

    selector

    selector能够检测多个注册的通道上是否有事件发生,(多个channel以事件的方式可以注册到同一个selector)

    • selector
      selector方法:
      【open()】得到一个选择器对象
      【select()】
      【selectedKeys()】获得selectKey集合

    • selectionKey

    selectionkey方法:
    【selector()】得到与之关联的selector
    【channel()】 得到与之关联的channel
    【attachment()】 得到与之关联的buffer,即数据
    public abstract SelectionKey interestOps(int ops); //设置或改变监听事件
    public final boolean isAcceptable(); //是否可以accept
    public final boolean isReadable();//是否可以读
    public final boolean isWritable(); //是否可以写

    selectionKey对应channel,channel绑定到selector

    • nio操作
    ----服务端
    public class NIOserver {
        public static void main(String[] args) throws Exception {
            // 创建serversoketChaneel
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
    
            serverSocketChannel.socket().bind(new InetSocketAddress(6666)); // 绑定端口
            serverSocketChannel.configureBlocking(false);// 设置非阻塞
    
            // 得到一个selector对象 selector开始监听
            Selector selector = Selector.open();
            // 把serverSocketChannel注册到selector   关系事件为OP_ACCEPT
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
    
            // 循环等待客户端连接
            while (true){
                // 等待1秒,如果没有事件发生
                if (selector.select(3000) == 0){
                    System.out.println("服务器等待了3秒,无连接");
                    continue;
                }
    
                // 如果返回>0 获取到selectionKey集合
                // selector.selectedKeys() 返回关注事件集合
                // 反向获取通道
                Iterator<SelectionKey> keyIterator = selector.selectedKeys().iterator();
                while (keyIterator.hasNext()){// 遍历有操作的selectionkey
                    SelectionKey key = keyIterator.next();// 获取到selectionkey
    
                    if (key.isAcceptable()){ // 如果事件是OP_ACCEPT  有新客户端连接
                        // 给该客户端生成一个socketChannel
                        SocketChannel socketChannel = serverSocketChannel.accept();
                        socketChannel.configureBlocking(false);// 将socketChannel设置为非阻塞
                        System.out.println("客户端连接成功 生成一个socketChannel:"+socketChannel.hashCode());
    
                        // 将当前socketChannel 注册到selector  关注事件为OP_READ  通过socketChannel关联一个buffer
                        socketChannel.register(selector,SelectionKey.OP_READ, ByteBuffer.allocate(1024));
                    }
                    if (key.isReadable()){  // 如果事件是OP_READ
                        // 通过key 反向获取channel
                        SocketChannel socketChannel = (SocketChannel) key.channel();
                        socketChannel.configureBlocking(false);
                        // 获取到该channel关联的buffer
                        ByteBuffer buffer = (ByteBuffer) key.attachment();
                        socketChannel.read(buffer);
                        System.out.println("form client:"+ new String(buffer.array()));
    
                    }
                    // 手动从集合中移动当前的selectionKey 防止重复操作
                    keyIterator.remove();
                }
    //        System.out.println("无连接,关闭!!!");
            }
        }
    }
    
    
    ----客户端
    public class NIOclient {
        public static void main(String[] args) throws Exception {
            // 得到一个网络通道
            SocketChannel socketChannel = SocketChannel.open();
            // 设置非阻塞
            socketChannel.configureBlocking(false);
            // 提供服务器端的ip和端口
            InetSocketAddress inetSocketAddress = new InetSocketAddress("127.0.0.1", 6666);
    
            // 连接服务器
            if (!socketChannel.connect(inetSocketAddress)){
                while (!socketChannel.finishConnect()){
                    System.out.println("因为连接需要时间,客户端不会阻塞,可以做其他工作。。。");
                }
            }
            // 如果连接成功就放数据
            String str = "hello,小艾";
            ByteBuffer buffer = ByteBuffer.wrap(str.getBytes());
            // 发动数据,将buffer数据写入channel
            socketChannel.write(buffer);
            System.in.read();
        } 
    }
    

    ----

    DMA : direct memory access直接内存拷贝(不使用CPU)
    0拷贝:从操作系统角度看,即没有cpu拷贝

  • 相关阅读:
    time模块
    time模块,计算时间差
    re模块
    Python之常用文件操作
    Django运行错误常见问题及解决方法1
    用JetBrains PyCharm 2017.2创建运行Django程序
    wsgi Python的WEB框架
    django模块安装环境变量
    Django
    DOM
  • 原文地址:https://www.cnblogs.com/xiaoaiying/p/14287982.html
Copyright © 2020-2023  润新知