Java BIO、NIO、AIO、Netty面试题(已整理全套PDF版本)
什么是IO
Java中的I/O(输入/输出)机制基于流(Stream)的概念实现数据的传输。流将数据序列化,即按照特定顺序逐次进行读写操作。简而言之,Java程序通过I/O流与外部设备进行数据交换。
Java类库中的I/O功能十分丰富,涵盖多个应用场景,主要包括:标准输入/输出
- 文件系统操作
- 网络数据传输
- 字符串流处理
- 对象序列化流等
由于资料篇幅较长,因此选择性地展示了部分内容。资料整理花费了一年的零碎时间,希望能对大家学习有所帮助!有需要的伙伴篇末vx名片获取即可!
这种设计使得Java能够处理各种不同类型的数据输入输出需求。
IO
- 比如程序从服务器上下载图片,就是通过流的方式从网络上以流的方式到程序中,在到硬盘中
在了解不同的IO之前先了解:同步与异步,阻塞与非阻塞的区别
- 同步,一个任务的完成之前不能做其他操作,必须等待(等于在打电话)
- 异步,一个任务的完成之前,可以进行其他操作(等于在聊QQ)
- 阻塞,是相对于CPU来说的, 挂起当前线程,不能做其他操作只能等待
- 非阻塞,,无须挂起当前线程,可以去执行其他操作
什么是BIO
- BIO:同步并阻塞,服务器实现一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,没处理完之前此线程不能做其他操作(如果是单线程的情况下,我传输的文件很大呢?),当然可以通过线程池机制改善。BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,但程序直观简单易理解。
什么是NIO
- NIO:同步非阻塞,服务器实现一个连接一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4之后开始支持。
什么是AIO
- AIO:异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由操作系统先完成了再通知服务器应用去启动线程进行处理,AIO方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用操作系统参与并发操作,编程比较复杂,JDK1.7之后开始支持。.
- AIO属于NIO包中的类实现,其实IO主要分为BIO和NIO,AIO只是附加品,解决IO不能异步的实现
- 在以前很少有Linux系统支持AIO,Windows的IOCP就是该AIO模型。但是现在的服务器一般都是支持AIO操作
什么Netty
- Netty是由JBOSS提供的一个Java开源框架。Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。
- Netty 是一个基于NIO的客户、服务器端编程框架,使用Netty 可以确保你快速和简单的开发出一个网络应用,例如实现了某种协议的客户,服务端应用。Netty相当简化和流线化了网络应用的编程开发过程,例如,TCP和UDP的socket服务开发。
Netty是由NIO演进而来,使用过NIO编程的用户就知道NIO编程非常繁重,Netty是能够能跟好的使用NIO
BIO和NIO、AIO的区别
- BIO是阻塞的,NIO是非阻塞的.
- BIO是面向流的,只能单向读写,NIO是面向缓冲的, 可以双向读写
- 使用BIO做Socket连接时,由于单向读写,当没有数据时,会挂起当前线程,阻塞等待,为防止影响其它连接,,需要为每个连接新建线程处理.,然而系统资源是有限的,,不能过多的新建线程,线程过多带来线程上下文的切换,从来带来更大的性能损耗,因此需要使用NIO进行BIO多路复用,使用一个线程来监听所有Socket连接,使用本线程或者其他线程处理连接
- AIO是非阻塞 以异步方式发起 I/O 操作。当 I/O 操作进行时可以去做其他操作,由操作系统内核空间提醒IO操作已完成(不懂的可以往下看)
IO流的分类
按照读写的单位大小来分:
字符流
:以字符为单位,每次次读入或读出是16位数据。其只能读取字符类型数据。 (Java代码接收数据为一般为char数组,也可以是别的
)- 字节流:以字节为单位,每次次读入或读出是8位数据。可以读任何类型数据,图片、文件、音乐视频等。 (Java代码接收数据只能为
byte数组
)
按照实际IO操作来分:
- 输出流:从内存读出到文件。只能进行写操作。
- 输入流:从文件读入到内存。只能进行读操作。
- 注意:输出流可以帮助我们创建文件,而输入流不会。
按照读写时是否直接与硬盘,内存等节点连接分:
- 节点流:直接与数据源相连,读入或读出。
- 处理流:也叫包装流,是对一个对于已存在的流的连接进行封装,通过所封装的流的功能调用实现数据读写。如添加个Buffering缓冲区。(意思就是有个缓存区,等于软件和mysql中的redis)
- 注意:为什么要有处理流?主要作用是在读入或写出时,对数据进行缓存,以减少I/O的次数,以便下次更好更快的读写文件,才有了处理流。
什么是内核空间
- 我们的应用程序是不能直接访问硬盘的,我们程序没有权限直接访问,但是操作系统(Windows、Linux......)会给我们一部分权限较高的内存空间,他叫内核空间,和我们的实际硬盘空间是有区别的
五种IO模型
- 注意:我这里的用户空间就是应用程序空间
1.阻塞BIO(blocking I/O)
- A拿着一支鱼竿在河边钓鱼,并且一直在鱼竿前等,在等的时候不做其他的事情,十分专心。只有鱼上钩的时,才结束掉等的动作,把鱼钓上来。
- 在内核将数据准备好之前,系统调用会一直等待所有的套接字,默认的是阻塞方式。
2.非阻塞NIO(noblocking I/O)
- B也在河边钓鱼,但是B不想将自己的所有时间都花费在钓鱼上,在等鱼上钩这个时间段中,B也在做其他的事情(一会看看书,一会读读报纸,一会又去看其他人的钓鱼等),但B在做这些事情的时候,每隔一个固定的时间检查鱼是否上钩。一旦检查到有鱼上钩,就停下手中的事情,把鱼钓上来。 B在检查鱼竿是否有鱼,是一个轮询的过程。
3.异步AIO(asynchronous I/O)
- C也想钓鱼,但C有事情,于是他雇来了D、E、F,让他们帮他等待鱼上钩,一旦有鱼上钩,就打电话给C,C就会将鱼钓上去。
当应用程序请求数据时,内核一方面去取数据报内容返回,另一方面将程序控制权还给应用进程,应用进程继续处理其他事情,是一种非阻塞的状态。
4.信号驱动IO(signal blocking I/O)
- G也在河边钓鱼,但与A、B、C不同的是,G比较聪明,他给鱼竿上挂一个铃铛,当有鱼上钩的时候,这个铃铛就会被碰响,G就会将鱼钓上来。
信号驱动IO模型,应用进程告诉内核:当数据报准备好的时候,给我发送一个信号,对SIGIO信号进行捕捉,并且调用我的信号处理函数来获取数据报。
http://5.IO多路转接(I/O multiplexing)
- H同样也在河边钓鱼,但是H生活水平比较好,H拿了很多的鱼竿,一次性有很多鱼竿在等,H不断的查看每个鱼竿是否有鱼上钩。增加了效率,减少了等待的时间。
IO多路转接是多了一个select函数,select函数有一个参数是文件描述符集合,对这些文件描述符进行循环监听,当某个文件描述符就绪时,就对这个文件描述符进行处理。
- IO多路转接是属于阻塞IO,但可以对多个文件描述符进行阻塞监听,所以效率较阻塞IO的高。
什么是比特(Bit),什么是字节(Byte),什么是字符(Char),它们长度是多少,各有什么区别
- Bit最小的二进制单位 ,是计算机的操作部分取值0或者1
- Byte是计算机中存储数据的单元,是一个8位的二进制数,(计算机内部,一个字节可表示一个英文字母,两个字节可表示一个汉字。)
取值(-128-127)
- Char是用户的可读写的最小单位,他只是抽象意义上的一个符号。如‘5’,‘中’,‘¥’ 等等等等。在java里面由16位bit组成Char 取值
(0-65535)
- Bit 是最小单位 计算机他只能认识0或者1
- Byte是8个字节 是给计算机看的
- 字符 是看到的东西 一个字符=二个字节
什么叫对象序列化,什么是反序列化,实现对象序列化需要做哪些工作
- 对象序列化,将对象以二进制的形式保存在硬盘上
- 反序列化;将二进制的文件转化为对象读取
- 实现serializable接口,不想让字段放在硬盘上就加transient
在实现序列化接口是时候一般要生成一个serialVersionUID字段,它叫做什么,一般有什么用
- 如果用户没有自己声明一个serialVersionUID,接口会默认生成一个serialVersionUID
- 但是强烈建议用户自定义一个serialVersionUID,因为默认的serialVersinUID对于class的细节非常敏感,反序列化时可能会导致InvalidClassException这个异常。
- (比如说先进行序列化,然后在反序列化之前修改了类,那么就会报错。因为修改了类,对应的SerialversionUID也变化了,而序列化和反序列化就是通过对比其SerialversionUID来进行的,一旦SerialversionUID不匹配,反序列化就无法成功。
怎么生成SerialversionUID
- 可序列化类可以通过声明名为 "serialVersionUID" 的字段(该字段必须是静态 (static)、最终 (final) 的 long 型字段)显式声明其自己的 serialVersionUID
- 两种显示的生成方式(当你一个类实现了Serializable接口,如果没有显示的定义serialVersionUID,Eclipse会提供这个提示功能告诉你去定义 。在Eclipse中点击类中warning的图标一下,Eclipse就会自动给定两种生成的方式。
BufferedReader属于哪种流,它主要是用来做什么的,它里面有那些经典的方法
- 属于处理流中的缓冲流,可以将读取的内容存在内存里面,有readLine()方法
Java中流类的超类主要有那些?
- 超类代表顶端的父类(都是抽象类)
- java.io.InputStream
- java.io.OutputStream
- java.io.Reader
- java.io.Writer
为什么图片、视频、音乐、文件等 都是要字节流来读取
- 这个很基础,你看看你电脑文件的属性就好了,CPU规定了计算机存储文件都是按字节算的
IO的常用类和方法,以及如何使用
注意,如果懂IO的普通文件读写操作可以直接点击此处跳过,直接看网络操作IO编程,那个才是重点,点击即会跳转
前面讲了那么多废话,现在我们开始进入主题,后面很长,从开始的文件操作到后面的网络IO操作都会有例子:
注意,如果懂IO的普通文件读写操作可以直接点击此处跳过,直接看网络操作IO编程,那个才是重点,点击即会跳转
IO基本操作讲解
这里的基本操作就是普通的读取操作,如果想要跟深入的了解不同的IO开发场景必须先了解IO的基本操作
1 按字符
流读取文件
1.1 按字符流的·节点流方式读取
- 如果我们要取的数据基本单位是字符,那么用(字符流)这种方法读取文件就比较适合。比如:读取test.txt文件
注释:
字符流
:以字符为单位,每次次读入或读出是16位数据。其只能读取字符类型数据。 (Java代码接收数据为一般为char数组,也可以是别的
)- 字节流:以字节为单位,每次次读入或读出是8位数据。可以读任何类型数据,图片、文件、音乐视频等。 (Java代码接收数据只能为
byte数组
) - FileReader 类:(字符输入流) 注意:new FileReader("D:\test.txt");//文件必须存在
package com.test.io; import java.io.FileReader; import java.io.IOException; public class TestFileReader { public static void main(String[] args) throws IOException { int num=0; //字符流接收使用的char数组 char[] buf=new char[1024]; //字符流、节点流打开文件类 FileReader fr = new FileReader("D:\\test.txt");//文件必须存在 //FileReader.read():取出字符存到buf数组中,如果读取为-1代表为空即结束读取。 //FileReader.read():读取的是一个字符,但是java虚拟机会自动将char类型数据转换为int数据, //如果你读取的是字符A,java虚拟机会自动将其转换成97,如果你想看到字符可以在返回的字符数前加(char)强制转换如 while((num=fr.read(buf))!=-1) { } //检测一下是否取到相应的数据 for(int i=0;i<buf.length;i++) { System.out.print(buf[i]); } } }
- 运行结果:
·
1.2 按字符流的·处理流方式读取
- 效果是一样,但是给了我们有不同的选择操作。进行了一个小封装,加缓冲功能,避免频繁读写硬盘。我这只是简单演示,处理流其实还有很多操作
- BufferedReader 类: 字符输入流使用的类,加缓冲功能,避免频繁读写硬盘
package com.test.io; import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; public class TestBufferedReader { public static void main(String[] args) throws IOException { int num=0; //字符流接收使用的String数组 String[] bufstring=new String[1024]; //字符流、节点流打开文件类 FileReader fr = new FileReader("D:\\test.txt");//文件必须存在 //字符流、处理流读取文件类 BufferedReader br = new BufferedReader(fr); //临时接收数据使用的变量 String line=null; //BufferedReader.readLine():单行读取,读取为空返回null while((line=br.readLine())!=null) { bufstring[num]=line; num++; } br.close();//关闭文件 for(int i=0;i<num;i++) { System.out.println(bufstring[i]); } } }
- 测试效果一样
2 按字符
流写出文件
2.1 按字符流的·节点流方式写出
- 写出字符,使用(字符流)这种方法写出文件比较适合。比如:输出内容添加到test.txt文件
- FileWriter类:(字符输出流),如果写出文件不存在会自动创建一个相对应的文件。使用FileWriter写出文件默认是覆盖原文件,如果要想在源文件添加内容不覆盖的话,需要构造参数添加true参数:看示例了解
package com.test.io; import java.io.File; import java.io.FileWriter; import java.io.IOException; public class TestFileWriter { public static void main(String[] args) throws IOException { //File是操作文件类 File file = new File("D:\\test.txt");//文件必须存在 //字符流、节点流写出文件类 //new FileWriter(file,true),这个true代表追加,不写就代表覆盖文件 FileWriter out=new FileWriter(file,true); //写入的字节,\n代表换行 String str="\nholler"; //写入 out.write(str); out.close(); } }
- 运行效果:
2.2 按字符流的·处理流方式写出
- BufferedWriter : 增加缓冲功能,避免频繁读写硬盘。 我这里: //new FileWriter(file),这里我只给了他文件位置,我没加true代表覆盖源文件
package com.test.io; import java.io.*; public class TestBufferedWriter { public static void main(String[] args) throws IOException { //File是操作文件类 File file = new File("D:\\test.txt");//文件必须存在 //字符流、节点流写出文件类 //new FileWriter(file),这个我没加true代表覆盖文件 Writer writer = new FileWriter(file); 字符流、处理流写出文件类 BufferedWriter bw = new BufferedWriter(writer); bw.write("\n小心"); bw.close(); writer.close(); } }
- 运行效果:
3 按字节
流写入写出文件
3.1 按字节流的·节点流写入写出文件
- 如果我们要取的数据 图片、文件、音乐视频等类型,就必须使用字节流进行读取写出
注释:
字符流
:以字符为单位,每次次读入或读出是16位数据。其只能读取字符类型数据。 (Java代码接收数据为一般为char数组,也可以是别的
)- 字节流:以字节为单位,每次次读入或读出是8位数据。可以读任何类型数据,图片、文件、音乐视频等。 (Java代码接收数据只能为
byte数组
) - FileInputStream:(字节输入流)
- FileOutputStream:(字节输出流)
package com.test.io; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class TestFileOutputStream { public static void main(String[] args) throws IOException { //创建字节输入流、节点流方式读取文件 FileInputStream fis = new FileInputStream("D:\\Akie秋绘 - Lemon(Cover:米津玄師).mp3"); //创建字节输入流、节点流方式输出文件 FileOutputStream fos = new FileOutputStream("D:\\copy.mp3"); //根据文件大小做一个字节数组 byte[] arr = new byte[fis.available()]; //将文件上的所有字节读取到数组中 fis.read(arr); //将数组中的所有字节一次写到了文件上 fos.write(arr); fis.close(); fos.close(); } }
- 运行之前:
运行之后:
3.2 按字节流的·处理流写入写出文件
- FileInputStream:(字节输入流)
- FileOutputStream:(字节输出流)
- BufferedInputStream:(带缓冲区字节输入流)
- BufferedOutputStream:(带缓冲区字节输入流) 带缓冲区的处理流,缓冲区的作用的主要目的是:避免每次和硬盘打交道,提高数据访问的效率。
package com.test.io; import java.io.*; public class TestBufferedOutputStream { //创建文件输入流对象,关联致青春.mp3 public static void main(String[] args) throws IOException { FileInputStream fis = new FileInputStream("D:\\copy.mp3"); //创建缓冲区对fis装饰 BufferedInputStream bis = new BufferedInputStream(fis); //创建输出流对象,关联copy.mp3 FileOutputStream fos = new FileOutputStream("D:\\copy2.mp3"); //创建缓冲区对fos装饰 BufferedOutputStream bos = new BufferedOutputStream(fos); //循环直接输出 int i; while((i = bis.read()) != -1) { bos.write(i); } bis.close(); bos.close(); } }
- 运行之前:
运行之后:
网络操作IO讲解
- 我这使用Socket简单的来模拟网络编程IO会带来的问题
- 不懂Socket可以看我之前的文章,这个东西很容易懂的,就是基于TCP实现的网络通信,比http要快,很多实现网络通信的框架都是基于Socket来实现
网络操作IO编程演变历史
1 BIO编程会出现什么问题?
- BIO是阻塞的
- 例子: 阻塞IO(blocking I/O) A拿着一支鱼竿在河边钓鱼,并且一直在鱼竿前等,在等的时候不做其他的事情,十分专心。只有鱼上钩的时,才结束掉等的动作,把鱼钓上来。
- 看起来没问题,但是我很多请求一起发送请求资源怎么办:
那不是要等待第一个人资源完成后后面的人才可以继续? 因为BIO是阻塞的所以读取写出操作都是非常浪费资源的
BIO代码示例:(后面有代码,往后移动一点点,认真看,代码学习量很足
)
- 我这有三个类,我模拟启动服务端,然后启动客户端,模拟客户端操作未完成的时候启动第二个客户端
- 启动服务端(
后面有代码,我这是教运行顺序
)
- 启动第一个客户端,发现服务器显示连接成功
先不要在控制台 输入 ,模拟堵塞。(我的代码输入了就代表请求完成了)
·
- 启动第二个客户端,
发现服务端没效果
,而客户端连接成功(在堵塞当中)我这启动了俩个Client,注意看,(这俩个代码是一样的)
·
- 第一个客户控制台输入,输入完后就会关闭第一个客户端, 在看服务端发现第二个客户端连接上来了
·
BIO通信代码:
- TCP协议Socket使用BIO进行通信:服务端(先执行)
package com.test.io; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; //TCP协议Socket使用BIO进行通信:服务端 public class BIOServer { // 在main线程中执行下面这些代码 public static void main(String[] args) { //使用Socket进行网络通信 ServerSocket server = null; Socket socket = null; //基于字节流 InputStream in = null; OutputStream out = null; try { server = new ServerSocket(8000); System.out.println("服务端启动成功,监听端口为8000,等待客户端连接..."); while (true){ socket = server.accept(); //等待客户端连接 System.out.println("客户连接成功,客户信息为:" + socket.getRemoteSocketAddress()); in = socket.getInputStream(); byte[] buffer = new byte[1024]; int len = 0; //读取客户端的数据 while ((len = in.read(buffer)) > 0) { System.out.println(new String(buffer, 0, len)); } //向客户端写数据 out = socket.getOutputStream(); out.write("hello!".getBytes()); } } catch (IOException e) { e.printStackTrace(); } } }
- TCP协议Socket使用BIO进行通信:客户端(第二执行)
package com.test.io; import java.io.IOException; import java.io.OutputStream; import java.net.Socket; import java.util.Scanner; //TCP协议Socket使用BIO进行通信:客户端 public class Client01 { public static void main(String[] args) throws IOException { //创建套接字对象socket并封装ip与port Socket socket = new Socket("127.0.0.1", 8000); //根据创建的socket对象获得一个输出流 //基于字节流 OutputStream outputStream = socket.getOutputStream(); //控制台输入以IO的形式发送到服务器 System.out.println("TCP连接成功 \n请输入:"); String str = new Scanner(http://System.in).nextLine(); byte[] car = str.getBytes(); outputStream.write(car); System.out.println("TCP协议的Socket发送成功"); //刷新缓冲区 outputStream.flush(); //关闭连接 socket.close(); } }
- TCP协议Socket使用BIO进行通信:客户端(第三执行)
package com.test.io; import java.io.IOException; import java.io.OutputStream; import java.net.Socket; import java.util.Scanner; //TCP协议Socket:客户端 public class Client02 { public static void main(String[] args) throws IOException { //创建套接字对象socket并封装ip与port Socket socket = new Socket("127.0.0.1", 8000); //根据创建的socket对象获得一个输出流 //基于字节流 OutputStream outputStream = socket.getOutputStream(); //控制台输入以IO的形式发送到服务器 System.out.println("TCP连接成功 \n请输入:"); String str = new Scanner(http://System.in).nextLine(); byte[] car = str.getBytes(); outputStream.write(car); System.out.println("TCP协议的Socket发送成功"); //刷新缓冲区 outputStream.flush(); //关闭连接 socket.close(); } }
为了解决堵塞问题,可以使用多线程,请看下面
2 多线程解决BIO编程会出现的问题
这时有人就会说,我多线程不就解决了吗?
- 使用多线程是可以解决堵塞等待时间很长的问题,因为他可以充分发挥CPU
- 然而系统资源是有限的,不能过多的新建线程,线程过多带来线程上下文的切换,从来带来更大的性能损耗
万一请求越来越多,线程越来越多那我CPU不就炸了?
多线程BIO代码示例:
- 四个客户端,这次我多复制了俩个一样客户端类
先启动服务端,在启动所有客户端,测试
,发现连接成功(后面有代码
)
在所有客户端输入消息(Client01、Client02这些是我在客户端输入的消息
):发现没有问题
多线程BIO通信代码:
服务端的代码,客户端的代码还是上面之前的代码
package com.test.io; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; //TCP协议Socket使用多线程BIO进行通行:服务端 public class BIOThreadService { public static void main(String[] args) { try { ServerSocket server = new ServerSocket(8000); System.out.println("服务端启动成功,监听端口为8000,等待客户端连接... "); while (true) { Socket socket = server.accept();//等待客户连接 System.out.println("客户连接成功,客户信息为:" + socket.getRemoteSocketAddress()); //针对每个连接创建一个线程, 去处理I0操作 //创建多线程创建开始 Thread thread = new Thread(new Runnable() { public void run() { try { InputStream in = socket.getInputStream(); byte[] buffer = new byte[1024]; int len = 0; //读取客户端的数据 while ((len = in.read(buffer)) > 0) { System.out.println(new String(buffer, 0, len)); } //向客户端写数据 OutputStream out = socket.getOutputStream(); out.write("hello".getBytes()); } catch (IOException e) { e.printStackTrace(); } } }); thread.start(); } } catch (IOException e) { e.printStackTrace(); } } }
为了解决线程太多,这时又来了,线程池
3 线程池解决多线程BIO编程会出现的问题
这时有人就会说,我TM用线程池?
- 线程池固然可以解决这个问题,万一需求量还不够还要扩大线程池。当是这是我们自己靠着自己的思想完成的IO操作,Socket 上来了就去创建线程去抢夺CPU资源,MD,线程都TM做IO去了,CPU也不舒服呀
- 这时呢:Jdk官方坐不住了,兄弟BIO的问题交给我,我来给你解决:
NIO的诞生
线程池BIO代码示例:
- 四个客户端
先启动服务端,在启动所有客户端,测试
,(后面有代码
)
在所有客户端输入消息(Client01、Client02这些是我在客户端输入的消息
):发现没有问题
线程池BIO通信代码:
服务端的代码,客户端的代码还是上面的代码
package com.test.io; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; //TCP协议Socket使用线程池BIO进行通行:服务端 public class BIOThreadPoolService { public static void main(String[] args) { //创建线程池 ExecutorService executorService = Executors.newFixedThreadPool(30); try { ServerSocket server = new ServerSocket(8000); System.out.println("服务端启动成功,监听端口为8000,等待客户端连接..."); while (true) { Socket socket = server.accept();//等待客户连接 System.out.println("客户连接成功,客户信息为:" + socket.getRemoteSocketAddress()); //使用线程池中的线程去执行每个对应的任务 executorService.execute(new Thread(new Runnable() { public void run() { try { InputStream in = socket.getInputStream(); byte[] buffer = new byte[1024]; int len = 0; //读取客户端的数据 while ((len = in.read(buffer)) > 0) { System.out.println(new String(buffer, 0, len)); } //向客户端写数据 OutputStream out = socket.getOutputStream(); out.write("hello".getBytes()); } catch (IOException e) { e.printStackTrace(); } } }) ); } } catch (IOException e) { e.printStackTrace(); } } }
4 使用NIO实现网络通信
- NIO是JDK1.4提供的操作,他的流还是流,没有改变,服务器实现的还是一个连接一个线程,当是:
客户端发送的连接请求都会注册到多路复用器上
,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4之后开始支持。
看不懂介绍可以认真看看代码实例,其实不难
什么是通道(Channel)
- Channel是一个对象,可以通过它读取和写入数据。 通常我们都是将数据写入包含一个或者多个字节的缓冲区,然后再将缓存区的数据写入到通道中,将数据从通道读入缓冲区,再从缓冲区获取数据。
- Channel 类似于原I/O中的流(Stream),但有所区别:
- 流是单向的,通道是双向的,可读可写。
- 流读写是阻塞的,通道可以异步读写。
什么是选择器(Selector)
- Selector可以称他为通道的集合,每次客户端来了之后我们会把Channel注册到Selector中并且我们给他一个状态,在用死循环来环判断(
判断是否做完某个操作,完成某个操作后改变不一样的状态
)状态是否发生变化,知道IO操作完成后在退出死循环
什么是Buffer(缓冲区)
- Buffer 是一个缓冲数据的对象, 它包含一些要写入或者刚读出的数据。
- 在普通的面向流的 I/O 中,一般将数据直接写入或直接读到 Stream 对象中。当是有了Buffer(缓冲区)后,数据第一步到达的是Buffer(缓冲区)中
- 缓冲区实质上是一个数组(
底层完全是数组实现的,感兴趣可以去看一下
)。通常它是一个字节数组,内部维护几个状态变量,可以实现在同一块缓冲区上反复读写(不用清空数据再写)。
代码实例:
- 目录结构
- 运行示例,先运行服务端,在运行所有客户端控制台输入消息就好了。:
我这客户端和服务端代码有些修该变,后面有代码
服务端示例,先运行,想要搞定NIO请认真看代码示例,真的很清楚
package com.test.io; import com.lijie.iob.RequestHandler; 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.util.Iterator; import java.util.Set; public class NIOServer { public static void main(String[] args) throws IOException { //111111111 //Service端的Channel,监听端口的 ServerSocketChannel serverChannel = ServerSocketChannel.open(); //设置为非阻塞 serverChannel.configureBlocking(false); //nio的api规定这样赋值端口 serverChannel.bind(new InetSocketAddress(8000)); //显示Channel是否已经启动成功,包括绑定在哪个地址上 System.out.println("服务端启动成功,监听端口为8000,等待客户端连接..."+ serverChannel.getLocalAddress()); //22222222 //声明selector选择器 Selector selector = Selector.open(); //这句话的含义,是把selector注册到Channel上面, //每个客户端来了之后,就把客户端注册到Selector选择器上,默认状态是Accepted serverChannel.register(selector, SelectionKey.OP_ACCEPT); //33333333 //创建buffer缓冲区,声明大小是1024,底层使用数组来实现的 ByteBuffer buffer = ByteBuffer.allocate(1024); RequestHandler requestHandler = new RequestHandler(); //444444444 //轮询,服务端不断轮询,等待客户端的连接 //如果有客户端轮询上来就取出对应的Channel,没有就一直轮询 while (true) { int select = selector.select(); if (select == 0) { continue; } //有可能有很多,使用Set保存Channel Set<SelectionKey> selectionKeys = selector.selectedKeys(); Iterator<SelectionKey> iterator = selectionKeys.iterator(); while (iterator.hasNext()) { //使用SelectionKey来获取连接了客户端和服务端的Channel SelectionKey key = iterator.next(); //判断SelectionKey中的Channel状态如何,如果是OP_ACCEPT就进入 if (key.isAcceptable()) { //从判断SelectionKey中取出Channel ServerSocketChannel channel = (ServerSocketChannel) key.channel(); //拿到对应客户端的Channel SocketChannel clientChannel = channel.accept(); //把客户端的Channel打印出来 System.out.println("客户端通道信息打印:" + clientChannel.getRemoteAddress()); //设置客户端的Channel设置为非阻塞 clientChannel.configureBlocking(false); //操作完了改变SelectionKey中的Channel的状态OP_READ clientChannel.register(selector, SelectionKey.OP_READ); } //到此轮训到的时候,发现状态是read,开始进行数据交互 if (key.isReadable()) { //以buffer作为数据桥梁 SocketChannel channel = (SocketChannel) key.channel(); //数据要想读要先写,必须先读取到buffer里面进行操作 channel.read(buffer); //进行读取 String request = new String(buffer.array()).trim(); buffer.clear(); //进行打印buffer中的数据 System.out.println(String.format("客户端发来的消息: %s : %s", channel.getRemoteAddress(), request)); //要返回数据的话也要先返回buffer里面进行返回 String response = requestHandler.handle(request); //然后返回出去 channel.write(ByteBuffer.wrap(response.getBytes())); } iterator.remove(); } } } }
- 客户端示例:(
我这用的不是之前的了,有修改
)运行起来客户端控制台输入消息就好了。要模拟测试,请复制粘贴改一下,修改客户端的类名就行了,四个客户端代码一样的
,
package com.test.io; import java.io.IOException; import java.io.OutputStream; import java.net.Socket; import java.util.Scanner; //TCP协议Socket:客户端 public class Client01 { public static void main(String[] args) throws IOException { //创建套接字对象socket并封装ip与port Socket socket = new Socket("127.0.0.1", 8000); //根据创建的socket对象获得一个输出流 OutputStream outputStream = socket.getOutputStream(); //控制台输入以IO的形式发送到服务器 System.out.println("TCP连接成功 \n请输入:"); while(true){ byte[] car = new Scanner(http://System.in).nextLine().getBytes(); outputStream.write(car); System.out.println("TCP协议的Socket发送成功"); //刷新缓冲区 outputStream.flush(); } } }
5 使用Netty实现网络通信
- Netty是由JBOSS提供的一个Java开源框架。Netty提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。
- Netty 是一个基于NIO的客户、服务器端编程框架,使用Netty 可以确保你快速和简单的开发出一个网络应用,例如实现了某种协议的客户,服务端应用。Netty相当简化和流线化了网络应用的编程开发过程,例如,TCP和UDP的Socket服务开发。
Netty是由NIO演进而来,使用过NIO编程的用户就知道NIO编程非常繁重,Netty是能够能跟好的使用NIO
- Netty的原里就是NIO,他是基于NIO的一个完美的封装,并且优化了NIO,使用他非常方便,简单快捷
- 我直接上代码:
- 1、先添加依赖:
<dependency> <groupId>io.netty</groupId> <artifactId>netty-all</artifactId> <version>4.1.16.Final</version> </dependency>
- 2、NettyServer 模板,看起来代码那么多,
其实只需要添加一行消息就好了
请认真看中间的代码
package com.lijie.iob; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.codec.serialization.ClassResolvers; import io.netty.handler.codec.serialization.ObjectEncoder; import io.netty.handler.codec.string.StringDecoder; public class NettyServer { public static void main(String[] args) throws InterruptedException { EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workerGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup) .channel(NioServerSocketChannel.class) .childHandler(new ChannelInitializer<SocketChannel>() { @Override protected void initChannel(SocketChannel socketChannel) throws Exception { ChannelPipeline pipeline = socketChannel.pipeline(); pipeline.addLast(new StringDecoder()); pipeline.addLast("encoder", new ObjectEncoder()); pipeline.addLast(" decoder", new io.netty.handler.codec.serialization.ObjectDecoder(Integer.MAX_VALUE, ClassResolvers.cacheDisabled(null))); //重点,其他的都是复用的 //这是真正的I0的业务代码,把他封装成一个个的个Hand1e类就行了 //把他当成 SpringMVC的Controller pipeline.addLast(new NettyServerHandler()); } }) .option(ChannelOption.SO_BACKLOG, 128) .childOption(ChannelOption.SO_KEEPALIVE, true); ChannelFuture f = b.bind(8000).sync(); System.out.println("服务端启动成功,端口号为:" + 8000); f.channel().closeFuture().sync(); } finally { workerGroup.shutdownGracefully(); bossGroup.shutdownGracefully(); } } }
- 3、需要做的IO操作,重点是继承ChannelInboundHandlerAdapter类就好了
package com.lijie.iob; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; public class NettyServerHandler extends ChannelInboundHandlerAdapter { RequestHandler requestHandler = new RequestHandler(); @Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception { Channel channel = ctx.channel(); System.out.println(String.format("客户端信息: %s", channel.remoteAddress())); } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { Channel channel = ctx.channel(); String request = (String) msg; System.out.println(String.format("客户端发送的消息 %s : %s", channel.remoteAddress(), request)); String response = requestHandler.handle(request); ctx.write(response); ctx.flush(); } }
- 4 客户的代码还是之前NIO的代码,我在复制下来一下吧
package com.test.io; import java.io.IOException; import java.io.OutputStream; import java.net.Socket; import java.util.Scanner; //TCP协议Socket:客户端 public class Client01 { public static void main(String[] args) throws IOException { //创建套接字对象socket并封装ip与port Socket socket = new Socket("127.0.0.1", 8000); //根据创建的socket对象获得一个输出流 OutputStream outputStream = socket.getOutputStream(); //控制台输入以IO的形式发送到服务器 System.out.println("TCP连接成功 \n请输入:"); while(true){ byte[] car = new Scanner(http://System.in).nextLine().getBytes(); outputStream.write(car); System.out.println("TCP协议的Socket发送成功"); //刷新缓冲区 outputStream.flush(); } } }
- 运行测试,还是之前那样,启动服务端,在启动所有客户端控制台输入就好了:
由于资料篇幅较长,因此选择性地展示了部分内容。资料整理花费了一年的零碎时间,希望能对大家学习有所帮助!有需要的伙伴篇末vx名片获取即可!
相关文章:
Java BIO、NIO、AIO、Netty面试题(已整理全套PDF版本)
什么是IO Java中的I/O(输入/输出)机制基于流(Stream)的概念实现数据的传输。流将数据序列化,即按照特定顺序逐次进行读写操作。简而言之,Java程序通过I/O流与外部设备进行数据交换。 Java类库中的I/O功能十…...
TapData × 梦加速计划 | 与 AI 共舞,TapData 携 AI Ready 实时数据平台亮相加速营,企业数据基础设施现代化
在实时跃动的数据节拍中,TapData 与 AI 共舞,踏出智能未来的新一步。 4月10日,由前海产业发展集团、深圳市前海梦工场、斑马星球科创加速平台等联合发起的「梦加速计划下一位独角兽营」正式启航。 本次加速营以“打造下一位独角兽企业”为目…...
一键部署k8s之EFK日志收集系统
一、部署es 1.下载安装 #下载安装 https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-8.13.2-linux-x86_64.tar.gz #解压 [rootes software]# tar xf elasticsearch-8.13.2-linux-x86_64.tar.gz #创建运行elasticsearch服务用户并修改权限 [rootes softw…...
Python常用的第三方模块【openpyxl库】读写Excel文件
openpyxl库模块是用于处理Microsoft Excel文件的第三方库,可以对Excel文件中的数据进行写入和读取。 weather.pyimport reimport requests#定义函数 def get_html():urlhttps://www.weather.com.cn/weather1d/101210101.shtml #爬虫打开浏览器上的网页resprequests.…...
加油站小程序实战教程12显示会员信息
目录 1 布局搭建1.1 搭建头像1.2 显示会员等级1.3 余额显示 最终效果 我们上一篇介绍了会员注册的功能,会员注册后再次进入页面的时候就可以根据openid加载会员信息,本篇我们介绍一下显示会员的余额 1 布局搭建 我们现在在我的页面显示的是会员未开通…...
iOS中使用AWS上传zip文件到Minio上的oss平台上
1. 集成AWS相关库(千万不要用最新的版本,否则会出现风格化虚拟路径,找不到主机名) pod AWSS3, ~> 2.10.0 pod AWSCore, ~> 2.10.0 2. 编写集成的相关代码 - (void)uploadFileToMinIO {NSString *endPoint "http://…...
PaginationInnerInterceptor使用(Mybatis-plus分页)
引言 最近在编写SQL语句时总是想着偷懒,于是在前不久学习黑马点评时学到可以使用PaginationInnerInterceptor,于是现在我也在自己的项目中进行使用了,但是使用也遇到一些问题,如果你和我的问题一样,希望我的解决办法能…...
极狐GitLab CEO 柳钢受邀出席 2025 全球机器学习技术大会
极狐GitLab 是 GitLab 在中国的发行版,关于中文参考文档和资料有: 极狐GitLab 中文文档极狐GitLab 中文论坛极狐GitLab 官网 2025 年 4 月 18 日至 19 日,2025 全球机器学习技术大会(ML-Summit 2025)在上海隆重举行。…...
数据仓库 vs 数据湖:架构、应用场景与技术差异全解析
目录 一、概念对比:结构化 vs 全类型数据 二、技术架构对比 1. 数据仓库架构特点 2. 数据湖架构特点 三、典型应用场景 数据仓库适合: 数据湖适合: 四、数据湖仓一体:趋势还是折中? 五、总结:如何…...
【25软考网工笔记】第三章 局域网(1)CSMA/CD、二进制指数退避算法、最小帧长计算
目录 一、CSMA/CD 1. 局域网架构概述 2. 局域网的拓扑结构 3. CSMA 1)CSMA的三种监听算法 1、1-坚持型监听算法(继续监听,不等待) 2、非坚持型监听算法(后退随机事件) 3、P-坚持型监听算法 2&#…...
Harbor对接非AWS对象存储
背景说明 项目的应用完全运行在一个离线环境中,同时通过K8S的方式进行容器编排。需要自建一个harbor的镜像仓库。并且通过私有云提供的S3服务进行容器镜像的持久化存储。我踩的其中的一个坑就是S3的region名字非AWS的标准名称。运行时抱错如下: 2025-04…...
实训Day-1 漏洞攻击实战
目录 实训任务1 漏洞攻击实战一 实训任务2 漏洞攻击实战二 实训任务3 白云新闻搜索 实训任务4 手速要快 实训任务5 包罗万象 总结 今天的实训目的是为了:了解漏洞攻击的一般步骤;掌握SQL注入的基本原理;掌握XSS攻击的基本原理ÿ…...
Linux-网络基础
一.网络背景 网络的起源与20世纪中期的冷战背景密切相关。美苏争霸期间,美国国防部担心传统集中式通信系统(如电话网络)在核战争中容易被摧毁,因此急需一种去中心化、高容错的通信方式。1969年,美国国防部高级研究计划…...
算法 | 鲸鱼优化算法(WOA)原理,公式,应用,算法改进研究综述,完整matlab代码
===================================================== github:https://github.com/MichaelBeechan CSDN:https://blog.csdn.net/u011344545 ===================================================== 鲸鱼优化算法 一、原理与公式二、应用领域三、算法改进研究四、完整MAT…...
[BJDCTF2020]EzPHP
这一道题里面的知识点实在是太多了,即使这道题是我最喜欢的RCE也有点大脑停转了,所以还是做个笔记,以后方便回忆 直接跳过打点,来到源码 <?php highlight_file(__FILE__); error_reporting(0); $file "1nD3x.php"…...
企业微信-自建应用
1. 创建自建应用 2. 配置小程序/H5入口 3. 准备 : CorpId(企业id)、 AgentID(应用id)、 CorpsecretID(应用Secret) 4. 配置企业可信IP 5. 如H5需要授权登录,那么需要配置网页授…...
[FPGA基础] 时钟篇
Xilinx FPGA 时钟管理详细文档 本文档详细介绍 Xilinx FPGA 中的时钟管理,包括时钟资源、时钟管理模块、设计注意事项以及最佳实践。适用于使用 Xilinx 7 系列、UltraScale 和 UltraScale 系列 FPGA 的开发者。 1. 时钟资源概述 Xilinx FPGA 提供丰富的时钟资源&a…...
高德火星坐标(GCJ-02)转WGS84坐标
高德火星坐标(GCJ-02)转WGS84坐标 1 转换算法 import mathdef gcj02_to_wgs84(lon, lat):"""高德火星坐标(GCJ-02)转WGS84坐标"""a 6378245.0 # 长半轴ee 0.00669342162296594323 # 扁率def transform_lon(x, y):ret 300.0 x 2.0 * y …...
基于opencv和PaddleOCR识别身份证信息
1、安装组件 pip install --upgrade paddlepaddle paddleocr 2、完整code import cv2 import numpy as np from paddleocr import PaddleOCR# 初始化 PaddleOCR use_angle_clsTrue, lang"ch", det_db_thresh0.1, det_db_box_thresh0.5)def preprocess_image(image…...
Day-1 漏洞攻击实战
实训任务1 漏洞攻击实战一 使用 御剑 得到网站后台地址 数据库登录与日志配置 使用默认密码 root:root 登录phpMyAdmin,执行 SHOW VARIABLES LIKE general% 查看日志状态。 开启日志功能:set global general_log "ON";(配图&…...
穿透数据迷雾:PR 曲线与 ROC 曲线的深度剖析+面试常见问题及解析
一、混淆矩阵与评价指标基础 混淆矩阵核心构成:混淆矩阵是分类模型性能评估的基石,以 22 矩阵形式呈现分类结果。其中,真正例(TP)表示实际为正类且被正确预测的样本;假正例(FP)是实…...
【Linux篇】轻松搭建命名管道通信:客户端与服务器的互动无缝连接
从零开始:基于命名管道实现客户端与服务器的实时通信 一. 命名管道1.1 基本概念1.2 创建命名管道1.2.1 创建方法1.2.2 示例代码:1.2.3 注意事项:1.3 与匿名管道区别 1.4 打开原则1.4.1 管道打开顺序1.4.2 阻塞行为1.4.3 管道的关闭1.4.4 关闭…...
快充协议芯片XSP04D支持使用一个Type-C与电脑传输数据和快充取电功能
快充是由充电器端的充电协议和设备端的取电协议进行握手通讯进行协议识别来完成的,当充电器端的充电协议和设备端的取电协议握手成功后,设备会向充电器发送电压请求,充电器会根据设备的需求发送合适的电压给设备快速供电。 设备如何选择快充…...
MySQL的窗口函数(Window Functions)
一、窗口函数核心概念 窗口(Window) 窗口是数据行的集合,由OVER()子句定义。它决定了函数计算的“数据范围”,可以是一个分区的全部行、当前行前后的行,或动态变化的子集。 语法结构 SELECT window_f…...
一个很简单的机器学习任务
一个很简单的机器学习任务 前言 基于线上colab做的一个简单的案例,应用了线性回归算法,预测了大概加州3000多地区的房价中位数 过程 先导入了Pandas,这是一个常见的Python数据处理函数库 用Pandas的read_csv函数把网上一个共享数据集&…...
ORION:通过视觉-语言指令动作生成的一个整体端到端自动驾驶框架
25年3月来自华中科技和小米电动汽车的论文“ORION: A Holistic End-to-End Autonomous Driving Framework by Vision-Language Instructed Action Generation”。 由于因果推理能力有限,端到端 (E2E) 自动驾驶方法仍然难以在交互式闭环评估中做出正确决策。当前的方…...
python全栈-flask
python全栈-flask 文章目录 入门上手hello worldflask运行方式测试路由with app.test_request_context():debug模式配置flask参数动态路由数据类型自定义转换器to_pythonPostMan(API测试查询参数的获取请求体参数上传文件其它参数url_for 函数重定向响应内容自定义响…...
Unity中的数字孪生项目:两种输入方式对观察物体的实现
在数字孪生项目中,精确的相机控制至关重要。相机不仅需要灵活地跟随目标,还要能够平滑地旋转和缩放,以便观察和分析物体的各个细节。今天,我将通过 TouchControlCamera 和 CameraRotate 两个脚本,展示如何实现一个适用…...
ECharts散点图-散点图14,附视频讲解与代码下载
引言: ECharts散点图是一种常见的数据可视化图表类型,它通过在二维坐标系或其它坐标系中绘制散乱的点来展示数据之间的关系。本文将详细介绍如何使用ECharts库实现一个散点图,包括图表效果预览、视频讲解及代码下载,让你轻松掌握…...
【教程】Digispark实现串口通信
转载请注明出处:小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你,欢迎[点赞、收藏、关注]哦~ 没想到这么老,很多代码都不能用,修了好久。。。 TinySoftwareSerial.cpp #include <stdlib.h> #include <stdio.h&g…...
GPT-4.1 开启智能时代新纪元
GPT-4.1 全解析:开启智能时代新纪元(含费用详解) 2025年4月,OpenAI 正式推出全新一代语言模型——GPT-4.1 系列,包括 GPT-4.1、GPT-4.1 Mini 和 GPT-4.1 Nano。相比以往模型,它在代码生成、指令理解、长文本…...
4.21 spark和hadoop的区别与联系
一、Hadoop 1. 定义 Hadoop是一个由Apache基金会开发的分布式系统基础架构。它最初是为了解决大规模数据存储和处理的问题而设计的。Hadoop的核心组件包括HDFS(Hadoop Distributed File System)和MapReduce。 2. HDFS(Hadoop Distributed Fi…...
Nacos 客户端 SDK 的核心功能是什么?是如何与服务端通信的?
Nacos 客户端 SDK 的核心功能 Nacos 客户端 SDK 是应用程序集成 Nacos 能力的桥梁,它封装了与 Nacos 服务端交互的复杂性,为开发者提供了简单易用的 API。其核心功能主要围绕两大方面:服务发现 和 配置管理。 服务发现 (Service Discovery) …...
servlet-保存作用域
保存作用域 保存作用域:原始情况下,保存作用域我们有四个:page(一般不用了) 、request(一般请求响应范围)、session(一次会话范围)、application(整个应用程序范围)1)request:一般请求响应范围…...
从规则到大模型:知识图谱信息抽取实体NER与关系RE任务近10年演进发展详解
摘要: 本文回顾了关系抽取与实体抽取领域的经典与新兴模型,清晰地梳理了它们的出现时间与核心创新,并给出在 2025 年不同资源与场景下的最佳实践推荐。文章引用了 BiLSTM‑CRF、BiLSTM‑CNN‑CRF、SpanBERT、LUKE、KnowBERT、CasRel、REBEL、…...
【自然语言处理与大模型】模型压缩技术之蒸馏
知识蒸馏是一种模型压缩技术,主要用于将大型模型(教师模型)的知识转移到更小的模型(学生模型)中。在大语言模型领域,这一技术特别重要。 知识蒸馏的核心思想是利用教师模型的输出作为软标签(sof…...
yum如果备份已经安装的软件?
在 CentOS 系统中,你可以通过以下步骤将 yum 下载的组件打包备份到本地: 方法 1:使用 yumdownloader 直接下载 RPM 包 1. 安装 yum-utils 工具 yum install -y yum-utils2. 下载指定软件包及其依赖 yumdownloader --resolve <package-n…...
室外摄像头异常自检指南+视频监控系统EasyCVR视频质量诊断黑科技
室外监控摄像头在安防监控系统运行中,常出现连接不畅、设备互认失败等问题。今天我们来介绍两类安防监控摄像头的典型问题及排查步骤。 问题1:同品牌新摄像头无法被老录像机识别 排查步骤: 1)供电检查 确认摄像头供电线路连接正…...
从本地存档到协作开发的Git简单使用
概念 工作区 : 在本地实际进行文件操作的目录 .暂存区 : 类似于缓冲区 , 用于记录准备进行下一次提交的内容 .本地仓库 : 储存在本地的完整版本库 , 包含项目的提交历史 , 分支信息和标签等 .远程仓库 : 部署在远程服务器的版本库 , 通常用于协作开发 . 文件状态 Untracked …...
在 Android 中实现通话录音
在 Android 中实现通话录音需要处理系统权限、通话状态监听和音频录制等关键步骤。以下是详细实现代码及注释,注意不同 Android 版本和厂商设备的兼容性问题: 1. 添加权限声明(AndroidManifest.xml) <!-- 录制音频权限 -->…...
系统分析师知识点:访问控制模型OBAC、RBAC、TBAC与ABAC的对比与应用
在信息安全领域,访问控制是确保数据和资源安全的关键技术。随着信息系统复杂度的提高,访问控制技术也在不断演进,从早期简单的访问控制列表(ACL)发展到如今多种精细化的控制模型。本文将深入剖析四种主流的访问控制模型:基于对象的…...
网络原理(TCP协议—协议格式,性质(上),状态)
目录 1.TCP协议段格式。 2.TCP协议传输时候的性质。 2.1确认应答。 2.2超时重传。 2.3连接管理。 2.3.1 三次握手。 2.3.2四次挥手。 3.TCP常见的状态。 1.TCP协议段格式。 TCP协议段是由首部和数据两部分构成的。首部包含了TCP通信所需要的各种控制信息,而…...
用全新发布的ChatGPT-o3搜文献写综述、专业审稿、降重润色,四个步骤轻松搞定全部论文难题!
今天和大家聊聊OpenAI近期发布的o系列模型中的两个大成果:o3和o4-mini,这个系列的模型最大特点是经过训练,会在响应之前进行更长时间的思考,给出更深入的回答。 下面文章七哥会为大家深度讲解o3模型在学术研究和论文写作方面的四大优势,并附上实用有效的使用技巧和步骤供…...
多路由器通过RIP动态路由实现通讯(单臂路由)
多路由器通过RIP动态路由实现通讯(单臂路由) R1(开启端口并配置IP) Router>en Router#conf t Router(config)#int g0/0 Router(config-if)#no shu Router(config-if)#no shutdown Router(config-if)#ip add 192.168.10.254 255.255.255.0 Router(c…...
分数线降低,25西电马克思主义学院(考研录取情况)
1、马克思主义学院各个方向 2、马克思主义学院近三年复试分数线对比 学长、学姐分析 由表可看出: 1、马克思主义理论25年相较于24年下降10分,为355分 3、25vs24推免/统招人数对比 学长、学姐分析 由表可看出: 1、 马克思主义学院25年共接…...
反转字符串
344. 反转字符串 题目 思路 双指针 设 s 长度为 n。反转可以看成是交换 s[0] 和 s[n−1],交换 s[1] 和 s[n−2],交换 s[2] 和 s[n−3],依此类推。 代码 class Solution:def reverseString(self, s: List[str]) -> None:""&q…...
乾元通渠道商中标舟山市自然灾害应急能力提升工程基层防灾项目
近日,乾元通渠道商中标舟山市自然灾害应急能力提升工程基层防灾项目(结余资金)装备采购项目,乾元通作为设备厂家,为项目提供通信指挥类装备(多链路聚合设备)QYT-X1。 青岛乾元通数码科技有限公司…...
信号调制与解调技术基础解析
调制解调技术是通信系统中实现基带信号与高频载波信号相互转换的主要技术,通过调整信号特性使其适应不同信道环境,保障信息传输的效率和可靠性。 调制与解调的基本概念 调制(Modulation) 将低频基带信号(如语音或数…...
多源异构网络安全数据(CAPEC、CPE、CVE、CVSS、CWE)的作用、数据内容及其相互联系的详细分析
1. CWE(Common Weakness Enumeration) 作用:CWE 是常见软件和硬件安全弱点的分类列表,用于描述漏洞的根本原因(如代码缺陷、逻辑错误等),为漏洞的根源分析提供框架。数据内容: 弱点…...
02_Flask是什么?
一、视频教程 02_Flask是什么 二、Flask简介 Flask 框架诞生于2010 年,是由 Armin 使用 Python 语言基于 Werkzeug 工具箱编写的轻量级Web开发框架。Armin 是 Python 编程语言的核心开发者之一,同时也是 Flask 项目的主要贡献者。 Flask主要依赖于两个核…...