本博客的内容包含了字节流,缓冲数组的概念。主要是研究为什么write(bytes)要放在read(bytes)的while循环里,及为啥要一边读,一遍写,而不是读完了(输入),再写(输出)。
今天学习到TCP时,使用Client向Server传入数据,其实这就是一种变相的复制文件 ,只是输出的目的地是服务器罢了。
下面是服务端的代码,没有任何问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 public class Server { public static void main (String[] args) throws IOException { ServerSocket serverSocket = new ServerSocket(8888 ); Socket socket = serverSocket.accept(); InputStream is = socket.getInputStream(); File file = new File("F:\\FileUpdate" ); if (!file.exists()){ file.mkdirs(); } FileOutputStream fos = new FileOutputStream(file+"\\dog.jpg" ); byte [] bytes = new byte [1024 ]; int len = 0 ; while ((len = is.read(bytes))!=-1 ){ System.out.println("保存中" ); fos.write(bytes); } System.out.println("wocap" ); OutputStream os = socket.getOutputStream(); if (bytes!=null ){ os.write("你好,你发送的数据已经保存成功" .getBytes()); }else { os.write("操作异常,请再次传输数据" .getBytes()); } fos.close(); serverSocket.close(); socket.close(); } }
在复制文件时,发生了点小插曲,就是服务器下载不下来文件,所以我就调试,在服务器程序中的读取文件的while循环 里打下了断点: 结果,在第二次循环时,停下了,紧接着去看照片是否复制好了,的确是由文件,因为执行了一次循环,但是为什么第二次就停止了呢?并且并没有向下执行,而是阻塞在了while循环第一句 ,我想应该是is.read(bytes)的问题,于是查看API,以下是原文:
public int read(byte[] b) throws IOException从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。以整数形式返回实际读取的字节数。在输入数据可用、检测到文件末尾或者抛出异常前, 此方法一直阻塞。 如果 b 的长度为 0,则不读取任何字节并返回 0;否则,尝试读取至少一个字节。如果因为流位于文件末尾而没有可用的字节,则返回值 -1 ;否则,至少读取一个字节并将其存储在 b 中。
将读取的第一个字节存储在元素 b[0] 中,下一个存储在 b[1] 中 ,依次类推。读取的字节数最多等于 b 的长度。设 k 为实际读取的字节数;这些字节将存储在 b[0] 到 b[k-1] 的元素中,不影响 b[k] 到 b[b.length-1] 的元素 。
通过API原文我们可以发现,阻塞在while循环第一句的原因:是没有检测到文件末尾及-1,所以程序一直被阻塞了,而没有向下执行。
那么问题来了,为啥没有我们的文件没有数据了呢,原因只有一个,那就是客户端没有把文件的字节全部输出,所以回头看,发现我们的Client里出现了错误:
1 2 3 4 while ((len = fis.read(bytes))!=-1 ){ System.out.println("传输中" ); } os.write(bytes);
对,没错就是今天标题,使用字节流复制文件时,为什么write(bytes)要放在read(bytes)的while循环里 ,通过API的介绍我们知道了我们形参bytes是一个缓冲数组,所谓缓冲数组,就是暂时放进,但是在再次调用会重新赋值 。
于是为了一探究竟我就去看了源码,但是发现他的底层是一个native声明的函数,看不见,native声明的函数是其他语言编写的,如C++,但是我想到了一个方法,怎么研究缓冲数组是否会被重新赋值 .
就是通过,每次输出缓冲数组的第一个字节 :代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class copyDemo1 { public static void main (String[] args) throws IOException { FileInputStream fis = new FileInputStream("C:\\Users\\asusc\\Pictures\\Saved Pictures\\dog.jpg" ); FileOutputStream fos = new FileOutputStream("F.jpg" ); byte [] bytes = new byte [1024 ]; int readLen= 0 ; int i =0 ; while ((readLen= fis.read(bytes))!=-1 ){ System.out.println("缓冲数组第一个字节" +bytes[0 ]); } fos.write(bytes,0 ,readLen); fos.close(); fis.close(); } }
输出结果:
1 2 3 4 5 6 7 缓冲数组第一个字节-1 缓冲数组第一个字节-91 缓冲数组第一个字节7 缓冲数组第一个字节117 缓冲数组第一个字节-86 缓冲数组第一个字节-55 缓冲数组第一个字节-110
显而易见,我们的思路是对的,缓冲数组,的确会在每次被调用时,被重新赋值 。
所以也就说明了为啥write(bytes)要放在read(bytes)的while循环里,及为啥要一边读,一遍写。 因为缓冲数组,会被重新赋值,如果把write()放在read()方法的while循环外面,会造成数组越界的异常。上面的例子也会抛出异常,因为write()写数据时,写完那1024个字节的数组,就没了,缓冲数组为空。
下面是客户端向服务器传入数据 的正确代码:
客户端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 package Net.FileUpdate;import java.io.FileInputStream;import java.io.IOException;import java.io.InputStream;import java.io.OutputStream;import java.net.Socket;public class Client { public static void main (String[] args) throws IOException { FileInputStream fis = new FileInputStream("C:\\Users\\asusc\\Pictures\\Saved Pictures\\dog.jpg" ); Socket socket = new Socket("localhost" ,8888 ); byte [] bytes = new byte [1024 ]; int len = 0 ; OutputStream os = socket.getOutputStream(); while ((len = fis.read(bytes))!=-1 ){ System.out.println("传输中" ); os.write(bytes); } InputStream is = socket.getInputStream(); byte [] bytes1 = new byte [1024 ]; int len1 = is.read(bytes); System.out.println(new String(bytes1,0 ,len)); fis.close(); socket.close(); } }
服务端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 package Net.FileUpdate;import java.io.*;import java.net.ServerSocket;import java.net.Socket;public class Server { public static void main (String[] args) throws IOException { ServerSocket serverSocket = new ServerSocket(8888 ); Socket socket = serverSocket.accept(); InputStream is = socket.getInputStream(); File file = new File("F:\\FileUpdate" ); if (!file.exists()){ file.mkdirs(); } FileOutputStream fos = new FileOutputStream(file+"\\dog.jpg" ); byte [] bytes = new byte [1024 ]; int len = 0 ; while ((len = is.read(bytes))!=-1 ){ System.out.println("保存中" ); fos.write(bytes); } System.out.println("wocap" ); OutputStream os = socket.getOutputStream(); if (bytes!=null ){ os.write("你好,你发送的数据已经保存成功" .getBytes()); }else { os.write("操作异常,请再次传输数据" .getBytes()); } fos.close(); serverSocket.close(); socket.close(); } }
好了,博客到现在也就写完了,希望能帮助到遇到此问题的你。