Day18

字节缓冲流

字节缓冲流:

  • BufferOutputStream:该类实现缓冲输出流。 通过设置这样的输出流,应用程序可以向底层输出流写入字节,而不必为写入的每个字节导致底层系统的调用

  • BufferedInputStream:创建BufferedInputStream将创建一个内部缓冲区数组。 当从流中读取或跳过字节时,内部缓冲区将根据需要从所包含的输入流中重新填充,一次很多字节

构造方法:

  • 字节缓冲输出流:BufferedOutputStream(OutputStream out)

  • 字节缓冲输入流:BufferedInputStream(InputStream in)

为什么构造方法需要的是字节流,而不是具体的文件或者路径呢?

  • 字节缓冲流仅仅提供缓冲区,而真正的读写数据还得依靠基本的字节流对象进行操作

 BufferedInputStream dos = new BufferedInputStream(new FileInputStream("myExtends//java.txt"));
 byte [] bys = new byte[1024];
 int len;
 while((len=dos.read(bys))!=-1){
     System.out.println(new String(bys,0,len));
 }
 dos.close();

字符流

为什么会出现字符流

由于字节流操作中文不是特别的方便,所以Java就提供字符流

  • 字符流 = 字节流 + 编码表

用字节流复制文本文件时,文本文件也会有中文,但是没有问题,原因是最终底层操作会自动进行字节拼接成中文,如何识别是中文的呢?

  • 汉字在存储的时候,无论选择哪种编码存储,第一个字节都是负数

编码表

基础知识:

  • 计算机中储存的信息都是用二进制数表示的;我们在屏幕上看到的英文、汉字等字符是二进制数转换之后的结果

  • 按照某种规则,将字符存储到计算机中,称为编码。反之,将存储在计算机中的二进制数按照某种规则解析显示出来,称为解码 。这里强调一下:按照A编码存储,必须按照A编码解析,这样才能显示正确的文本符号。否则就会导致乱码现象 字符编码:就是一套自然语言的字符与二进制数之间的对应规则(A,65)

  • 字符集: 是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等 计算机要准确的存储和识别各种字符集符号,就需要进行字符编码,一套字符集必然至少有一套字符编码。常见字符集有ASCII字符集、GBXXX字符集、Unicode字符集等

  • ASCII字符集: ASCII(American Standard Code for Information Interchange,美国信息交换标准代码):是基于拉丁字母的一套电脑编码系统,用于显示现代英语,主要包括控制字符(回车键、退格、换行键等)和可显示字符(英文大小写字符、阿拉伯数字和西文符号)

  • 基本的ASCII字符集,使用7位表示一个字符,共128字符。ASCII的扩展字符集使用8位表示一个字符,共256字符,方便支持欧洲常用字符。是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等

GBXXX字符集:

  • GB2312:简体中文码表。一个小于127的字符的意义与原来相同,但两个大于127的字符连在一起时, 就表示一个汉字,这样大约可以组合了包含7000多个简体汉字,此外数学符号、罗马希腊的字母、日文的假名等都编进去了,连在ASCII里本来就有的数字、标点、字母都统统重新编了两个字节长的编码,这就是常说的"全角"字符,而原来在127号以下的那些就叫"半角"字符了

  • GBK:最常用的中文码表。是在GB2312标准基础上的扩展规范,使用了双字节编码方案,共收录了21003个汉字,完全兼容GB2312标准,同时支持繁体汉字以及日韩汉字等

  • GB18030:最新的中文码表。收录汉字70244个,采用多字节编码,每个字可以由1个、2个或4个字节组成。支持中国国内少数民族的文字,同时支持繁体汉字以及日韩汉字等

Unicode字符集:

  • 为表达任意语言的任意字符而设计,是业界的一种标准,也称为统一码、标准万国码。它最多使用4个字节的数字来表达每个字母、符号,或者文字。有三种编码方案,UTF-8、UTF-16和UTF32。最为常用的UTF-8编码

  • UTF-8编码:可以用来表示Unicode标准中任意字符,它是电子邮件、网页及其他存储或传送文字的应用 中,优先采用的编码。互联网工程工作小组(IETF)要求所有互联网协议都必须支持UTF-8编码。它使用一至四个字节为每个字符编码 编码规则: 128个US-ASCII字符,只需一个字节编码 拉丁文等字符,需要二个字节编码 大部分常用字(含中文),使用三个字节编码 其他极少使用的Unicode辅助字符,使用四字节编码 小结:采用何种规则编码,就要采用对应规则解码,否则就会出现乱码

字符串中的编码解码问题

编码:

  • byte[] getBytes():使用平台的默认字符集将该 String编码为一系列字节,将结果存储到新的字节数组中

byte[] getBytes(String charsetName):使用指定的字符集将该 String编码为一系列字节,将结果存储到新的字节数组中 解码:

  • String(byte[] bytes):通过使用平台的默认字符集解码指定的字节数组来构造新的 String

  • String(byte[] bytes, String charsetName):通过指定的字符集解码指定的字节数组来构造新的 String

  •  package com.itheima_01;
     ​
     import java.io.UnsupportedEncodingException;
     import java.util.Arrays;
     ​
     /*
         编码:
             byte[] getBytes():使用平台的默认字符集将该 String编码为一系列字节,将结果存储到新的字节数组中
             byte[] getBytes(String charsetName):使用指定的字符集将该 String编码为一系列字节,将结果存储到新的字节数组中
     ​
         解码:
             String(byte[] bytes):通过使用平台的默认字符集解码指定的字节数组来构造新的 String
             String(byte[] bytes, String charsetName):通过指定的字符集解码指定的字节数组来构造新的 String
      */
     public class StringDemo {
         public static void main(String[] args) throws UnsupportedEncodingException {
             //定义一个字符串
             String s = "中国";
     ​
             //byte[] getBytes():使用平台的默认字符集将该 String编码为一系列字节,将结果存储到新的字节数组中
             //byte[] bys = s.getBytes(); //[-28, -72, -83, -27, -101, -67]
             //byte[] getBytes(String charsetName):使用指定的字符集将该 String编码为一系列字节,将结果存储到新的字节数组中
     //        byte[] bys = s.getBytes("UTF-8"); //[-28, -72, -83, -27, -101, -67]
             byte[] bys = s.getBytes("GBK"); //[-42, -48, -71, -6]
             System.out.println(Arrays.toString(bys));
     ​
             //String(byte[] bytes):通过使用平台的默认字符集解码指定的字节数组来构造新的 String
     //        String ss = new String(bys);
             //String(byte[] bytes, String charsetName):通过指定的字符集解码指定的字节数组来构造新的 String
     //        String ss = new String(bys,"UTF-8");
             String ss = new String(bys,"GBK");
             System.out.println(ss);
         }
     }

字符流中的编码解码问题

字符流抽象基类

  • Reader:字符输入流的抽象类

  • Writer:字符输出流的抽象类

字符流中和编码解码问题相关的两个类:

  • InputStreamReader

  • OutputStreamWriter

字符流读写数据的方式

方法名 说明
void write(int c) 写一个字符
void write(char[] cbuf) 写入一个字符数组
void write(char[] cbuf, int off, int len) 写入字符数组的一部分
void write(String str) 写一个字符串
void write(String str, int off, int len) 写一个字符串的一部分
flush() 刷新流,还可以继续写数据
close() 关闭流,释放资源,但是在关闭之前会先刷新流。一旦关闭,就不能再写数据
int read() 一次读一个字符数据
int read(char[] cbuf) 一次读一个字符数组数据

字符缓冲流

字符缓冲流:

  • BufferedWriter:将文本写入字符输出流,缓冲字符,以提供单个字符,数组和字符串的高效写入,可以指定缓冲区大小,或者可以接受默认大小。默认值足够大,可用于大多数用途

  • BufferedReader:从字符输入流读取文本,缓冲字符,以提供字符,数组和行的高效读取,可以指定缓冲区大小,或者可以使用默认大小。 默认值足够大,可用于大多数用途

构造方法:

  • BufferedWriter(Writer out)

  • BufferedReader(Reader in)

字符缓冲流特有功能

BufferedWriter:

  • void newLine():写一行行分隔符,行分隔符字符串由系统属性定义

BufferedReader:

  • public String readLine() :读一行文字。 结果包含行的内容的字符串,不包括任何行终止字符,如果流的结尾已经到达,则为null

IO小结

案例

字节流复制视频

 package com.itheima_03;
 ​
 import java.io.*;
 ​
 /*
     需求:
         把E:\\itcast\\字节流复制图片.avi 复制到模块目录下的 字节流复制图片.avi
 ​
     思路:
         1:根据数据源创建字节输入流对象
         2:根据目的地创建字节输出流对象
         3:读写数据,复制图片(一次读取一个字节数组,一次写入一个字节数组)
         4:释放资源
 ​
     四种方式实现复制视频,并记录每种方式复制视频的时间
         1:基本字节流一次读写一个字节             共耗时:64565毫秒
         2:基本字节流一次读写一个字节数组          共耗时:107毫秒
         3:字节缓冲流一次读写一个字节             共耗时:405毫秒
         4:字节缓冲流一次读写一个字节数组          共耗时:60毫秒
  */
 public class CopyAviDemo {
     public static void main(String[] args) throws IOException {
         //记录开始时间
         long startTime = System.currentTimeMillis();
 ​
         //复制视频
 //        method1();
 //        method2();
 //        method3();
         method4();
 ​
         //记录结束时间
         long endTime = System.currentTimeMillis();
         System.out.println("共耗时:" + (endTime - startTime) + "毫秒");
     }
 ​
     //字节缓冲流一次读写一个字节数组
     public static void method4() throws IOException {
         BufferedInputStream bis = new BufferedInputStream(new FileInputStream("E:\\itcast\\字节流复制图片.avi"));
         BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("myByteStream\\字节流复制图片.avi"));
 ​
         byte[] bys = new byte[1024];
         int len;
         while ((len=bis.read(bys))!=-1) {
             bos.write(bys,0,len);
         }
 ​
         bos.close();
         bis.close();
     }
 ​
     //字节缓冲流一次读写一个字节
     public static void method3() throws IOException {
         BufferedInputStream bis = new BufferedInputStream(new FileInputStream("E:\\itcast\\字节流复制图片.avi"));
         BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("myByteStream\\字节流复制图片.avi"));
 ​
         int by;
         while ((by=bis.read())!=-1) {
             bos.write(by);
         }
 ​
         bos.close();
         bis.close();
     }
 ​
 ​
     //基本字节流一次读写一个字节数组
     public static void method2() throws IOException {
         //E:\\itcast\\字节流复制图片.avi
         //模块目录下的 字节流复制图片.avi
         FileInputStream fis = new FileInputStream("E:\\itcast\\字节流复制图片.avi");
         FileOutputStream fos = new FileOutputStream("myByteStream\\字节流复制图片.avi");
 ​
         byte[] bys = new byte[1024];
         int len;
         while ((len=fis.read(bys))!=-1) {
             fos.write(bys,0,len);
         }
 ​
         fos.close();
         fis.close();
     }
 ​
     //基本字节流一次读写一个字节
     public static void method1() throws IOException {
         //E:\\itcast\\字节流复制图片.avi
         //模块目录下的 字节流复制图片.avi
         FileInputStream fis = new FileInputStream("E:\\itcast\\字节流复制图片.avi");
         FileOutputStream fos = new FileOutputStream("myByteStream\\字节流复制图片.avi");
 ​
         int by;
         while ((by=fis.read())!=-1) {
             fos.write(by);
         }
 ​
         fos.close();
         fis.close();
     }
 }

复制Java文件

 package com.itheima_03;
 ​
 import java.io.*;
 ​
 /*
     需求:
         把模块目录下的ConversionStreamDemo.java 复制到模块目录下的 Copy.java
 ​
     思路:
         1:根据数据源创建字符输入流对象
         2:根据目的地创建字符输出流对象
         3:读写数据,复制文件
         4:释放资源
  */
 public class CopyJavaDemo01 {
     public static void main(String[] args) throws IOException {
         //根据数据源创建字符输入流对象
         InputStreamReader isr = new InputStreamReader(new FileInputStream("myCharStream\\ConversionStreamDemo.java"));
         //根据目的地创建字符输出流对象
         OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("myCharStream\\Copy.java"));
 ​
         //读写数据,复制文件
         //一次读写一个字符数据
 //        int ch;
 //        while ((ch=isr.read())!=-1) {
 //            osw.write(ch);
 //        }
 ​
         //一次读写一个字符数组数据
         char[] chs = new char[1024];
         int len;
         while ((len=isr.read(chs))!=-1) {
             osw.write(chs,0,len);
         }
 ​
         //释放资源
         osw.close();
         isr.close();
     }
 }

复制Java文件(改进版)

 package com.itheima_03;
 ​
 import java.io.FileReader;
 import java.io.FileWriter;
 import java.io.IOException;
 ​
 /*
     需求:
         把模块目录下的ConversionStreamDemo.java 复制到模块目录下的 Copy.java
 ​
     数据源和目的地的分析
         数据源:myCharStream\\ConversionStreamDemo.java --- 读数据 --- Reader --- InputStreamReader --- FileReader
         目的地: myCharStream\\ Copy.java --- 写数据 --- Writer --- OutputStreamWriter --- FileWriter
 ​
     思路:
         1:根据数据源创建字符输入流对象
         2:根据目的地创建字符输出流对象
         3:读写数据,复制文件
         4:释放资源
  */
 public class CopyJavaDemo02 {
     public static void main(String[] args) throws IOException {
         //根据数据源创建字符输入流对象
         FileReader fr = new FileReader("myCharStream\\ConversionStreamDemo.java");
         //根据目的地创建字符输出流对象
         FileWriter fw = new FileWriter("myCharStream\\Copy.java");
 ​
         //读写数据,复制文件
 //        int ch;
 //        while ((ch=fr.read())!=-1) {
 //            fw.write(ch);
 //        }
 ​
         char[] chs = new char[1024];
         int len;
         while ((len=fr.read(chs))!=-1) {
             fw.write(chs,0,len);
         }
 ​
         //释放资源
         fw.close();
         fr.close();
     }
 }

复制Java文件(字符缓冲流改进版)

 package com.itheima_04;
 ​
 import java.io.*;
 ​
 /*
     需求:
         把模块目录下的ConversionStreamDemo.java 复制到模块目录下的 Copy.java
 ​
     思路:
         1:根据数据源创建字符缓冲输入流对象
         2:根据目的地创建字符缓冲输出流对象
         3:读写数据,复制文件
         4:释放资源
  */
 public class CopyJavaDemo01 {
     public static void main(String[] args) throws IOException {
         //根据数据源创建字符缓冲输入流对象
         BufferedReader br = new BufferedReader(new FileReader("myCharStream\\ConversionStreamDemo.java"));
         //根据目的地创建字符缓冲输出流对象
         BufferedWriter bw = new BufferedWriter(new FileWriter("myCharStream\\Copy.java"));
 ​
         //读写数据,复制文件
         //一次读写一个字符数据
 //        int ch;
 //        while ((ch=br.read())!=-1) {
 //            bw.write(ch);
 //        }
 ​
         //一次读写一个字符数组数据
         char[] chs = new char[1024];
         int len;
         while ((len=br.read(chs))!=-1) {
             bw.write(chs,0,len);
         }
 ​
         //释放资源
         bw.close();
         br.close();
     }
 }

复制Java文件(字符缓冲流特有功能改进版)

 package com.itheima_04;
 ​
 import java.io.*;
 ​
 /*
     需求:
         把模块目录下的ConversionStreamDemo.java 复制到模块目录下的 Copy.java
 ​
     思路:
         1:根据数据源创建字符缓冲输入流对象
         2:根据目的地创建字符缓冲输出流对象
         3:读写数据,复制文件
             使用字符缓冲流特有功能实现
         4:释放资源
  */
 public class CopyJavaDemo02 {
     public static void main(String[] args) throws IOException {
         //根据数据源创建字符缓冲输入流对象
         BufferedReader br = new BufferedReader(new FileReader("myCharStream\\ConversionStreamDemo.java"));
         //根据目的地创建字符缓冲输出流对象
         BufferedWriter bw = new BufferedWriter(new FileWriter("myCharStream\\Copy.java"));
 ​
         //读写数据,复制文件
         //使用字符缓冲流特有功能实现
         String line;
         while ((line=br.readLine())!=null) {
             bw.write(line);
             bw.newLine();
             bw.flush();
         }
 ​
         //释放资源
         bw.close();
         br.close();
     }
 }

集合到文件

 package com.itheima_05;
 ​
 import java.io.BufferedWriter;
 import java.io.FileWriter;
 import java.io.IOException;
 import java.util.ArrayList;
 ​
 /*
     需求:
         把ArrayList集合中的字符串数据写入到文本文件。要求:每一个字符串元素作为文件中的一行数据
 ​
     思路:
         1:创建ArrayList集合
         2:往集合中存储字符串元素
         3:创建字符缓冲输出流对象
         4:遍历集合,得到每一个字符串数据
         5:调用字符缓冲输出流对象的方法写数据
         6:释放资源
  */
 public class ArrayListToTxtDemo {
     public static void main(String[] args) throws IOException {
         //创建ArrayList集合
         ArrayList<String> array = new ArrayList<String>();
 ​
         //往集合中存储字符串元素
         array.add("hello");
         array.add("world");
         array.add("java");
 ​
         //创建字符缓冲输出流对象
         BufferedWriter bw = new BufferedWriter(new FileWriter("myCharStream\\array.txt"));
 ​
         //遍历集合,得到每一个字符串数据
         for(String s : array) {
             //调用字符缓冲输出流对象的方法写数据
             bw.write(s);
             bw.newLine();
             bw.flush();
         }
 ​
         //释放资源
         bw.close();
     }
 }

文件到集合

 package com.itheima_05;
 ​
 import java.io.BufferedReader;
 import java.io.FileReader;
 import java.io.IOException;
 import java.util.ArrayList;
 ​
 /*
     需求:
         把文本文件中的数据读取到集合中,并遍历集合。要求:文件中每一行数据是一个集合元素
 ​
     思路:
         1:创建字符缓冲输入流对象
         2:创建ArrayList集合对象
         3:调用字符缓冲输入流对象的方法读数据
         4:把读取到的字符串数据存储到集合中
         5:释放资源
         6:遍历集合
  */
 public class TxtToArrayListDemo {
     public static void main(String[] args) throws IOException {
         //创建字符缓冲输入流对象
         BufferedReader br = new BufferedReader(new FileReader("myCharStream\\array.txt"));
 ​
         //创建ArrayList集合对象
         ArrayList<String> array = new ArrayList<String>();
 ​
         //调用字符缓冲输入流对象的方法读数据
         String line;
         while ((line=br.readLine())!=null) {
             //把读取到的字符串数据存储到集合中
             array.add(line);
         }
 ​
         //释放资源
         br.close();
 ​
         //遍历集合
         for(String s : array) {
             System.out.println(s);
         }
     }
 }

点名器

 package com.itheima_05;
 ​
 import java.io.BufferedReader;
 import java.io.FileReader;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Random;
 ​
 /*
     需求:
         我有一个文件里面存储了班级同学的姓名,每一个姓名占一行,要求通过程序实现随点名器
 ​
     思路:
         1:创建字符缓冲输入流对象
         2:创建ArrayList集合对象
         3:调用字符缓冲输入流对象的方法读数据
         4:把读取到的字符串数据存储到集合中
         5:释放资源
         6:使用Random产生一个随机数,随机数的范围在:[0,集合的长度)
         7:把第6步产生的随机数作为索引到ArrayList集合中获取值
         8:把第7步得到的数据输出在控制台
  */
 public class CallNameDemo {
     public static void main(String[] args) throws IOException {
         //创建字符缓冲输入流对象
         BufferedReader br = new BufferedReader(new FileReader("myCharStream\\names.txt"));
 ​
         //创建ArrayList集合对象
         ArrayList<String> array = new ArrayList<String>();
 ​
         //调用字符缓冲输入流对象的方法读数据
         String line;
         while ((line=br.readLine())!=null) {
             //把读取到的字符串数据存储到集合中
             array.add(line);
         }
 ​
         //释放资源
         br.close();
 ​
         //使用Random产生一个随机数,随机数的范围在:[0,集合的长度)
         Random r = new Random();
         int index = r.nextInt(array.size());
 ​
         //把第6步产生的随机数作为索引到ArrayList集合中获取值
         String name = array.get(index);
 ​
         //把第7步得到的数据输出在控制台
         System.out.println("幸运者是:" + name);
     }
 }

集合到文件(改进版)

 package com.itheima_06;
 ​
 import java.io.BufferedWriter;
 import java.io.FileWriter;
 import java.io.IOException;
 import java.util.ArrayList;
 ​
 /*
     需求:
         把ArrayList集合中的学生数据写入到文本文件。要求:每一个学生对象的数据作为文件中的一行数据
         格式:学号,姓名,年龄,居住地    举例:itheima001,林青霞,30,西安
 ​
     思路:
         1:定义学生类
         2:创建ArrayList集合
         3:创建学生对象
         4:把学生对象添加到集合中
         5:创建字符缓冲输出流对象
         6:遍历集合,得到每一个学生对象
         7:把学生对象的数据拼接成指定格式的字符串
         8:调用字符缓冲输出流对象的方法写数据
         9:释放资源
  */
 public class ArrayListToFileDemo {
     public static void main(String[] args) throws IOException {
         //创建ArrayList集合
         ArrayList<Student> array = new ArrayList<Student>();
 ​
         //创建学生对象
         Student s1 = new Student("itheima001", "林青霞", 30, "西安");
         Student s2 = new Student("itheima002", "张曼玉", 35, "武汉");
         Student s3 = new Student("itheima003", "王祖贤", 33, "郑州");
 ​
         //把学生对象添加到集合中
         array.add(s1);
         array.add(s2);
         array.add(s3);
 ​
         //创建字符缓冲输出流对象
         BufferedWriter bw = new BufferedWriter(new FileWriter("myCharStream\\students.txt"));
 ​
         //遍历集合,得到每一个学生对象
         for (Student s : array) {
             //把学生对象的数据拼接成指定格式的字符串
             StringBuilder sb = new StringBuilder();
             sb.append(s.getSid()).append(",").append(s.getName()).append(",").append(s.getAge()).append(",").append(s.getAddress());
 ​
             //调用字符缓冲输出流对象的方法写数据
             bw.write(sb.toString());
             bw.newLine();
             bw.flush();
         }
 ​
         //释放资源
         bw.close();
     }
 }

文件到集合(改进版)

 package com.itheima_06;
 ​
 import java.io.BufferedReader;
 import java.io.FileReader;
 import java.io.IOException;
 import java.util.ArrayList;
 ​
 /*
     需求:把文本文件中的数据读取到集合中,并遍历集合。要求:文件中每一行数据是一个学生对象的成员变量值
           举例:itheima001,林青霞,30,西安
 ​
     思路:
         1:定义学生类
         2:创建字符缓冲输入流对象
         3:创建ArrayList集合对象
         4:调用字符缓冲输入流对象的方法读数据
         5:把读取到的字符串数据用split()进行分割,得到一个字符串数组
         6:创建学生对象
         7:把字符串数组中的每一个元素取出来对应的赋值给学生对象的成员变量值
         8:把学生对象添加到集合
         9:释放资源
         10:遍历集合
  */
 public class FileToArrayListDemo {
     public static void main(String[] args) throws IOException {
         //创建字符缓冲输入流对象
         BufferedReader br = new BufferedReader(new FileReader("myCharStream\\students.txt"));
 ​
         //创建ArrayList集合对象
         ArrayList<Student> array = new ArrayList<Student>();
 ​
         //调用字符缓冲输入流对象的方法读数据
         String line;
         while ((line = br.readLine()) != null) {
             //把读取到的字符串数据用split()进行分割,得到一个字符串数组
             String[] strArray = line.split(",");
 ​
             //创建学生对象
             Student s = new Student();
             //把字符串数组中的每一个元素取出来对应的赋值给学生对象的成员变量值
             //itheima001,林青霞,30,西安
             s.setSid(strArray[0]);
             s.setName(strArray[1]);
             s.setAge(Integer.parseInt(strArray[2]));
             s.setAddress(strArray[3]);
 ​
             //把学生对象添加到集合
             array.add(s);
         }
 ​
         //释放资源
         br.close();
 ​
         //遍历集合
         for (Student s : array) {
             System.out.println(s.getSid() + "," + s.getName() + "," + s.getAge() + "," + s.getAddress());
         }
     }
 }
上一篇:简析数据中心三大Overlay技术


下一篇:SD-WAN和IP网络演进探讨