本博客的内容蕴含了字节流,缓冲数组的概念。次要是钻研为什么write(bytes)要放在read(bytes)的while循环里,及为啥要一边读,一遍写,而不是读完了(输出),再写(输入)。
明天学习到TCP时,应用Client向Server传入数据,其实这就是一种变相的复制文件
,只是输入的目的地是服务器罢了。
上面是服务端的代码,没有任何问题。
public class Server { public static void main(String[] args) throws IOException { //1.创立serverSocket对象,传入端口号 ServerSocket serverSocket = new ServerSocket(8888); //2.用serverSocket接管,客户端申请的socket对象 Socket socket = serverSocket.accept(); //3.应用socket对象来获取网络输出流对象,用来读取客户端输入的数据 InputStream is = socket.getInputStream(); //如果文件夹不存在,则创立一个 File file = new File("F:\\FileUpdate"); if (!file.exists()){ file.mkdirs(); } //4.创立本地输入流对象 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"); //5.利用socket对象,获取网络输入流对象 OutputStream os = socket.getOutputStream(); //6.向客户端发送数据 if (bytes!=null){ os.write("你好,你发送的数据曾经保留胜利".getBytes()); }else { os.write("操作异样,请再次传输数据".getBytes()); } //7.开释资源 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里呈现了谬误:
while((len = fis.read(bytes))!=-1){ System.out.println("传输中"); } os.write(bytes);
对,没错就是明天题目,应用字节流复制文件时,为什么write(bytes)要放在read(bytes)的while循环里
,通过API的介绍咱们晓得了咱们形参bytes是一个缓冲数组,所谓缓冲数组,就是临时放进,然而在再次调用会从新赋值
。
于是为了一探到底我就去看了源码,然而发现他的底层是一个native申明的函数,看不见,native申明的函数是其余语言编写的,如C++,然而我想到了一个办法,怎么钻研缓冲数组是否会被从新赋值
.
就是通过,每次输入缓冲数组的第一个字节
:代码如下:
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缓冲数组第一个字节-91缓冲数组第一个字节7缓冲数组第一个字节117缓冲数组第一个字节-86缓冲数组第一个字节-55缓冲数组第一个字节-110
不言而喻,咱们的思路是对的,缓冲数组,确实会在每次被调用时,被从新赋值
。
所以也就阐明了为啥write(bytes)要放在read(bytes)的while循环里,及为啥要一边读,一遍写。
因为缓冲数组,会被从新赋值,如果把write()放在read()办法的while循环里面,会造成数组越界的异样。下面的例子也会抛出异样,因为write()写数据时,写完那1024个字节的数组,就没了,缓冲数组为空。
上面是客户端向服务器传入数据的正确代码:
客户端
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 { //1.创立本地文件输出流,读取本地的文件 FileInputStream fis = new FileInputStream("C:\\Users\\asusc\\Pictures\\Saved Pictures\\dog.jpg"); //2.创立socket对象,传入ip和端口号 Socket socket = new Socket("localhost",8888); //装被读取文件的字节数组 byte[] bytes = new byte[1024]; int len = 0; //3.获取网络输入流的对象,向服务端写入数据 OutputStream os = socket.getOutputStream(); while((len = fis.read(bytes))!=-1){ System.out.println("传输中"); os.write(bytes); } //4.获取网络输出流的对象,用于读取服务器输出的数据 InputStream is = socket.getInputStream(); byte[] bytes1 = new byte[1024]; int len1 = is.read(bytes); System.out.println(new String(bytes1,0,len)); //5.开释资源 fis.close(); socket.close(); }}
服务端
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 { //1.创立serverSocket对象,传入端口号 ServerSocket serverSocket = new ServerSocket(8888); //2.用serverSocket接管,客户端申请的socket对象 Socket socket = serverSocket.accept(); //3.应用socket对象来获取网络输出流对象,用来读取客户端输入的数据 InputStream is = socket.getInputStream(); //如果文件夹不存在,则创立一个 File file = new File("F:\\FileUpdate"); if (!file.exists()){ file.mkdirs(); } //4.创立本地输入流对象 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"); //5.利用socket对象,获取网络输入流对象 OutputStream os = socket.getOutputStream(); //6.向客户端发送数据 if (bytes!=null){ os.write("你好,你发送的数据曾经保留胜利".getBytes()); }else { os.write("操作异样,请再次传输数据".getBytes()); } //7.开释资源 fos.close(); serverSocket.close(); socket.close(); }}
好了,博客到当初也就写完了,心愿能帮忙到遇到此问题的你。