看了就能懂的NIO使用深入详解

news2024/11/17 3:44:51

 NIO概述
 

  NIO介绍
 

  传统IO流(java.io):读写操作结束前,处于线性阻塞,代码简单,安全,性能低
 

  NIO:支持非阻塞式编程,性能更有优势,但代码编写较为复杂。
 

  概念理解
 

  同步(synchronous):一条线程执行期间,其他线程就只能等待。

  异步(asynchronous):一条线程在执行期间,其他线程无需等待。

  阻塞(blocking):当前任务未执行结束,会阻碍后续任务的执行。

  非阻塞(non-blocking):当前任务未执行结束,不会阻碍后续任务的执行。
 

  IO流与NIO的区别
 

  NIO是面向缓冲区,IO 面向流。

  NIO是非阻塞的,IO是阻塞的。

  NIO可以使用选择器,IO不涉及选择器。
 

  NIO组成
 

  Buffer(缓冲区,负责读写数据,类似火车)
 

  Channel(通道 ,负责传输,类似铁轨)
 

  Selector(选择器,负责调度通道,类似指挥中心)
 

  Buffer
 

  介绍
 

  理解:实质相当于普通IO流中的数组,负责数据的存和取。但是它提供了对数据的结构化访问,可以跟踪系统的读、写进程。

  常见分类:ByteBuffer、CharBuffer、DoubleBuffer、FloatBuffer、IntBuffer、LongBuffer、ShortBuffer。
 

  核心属性
 

  capacity:代表缓冲区的最大容量。
 

  limit:代表剩余(可存入/可读取)数量
 

  position:代表(存入/读取)位置
 

  mark:标记当前position的位置。
 

  四个属性关系:mark <= position <= limit <= capacity
 

  构造方法(以ByteBuffer为例)
 

  static ByteBuffer allocate(int capacity)分配一个新的字节缓冲区。
 

  static ByteBuffer allocateDirect(int capacity) 分配新的直接字节缓冲区。
 

  static ByteBuffer wrap(byte[] array)将 byte 数组包装到缓冲区中。
 

  常用方法
 

  获取属性值
 

  capacity():获取缓冲区的最大容量。
 

  limit():获取剩余(可存入/可读取)数量。
 

  position():获取(存入/读取)位置。
 

  mark():标记当前position的位置。
 

  存取数据
 

  put(Xxx[] xxx) 存入数据到缓冲区中,position >= limit不可写。

  get() 获取缓冲区的position位置数据,并将position后移,position >= limit不可读。
 

  核心方法
 

  flip()翻转此缓冲区(limit=capacity-postion,postion=0),清除标记,用于读取模式。
 

  clear()清除此缓冲区(limit=capacity,postion=0),清除标记,用于写入模式。
 

  rewind() 倒回这个缓冲区(position=0),清除标记。
 

  reset() 将此缓冲区的位置重置为先前标记的位置(position=mark)。
 

  演示代码

public class Test01Buffer {
    public static void main(String[] args) {
        //新建缓冲区对象(默认为写入模式)
        ByteBuffer b = ByteBuffer.allocate(10);

        //写入数据
        System.out.println("====写入模式属性状态====");
        showProperty(b);
        System.out.println("====读取模式属性状态====");
        //切换为读取模式
        b.flip();
        showProperty(b);

        System.out.println("====写入数据====");
        b.clear();
        b.put(new byte[]{1,2});
        showProperty(b);

        System.out.println("====读取数据====");
        b.flip();
        System.out.println("position-------->" + b.position() + ",get:" + b.get());
        //循环遍历通用格式
        //while (b.position()<b.limit()){
        //    System.out.println("position-------->" + b.position() + ",get:" + b.get());
        //}

        System.out.println("====重置操作位置前:记录位置====");
        showProperty(b);
        //记录位置
        b.mark();
        System.out.println("====重置操作位置前:获取新数据====");
        System.out.println("position-------->" + b.position() + ",get:" + b.get());
        showProperty(b);
        System.out.println("====重置操作位置后====");
        b.reset();
        showProperty(b);

        System.out.println("====倒回缓冲区前====");
        showProperty(b);
        System.out.println("====倒回缓冲区后====");
        b.rewind();
        showProperty(b);
    }

    //展示参数
    public static void showProperty(ByteBuffer b) {
        //容量
        System.out.println("capacity:" + b.capacity());
        //可存放个数
        System.out.println("limit:" + b.limit());
        //下一个存入位置
        System.out.println("position:" + b.position());
    }
}

  Channel入门
 

  介绍
 

  理解 Channel理解为通道,包含了写入和读取的操作,可以理解为IO中的流对象。Channel负责读写,Buffer负责存取。

  常见分类:FileChannel、SocketChannel、ServerSocketChannel、DatagramChannel
 

  Channel与IO流区别
 

  Channel是双向的,既可以读又可以写,而IO是单向的。

  Channel可以进行异步的读写,IO是不支持异步。

  Channel的读写必须通过buffer对象,IO通过流可以直接读写。
 

  构造方法(以FileChannel为例 )
 

  在IO流FileXXX字节流中提供了getChannel()方法获取FileChannel对象。

  FileChannel getChannel() 通过FileXXX字节流的方法获取对象
 

  常用方法
 

  int read(ByteBuffer dst):将数据读取到缓冲区中

  int write(ByteBuffer src):将数据从缓冲区中写出到指定位置
 

  演示代码

public class Test02FileChannel {
    public static void main(String[] args) throws IOException {
        FileChannel in = new FileInputStream("D:\\image.jpg").getChannel();
        FileChannel out = new FileOutputStream("D:\\imageCopy.jpg").getChannel();

        ByteBuffer b = ByteBuffer.allocate(10);
        int len = -1;
        while ((len = in.read(b)) != -1) {
            b.flip();
            out.write(b);
            b.clear();
        }
        in.close();
        out.close();
    }
}

  ChannelTCP协议编程
 

  介绍
 

  NIO中通过SocketChannel与ServerSocketChannel替代TCP协议的网络通信编程
 

  客户端通道操作
 

  SocketChannel 客户端通道,用于读写TCP网络协议数据
 

  获取对象 public static SocketChannelopen()
 

  连接服务器 boolean connect(SocketAddress remote)
 

  SocketAddress是抽象类,使用其子类InetSocketAddress创建的对象。InetSocketAddress(String ip,int port)
 

  等待客户端连接 SocketChannel accept()
 

  服务端通道操作
 

  ServerSocketChannel 服务端通道,用于服务端监听TCP连接
 

  获取对象 public static ServerSocketChannel open()
 

  绑定端口号 ServerSocketChannel bind(SocketAddress local)
 

  服务器代码

public class Test03ServerByChanner {
    public static void main(String[] args) throws IOException {
        //获取服务器通道对象
        ServerSocketChannel serverSocket = ServerSocketChannel.open();
        //绑定端口
        ServerSocketChannel socket = serverSocket.bind(new InetSocketAddress(8888));
        SocketChannel server = socket.accept();

        //接收数据
        System.out.println("服务端开始接收数据......");
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        int len = -1;
        while ((len = server.read(buffer)) != -1) {
            //翻转缓冲区,读取数据
            buffer.flip();
            System.out.println("server:" + new String(buffer.array()));
            buffer.clear();
        }

        System.out.println("服务端开始反馈数据......");

        buffer.put("数据收到了".getBytes());
        //翻转缓冲区,读取数据
        buffer.flip();
        //取出缓冲区数据,写会给客户端
        server.write(buffer);
       
        server.close();

    }
}

  客户端代码

public class Test03ClientByChannel {
    public static void main(String[] args) throws Exception {
        //获取连接对象
        SocketChannel client = SocketChannel.open();
        //连接服务器
        client.connect(new InetSocketAddress("localhost", 8888));

        //发送数据
        System.out.println("客户端开始发送数据......");
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        buffer.put("服务器,你好啊".getBytes());
        //翻转缓冲区,读取数据
        buffer.flip();
        //从缓冲区取出数据写入通道
        client.write(buffer);
        client.shutdownOutput();

        //等待反馈
        buffer.clear();
        int len = -1;
        while ((len = client.read(buffer)) != -1) {
            buffer.flip();
            System.out.println("client:" + new String(buffer.array()));
            buffer.clear();
        }
        //关闭客户端
        client.close();
    }
}

  多路复用
 

  介绍
 

  非多路复用:服务器端需要为每个端口的每次请求,开辟线程处理业务,高并发状态下会造成系统性能下降。

  多路复用:服务器端利用一个线程处理多个端口的访问请求,节省CPU资源,提高程序运行效率,高并发状态下有明显优势。

  核心知识
 

      1.通过Selector中的open方法,获取选择器对象
 

  public static Selector open():获取Selector对象
 

  2.通过Channel中的方法,注册通道给选择器
 

  ①创建通道对象,设置通道屏蔽模式
 

  void configureBlocking(boolean block)
 

  ②将通道注册给选择器,并设置该通道的关注事件
 

  SelectionKey register(Selector sel,int ops)
 

  Selector sel 要注册的选择器

  ops表示注册的事件类型,在 SelectionKey类中提供的四种类型实现。

  SelectionKey.OP_ACCEPT : 接收连接就绪事件,表示服务器监听到了客户连接,服务器可以接收这个连接了

  SelectionKey.OP_CONNECT:连接就绪事件,表示客户端和服务器的连接已经建立成功

  SelectionKey.OP_READ: 读就绪事件,表示通道中有了可读的数据,可以执行读操作了

  SelectionKey.OP_WRITE: 写就绪事件,表示已经可以向通道中写数据了
 

  注意事项

        被注册的Channel必须支持异步模式,否则异步NIO就无法工作,例如FileChannel(没有异步模式)不能被注册到Selector。

        ServerSocketChannel在注册时,只能使用以OP_ACCEPT状态注册,否则抛出异常。

        SocketChannel在注册时,不支持OP_ACCEPT状态注册。
 

  3.通过Selector中的方法,获取事件
 

  int select():将事件存放至事件集合,返回已就绪事件个数。如果没有新的已就绪事件,该方法将持续阻塞。
 

  Selector的Set selectedKeys():返回选择器的已就绪事件集。
 

  Set keys():返回选择器的感兴趣事件集(已注册的事件数)。
 

  SelectionKey概述

         SelectionKey 代表一个通道在Selector的注册事件关系键。

         当Selector通知某个传入事件时,是通过对应 SelectionKey 进行传递的。

         想要取消已注册的通道事件,需要通过SelectionKey的cancel方法完成。

  SelectionKey中属性:

              Interest set:兴趣集,表示已注册的事件集合,下一次调用方法,将测试是否有此事件的加入。

              通过SelectionKey的 int interestOps() 方法,可以获取当前 SelectionKey的感兴趣事件。

              Ready set:准备集,表示已准备就绪的事件集合。

              通过SelectionKey的 int readyOps()方法,可以获取当前 SelectionKey的准备就绪事件。

              Channel:事件对应的通道。

              通过SelectionKey的 SelectableChannel channel()方法,可以获取当前 SelectionKey的表示的通道。

             Selector:事件绑定的选择器。

             通过SelectionKey的 Selector selector() 方法,可以获取当前 SelectionKey的绑定的选择器。

            Attached:事件对象的附加信息。

            通过 SelectionKey的 Object attach(Object ob)方法,将给定对象附加到此键。

            通过 SelectionKey的 Object attachment()方法,检索当前的附件。

            通过 Channel的SelectionKey register(Selector sel,int ops,Object ob)方法,可以附件及获取附加信

  SelectionKey迭代器
 

  4.通过SelectionKey中的方法,判断事件
 

  isAcceptable() 是否有准备好接收新连接

  isConnectable() 是否有完成连接状态

  isReadable() 是否有处于可读取状态

  isWritable() 是否有处于可写入状态

  isValid() 是否是有效的键
 

  步骤
 

  1.获取选择器对象

  2.创建通道对象,设置异步,注册到选择器

  3.定义死循环,重复检查是否有新事件触发(Selector中的int select()方法)

  3.1.如果触发新时间,获取所有触发事件集(Selector的Set selectedKeys()方法)

  3.2.获取触发事件集合的迭代器

  3.3.遍历迭代器,获取所有触发的事件

       3.3.1判断触发事件类型,指向相应操作 举例 if (selectionKey.isAcceptable()) {}

       3.3.2删除已完成操作的触发事件 (Iterator的remove()方法)
 

  服务器端代码

public class Test04ServerBySelector {
    public static void main(String[] args) throws IOException, InterruptedException {

        //获取一个选择器
        Selector selector = Selector.open();

        //创建三个服务器通道,监听三个端口
        ServerSocketChannel serverChannel1 = ServerSocketChannel.open();
        serverChannel1.bind(new InetSocketAddress(6666));
        serverChannel1.configureBlocking(false);
        ServerSocketChannel serverChannel2 = ServerSocketChannel.open();
        serverChannel2.bind(new InetSocketAddress(7777));
        serverChannel2.configureBlocking(false);
        ServerSocketChannel serverChannel3 = ServerSocketChannel.open();
        serverChannel3.bind(new InetSocketAddress(8888));
        serverChannel3.configureBlocking(false);

        //将三个服务器通道注册给选择器
        serverChannel1.register(selector, SelectionKey.OP_ACCEPT);
        serverChannel2.register(selector, SelectionKey.OP_ACCEPT);
        serverChannel3.register(selector, SelectionKey.OP_ACCEPT);

        //循环监听三个通道
        while (true) {
            System.out.println("--------");
            System.out.println("等待客户端连接...");

            //获取触发的事件个数
            int keyCount = selector.select();//阻塞式方法
            System.out.println("有一个客户端连接成功...");
            System.out.println("已就绪事件个数=" + keyCount);
            System.out.println("注册通道数量=" + selector.keys().size());


            //获取触发事件集
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            System.out.println("触发事件数量=" + selectionKeys.size());

            //获取事件集迭代器
            Iterator<SelectionKey> it = selectionKeys.iterator();

            //遍历事件集
            while (it.hasNext()) {
                //获取注册键
                SelectionKey selectionKey = it.next();

                //使用选择器完成数据读取
                if (selectionKey.isAcceptable()) {
                    //获取通道对象
                    ServerSocketChannel channel = (ServerSocketChannel) selectionKey.channel();
                    //获取服务器与客户端的连接
                    SocketChannel server = channel.accept();
                    //设置非阻塞
                    server.configureBlocking(false);
                    //注册读取事件
                    server.register(selector, selectionKey.OP_READ);
                    //selectionKey.interestOps(selectionKey.OP_READ);
                } else if (selectionKey.isReadable()) {
                    //获取客户端数据
                    ByteBuffer buffer = ByteBuffer.allocate(1024);
                    SocketChannel server = (SocketChannel) selectionKey.channel();
                    server.read(buffer);
                    buffer.flip();
                    String content = new String(buffer.array(), 0, buffer.limit());
                    System.out.println("客户端发送的数据:" + content);
                    //关闭资源
                    server.close();

                }
                //删除当前触发事件
                it.remove();
            }
            System.out.println("休息1秒,等待下一次操作...");
            Thread.sleep(1000);
        }
    }
}

  客户端代码

public class Test04ClientByChannel {
    public static void main(String[] args) {
        int[] ports = {7777, 8888, 6666};
        for (int i = 0; i < ports.length; i++) {
            int port = ports[i];
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        //创建客户端通道
                        SocketChannel client = SocketChannel.open();
                        //连接服务器
                        client.connect(new InetSocketAddress("localhost", port));
                        //发送数据
                        ByteBuffer buffer = ByteBuffer.allocate(1024);
                        buffer.put("你好啊,哈哈哈".getBytes());
                        buffer.flip();
                        client.write(buffer);
                        //关闭资源
                        client.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
    }
}

  异步非阻塞交互(AIO)
 

  介绍
 

  支持异步操作的NIO体系

  常见分类:

         AsynchronousSocketChannel 客户端异步通道

         AsynchronousServerSocketChannel服务端异步通道

         AsynchronousFileChannel文件异步通道

         AsynchronousDatagramChannel 数据异步通道
 

  CompletionHandler回调接口

     void completed(V result,A attachment);异步操作成功被回调。

     void failed(Throwable exc,A attachment);异步操作失败时被回调。
 

  AsynchronousSocketChannel常用方法
 

  public static AsynchronousSocketChannel open();打开异步服务器套接字通道。

  void read(ByteBuffer dst,A attachment,CompletionHandler handler) 读取数据。

  void write(ByteBuffer src,A attachment,CompletionHandler handler) 写出数据
 

  AsynchronousServerSocketChannel常用方法
 

  public static AsynchronousServerSocketChannel open()打开异步服务器套接字通道。

  AsynchronousServerSocketChannel bind(SocketAddress local,int backlog) ;绑定服务端IP地址,端口号

  void accept(A attachment,CompletionHandler handler) ;接收连接
 

  服务器端代码

package com.NIO.src.com.itheima;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.util.concurrent.ExecutionException;

public class Test05ServerBySynChanner {
    //如果为true,服务器结束。
    static boolean isOver = false;

    public static void main(String[] args) throws IOException, ExecutionException, InterruptedException {


        //获取服务器通道
        AsynchronousServerSocketChannel serverChanner = AsynchronousServerSocketChannel.open();
        //绑定端口号
        serverChanner.bind(new InetSocketAddress(8888));
        // 获取服务器与客户端的对接
        serverChanner.accept("accept", new CompletionHandler<AsynchronousSocketChannel, String>() {
            @Override
            public void completed(AsynchronousSocketChannel result, String attachment) {
                try {
                    isOver=true;
                    System.out.println("接受了一个连接:" + result.getLocalAddress()
                            .toString());
                    // 给客户端发送数据并等待发送完成
                    result.write(ByteBuffer.wrap("From Server:我是服务器".getBytes()))
                            .get();
                    ByteBuffer readBuffer = ByteBuffer.allocate(128);
                    // 阻塞等待客户端接收数据
                    result.read(readBuffer).get();
                    System.out.println(new String(readBuffer.array()));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (ExecutionException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void failed(Throwable exc, String attachment) {
                isOver=true;
                System.out.println("连接失败");
            }
        });

        //由于异步执行,所以上述操作不会阻碍当前循环的执行。
        while (true) {
            if (isOver) {
                break;
            }
            System.out.println("服务端:先干为敬");
        }

    }
}

  客户端代码

public class Test05ClientBySynChannel {
    public static void main(String[] args) throws IOException, ExecutionException, InterruptedException {

        //创建客户端通道对象
        AsynchronousSocketChannel client = AsynchronousSocketChannel.open();
        //与服务器进行连接
        client.connect(new InetSocketAddress("localhost", 8888), "connect", new CompletionHandler<Void, String>() {
            @Override
            public void completed(Void result, String attachment) {
                System.out.println("连接到服务器成功!");
                try {
                    // 给服务器发送信息并等待发送完成
                    client.write(ByteBuffer.wrap("From client:我是服务器".getBytes())).get();
                    ByteBuffer readBuffer = ByteBuffer.allocate(128);
                    // 阻塞等待接收服务端数据
                    client.read(readBuffer).get();
                    System.out.println(new String(readBuffer.array()));
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (ExecutionException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void failed(Throwable exc, String attachment) {
                System.out.println("连接到服务器失败");
            }
        });
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/22747.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java基础深化和提高-------多线程与并发编程

目录 多线程与并发编程 多线程介绍 什么是程序&#xff1f; 什么是进程? 什么是线程&#xff1f; 进程、线程的区别 什么是并发 线程和方法的执行特点 方法的执行特点 线程的执行特点 什么是主线程以及子线程 主线程 子线程 线程的创建 通过继承Thread类实现多线程 通过Ru…

暴力美学,拒绝平庸,Alibab开源内部神仙级“K8S核心笔记”下载

各大互联网巨头在技术战略层面&#xff0c;都把云原生列为了主要发展方向。以阿里巴巴为例&#xff0c;他们技术老大说&#xff0c;云原生是云计算释放红利的最短路径&#xff0c;也是企业数字化的最短路径。 现在云原生工程师、Kubernetes 工程师工资都特别高&#xff0c;并且…

大厂光环下的功能测试,出去面试自动化一问三不知

在一家公司待久了技术能力反而变弱了&#xff0c;原来的许多知识都会慢慢遗忘&#xff0c;这种情况并不少见。 一个京东员工发帖吐槽&#xff1a;感觉在大厂快待废了&#xff0c;出去面试问自己接口环境搭建、pytest测试框架&#xff0c;自己做点工太久都忘记了。平时用的时候…

【BLE】蓝牙数据速率

【BLE】蓝牙数据速率 理论速度 物理层 未编码PHY&#xff0c;每位数据使用1个符号表示 1Mbps&#xff08;LE 1M PHY&#xff09; 2Mbps&#xff08;LE 2M PHY&#xff09; 编码PHY 500Kbps&#xff08;S2&#xff09; 125Kbps&#xff08;S8&#xff09; 1Mbps指的是每…

MATLAB改变默认工作路径

软件版本&#xff1a;MATLAB2022a 电脑系统&#xff1a;win10 问题&#xff1a; 每次打开matlab都会自动打开matlab.exe文件夹位置&#xff0c;而不是打开自己新建的工作空间每次都要转换&#xff0c;很麻烦 方法&#xff1a; 1、找到安装目录下的matlabrc.m文件&#xff0…

大事务问题到底要如何解决?

文章目录大事务引发的问题pom依赖解决方法1. 少用Transactional 注解2. 将查询(select)方法放到事务外3. 事务中避免远程调用4. 事务中避免一次性处理太多数据5. 非事务执行6. 异步处理大事务引发的问题 在 分 享 解 决 办 法 之 前 &#xff0c;先 看 看 系 统 中 如 果 出 现…

一款集成ST-link下载及虚拟串口的STM32F103C8T6最小系统板设计

前言 在以前的STM32单片机应用中&#xff0c;经常使用STM32F103C8T6最小系统板&#xff08;小蓝板&#xff09;作为主控。程序下载和串口交互都需要额外器件和接线&#xff0c;程序下载的话要用到ST-link&#xff0c;串口交互用到USB-TTL&#xff0c;常见的样子就下面这…

(历史上最详细的网络)华为初级网络工程师知识点总结(二)工作考研均受益

超级详细网络知识二一&#xff0c;关于IPV4和IPV6地址的介绍&#xff08;重点是IPV4&#xff09;1,IPV4地址的组成2&#xff0c;子网掩码的详解3&#xff0c;IP地址的分类和播的形式4&#xff0c;IP地址的分类可用地址5&#xff0c;IPV4的特殊地址&#xff0c;公网地址&#xf…

信息数据采集软件-什么工具可以快速收集信息

随着时代的不断的进步&#xff0c;我们已经悄然无息地步入了一个大数据信息时代&#xff0c;每个人在互联网上都离不开信息数据的汇总分析以及信息数据的应用&#xff0c;不管是亮化自己的信息数据&#xff0c;还是分析同行详细信息的数据。今天小编就教大家如何用信息抓取软件…

yapi文档转换jmx脚本

需求 需要自动生成接口测试脚本接口文档&#xff08;swagger/yapi/wiki&#xff09;很多&#xff0c;我不想一个一个去复制黏贴到jmeter 期望 一键自动生成接口测试脚本&#xff0c;解放双手&#xff0c;降低纯手力劳动占比&#xff0c;进而给自己提供更多的时间去思考、理解…

第九章:单调栈与单调队列

单调栈与单调队列一、单调栈1、什么是单调栈&#xff1f;2、单调栈的模板&#xff08;1&#xff09;问题&#xff1a;&#xff08;2&#xff09;分析&#xff1a;二、单调队列1、什么是单调队列2、单调队列模板&#xff08;1&#xff09;问题&#xff08;2&#xff09;分析一、…

深入浅出学习透析Nginx服务器的基本原理和配置指南「Https安全控制篇」

Https反向代理 之前的内容中我们主要针对于一些对安全性要求比较高的站点&#xff0c;可能会使用HTTPS&#xff08;一种使用SSL通信标准的安全HTTP协议&#xff09;&#xff0c;针对于HTTP 协议和SSL标准相信大家都知道了&#xff0c;在这里我就不为大家进行介绍了&#xff0c…

共建“医疗合规科技实验室”,美创科技实力护航医疗数据安全

11月15日-17日&#xff0c;由工业和信息化部、深圳市人民政府主办&#xff0c;中国互联网协会、广东省通信管理局、深圳市工业和信息化局等单位承办的2022中国互联网大会隆重召开。 在互联网医疗健康合规发展论坛上&#xff0c;医疗合规科技实验室合作伙伴计划正式启动&#xf…

scau Java综合性实验之Java源程序分析程序

1. 编写一个Java应用程序&#xff0c;实现对某个目录中的所有Java源程序文件&#xff08;包含该目录的子目录中的源程序文件&#xff09;进行统计。统计内容包括&#xff1a; (1) 目录中每个源程序文件的总行数和空白行数&#xff0c;文件的字节数&#xff1b; (2) 目录中所有源…

ADB调试--详细教程(附华为手机无法显示设备解决方法)

终端打开开发者模式&#xff0c;用数据线连接电脑&#xff0c;然后按照下面的步骤操作 1、开启开发者选项&#xff1a; 设置->关于设备->版本号&#xff08;连续点击5次&#xff09; 2、打开USB调试 在开发者选项中&#xff0c;找到USB调试&#xff0c;将此打开。 3、…

作为资深程序民工怎么能被性能优化难倒!原理与实战齐飞,源自大厂自然更专业!

性能优化是一个很复杂的工作&#xff0c;且充满了不确定性。它不像Java业务代码&#xff0c;可以一次编写到处运行(write once, run anywhere)&#xff0c;往往一些我们可能并不能察觉的变化&#xff0c;就会带来惊喜/惊吓。能够全面的了解并评估我们所负责应用的性能&#xff…

全渠道商城授权管控经销商,渠道商管理系统助力医药企业快速扩大渠道规模

随着医改的稳步推进&#xff0c;医药行业传统的以销售为主的扩张模式难以为继&#xff0c;国内药企面临创新转型。如何探寻医药数字化营销方法论&#xff0c;如何把握政策机遇和用户需求&#xff0c;利用数字化推动医药创新渠道破局&#xff0c;已成业内关注的重点。 后疫情时…

如何在win11中用双硬盘或移动硬盘装Ubuntu 20.04 双系统

首先明确一下思路&#xff0c;这个多硬盘的安装方式与单硬盘的方式没什么本质区别 下面介绍具体的方法&#xff1a; 1.下载Ubuntu系统镜像、制作系统盘 1.1 下载镜像 ubuntu20.04镜像下载&#xff1a;ubuntu20.04官网&#xff0c;点击进入下载 现在最新版是 Ubuntu 22.04.1…

ZX297520V3T:Codec NAU88C22驱动调试

一、音频驱动框架 ALSA(Advanced Linux Sound Architecture)是目前linux的主流音频体系结构。ALSA不仅在内核设备驱动层提供了alsa-driver,同时在应用层为我们提供了alsa-lib,应用程序只要调用alsa-lib提供的API,即可以完成对底层音频硬件的控制。为了方便调试,ALSA也提…

Spring~五种存储Bean对象的类注解、方法注解(@Bean)以及Bean对象的获取

目录 五种存储Bean对象的类注解 ​Controller Service Repository Component Configuration 方法注解Bean 使用Bean注解的常见问题 当一个类型有多个实例对象&#xff0c;使用类型获取就会报错 在容器中找不到Bean&#xff0c;不论通过什么方式来获取Bean对象都会报…