Java Socket实现文件的发送和接收功能&遇到的Bug

在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://blog.csdn.net/wzq1915414095/article/details/108185533

原因如下:“只要客户端的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();
		}
	}

以上

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值