Java Socket实现文件发送和接收功能以及遇到的Bug问题
作者:I am Groot!
Java Socket实现文件发送和接收功能
在Java中,如何用Socket实现文件的发送和接收功能?
我的第一版代码
文件发送:
public void sendFile(String filePath) {//过长、过多的密文信息直接发送文件 File file = new File(filePath); try { DataOutputStream d_out = new DataOutputStream(socket.getOutputStream()); FileInputStream f_in = new FileInputStream(file); int all = 0; byte[] buffer = new byte[4096]; int read = 0; while ((read = (f_in.read(buffer))) > 0) { d_out.write(buffer, 0, read); all += read; } System.out.println("Send file length: "+all); d_out.flush(); f_in.close(); d_out.close();//注意这一行 } catch (IOException e) { e.printStackTrace(); } }
文件接收:
public void receiveFile(String filePath) {//接收文件 try { DataOutputStream dosOutputStream = new DataOutputStream(new FileOutputStream(filePath)); byte[] buf = new byte[4096]; int len = 0; System.out.println("开始接收文件!"); d_in = new DataInputStream(sock.getInputStream()); while((len = d_in.read(buf)) != -1) { dosOutputStream.write(buf, 0, len); } dosOutputStream.flush(); System.out.println("文件接收结束!"); //d_in.close(); dosOutputStream.close(); } catch (IOException e) { e.printStackTrace(); } }
上面的写法的确实现了文件发送和接收的功能(Socket初始化这里没有给出,大家可以自行补充这部分,是可以运行的),但是这个实现方式存在一个很致命的问题,就是只能完成一次文件的发送和接收。
在此之后如果你想再调用文件的发送和接收方法,就会遇到如下"Socket is closed"这个问题:
为什么会遇到这一问题
首先肯定是因为Socket被我关闭了,但我并没有写"socket.close();"这样的代码呀,为什么还是被关闭了呢?
我们把注意力放到上面提到的文件发送的代码上,注意这一行:
d_out.close();
这里原本的目的是把DataOutputStream给关闭掉,结束我们的文件发送输出流。
但是当我们关闭DataOutputStream时,Socket也会随之关闭,这便有了后面想再次执行sendFile方法时,出现的"Socket is closed"问题。
所以为了能连续多次地发送、接收不同的文件,这一行代码肯定是不能要了。
把这行代码注释掉,修改后的代码为
文件发送:
public void sendFile(String filePath) {//过长、过多的密文信息直接发送文件 File file = new File(filePath); try { DataOutputStream d_out = new DataOutputStream(socket.getOutputStream()); FileInputStream f_in = new FileInputStream(file); int all = 0; byte[] buffer = new byte[4096]; int read = 0; while ((read = (f_in.read(buffer))) > 0) { d_out.write(buffer, 0, read); all += read; } System.out.println("Send file length: "+all); d_out.flush(); f_in.close(); } catch (IOException e) { e.printStackTrace(); } }
文件接收:
public void receiveFile(String filePath) {//接收文件 try { DataOutputStream dosOutputStream = new DataOutputStream(new FileOutputStream(filePath)); byte[] buf = new byte[4096]; int len = 0; System.out.println("开始接收文件!"); d_in = new DataInputStream(sock.getInputStream()); while((len = d_in.read(buf)) != -1) { dosOutputStream.write(buf, 0, len); } dosOutputStream.flush(); System.out.println("文件接收结束!"); //d_in.close(); dosOutputStream.close(); } catch (IOException e) { e.printStackTrace(); } }
这样修改后又遇到了新的问题
那就是在接收文件时,发生了阻塞的现象:
控制台就一直卡在“开始接收文件!”这行输出这里,显然是因为文件接收的while循环被卡住了,但刚刚明明没有这个问题啊,为什么注释掉“sendFile”中的d_out.close();会使得“receiveFile”的while循环被卡住呢?
while((len = d_in.read(buf)) != -1) { dosOutputStream.write(buf, 0, len); }
这篇文章给出了比较好的解答:https://www.jb51.net/program/325734b3l.htm
原因如下
“只要客户端的DataOutputStream不close掉,那么服务端的DataInputStream read就永远不等于-1。即使文件的数据已经传完了,DataInputStream依旧会等着客户端DataOutputStream再传数据过来。最后只能通过判断文件的的大小来确认文件是否已经传输完成。”
这便是为什么在d_out.close();没有被注释前是没有这个Bug的:因为当我们将DataOutputStream close掉时,接收方的while循环也就结束了。
所以我们现在可以采用len的实际长度来判断是否已经传输完成,修改后的代码如下:
while((len = d_in.read(buf)) != -1) { dosOutputStream.write(buf, 0, len); if(len < buf.length) break; }
即:判断buf是否被填满,没有填满(len<buf.length)则代表已经接收完毕
经过测试,问题解决
最终代码如下:
文件发送:
public void sendFile(String filePath) {//发送文件 File file = new File(filePath); try { DataOutputStream d_out = new DataOutputStream(socket.getOutputStream()); FileInputStream f_in = new FileInputStream(file); int all = 0; byte[] buffer = new byte[4096]; int read = 0; while ((read = (f_in.read(buffer))) > 0) { d_out.write(buffer, 0, read); all += read; } System.out.println("Send file length: "+all); d_out.flush(); f_in.close(); } catch (IOException e) { e.printStackTrace(); } }
文件接收:
public void receiveFile(String filePath) {//接收文件 try { DataOutputStream dosOutputStream = new DataOutputStream(new FileOutputStream(filePath)); byte[] buf = new byte[4096]; int len = 0; System.out.println("开始接收文件!"); d_in = new DataInputStream(sock.getInputStream()); while((len = d_in.read(buf)) != -1) { dosOutputStream.write(buf, 0, len); if(len < buf.length) break; } dosOutputStream.flush(); System.out.println("文件接收结束!"); dosOutputStream.close(); } catch (IOException e) { e.printStackTrace(); } }
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。