Java Socket传输文件后发送MD5校验失败,如何调整代码?
问题根源
服务端代码中使用while ((length = dis.read(bytes, 0, bytes.length)) != -1)循环读取文件内容,这个循环会持续阻塞直到客户端关闭输出流。但客户端发送完文件切片内容后,并未关闭流,而是继续发送MD5值,导致服务端一直卡在循环里,永远执行不到后续读取MD5的代码。
解决思路
在客户端发送文件切片内容前,先把切片的字节长度发送给服务端;服务端先读取这个长度,再根据长度精准读取对应字节数的文件内容,读完后就跳出循环,再去读取MD5值。
修改后的代码
客户端调整
在核心传输逻辑前,新增发送切片字节长度的代码:
File file = new File(filePath); if (file.exists()) { this.rafin = new RandomAccessFile(filePath, "r"); this.dos = new DataOutputStream(super.getOutputStream()); rafin.seek(start); // 找到位置 // 写入文件名 dos.writeUTF(file.getName()+" ."+order); // 写入文件切片个数 dos.writeInt(fileSplitNum); // 写入sendIp dos.writeUTF(localIp); // 写入文件落盘时间 dos.writeUTF(lastModifiedDate); long fileLength = end - start + 1; // 新增:发送切片的字节长度 dos.writeLong(fileLength); dos.flush(); log.info("文件名为:"+file.getName()+" ."+order); byte[] bytes = new byte[1024 * 8]; // 计算循环 long times = fileLength / bytes.length; int length; long start = System.currentTimeMillis(); // ======================核心传输逻辑========================== MessageDigest md5 = MessageDigest.getInstance("MD5"); for (int i = 0;i < times; i++) { length = rafin.read(bytes, 0, bytes.length); md5.update(bytes, 0, length); // md5计算 dos.write(bytes, 0, length); } long last = fileLength % bytes.length; if (last != 0) { length = rafin.read(bytes, 0, (int) last); md5.update(bytes, 0, (int) last); // md5 dos.write(bytes, 0, length); } // ========================核心传输逻辑结束============================== dos.flush(); String fileMd5 = DigestUtil.toHexString(md5.digest()); // 将文件的md5编码传入server dos.writeUTF(fileMd5); dos.flush(); log.info("第{}切片的md5加密密钥为:{}", order, fileMd5); long end = System.currentTimeMillis(); String file_trans_stat = dis.readUTF(); log.info("文件传输状态:{}", file_trans_stat); latch.countDown(); // latch锁减一 log.info(String.format("%s 传输完毕,用时:%.2f秒%n",Thread.currentThread().getName(),(double)(end-start)/1000)); }
服务端调整
先读取切片长度,再根据长度精准读取内容,替换原有的无限循环:
dis = new DataInputStream(socket.getInputStream()); // 文件名 String fileName = dis.readUTF(); // 文件切片个数 int fileSplitNum = dis.readInt(); // sendIp String sendIp = dis.readUTF(); // 文件落盘时间 String lastModifiedDate = dis.readUTF(); // 新增:读取切片的字节长度 long fileLength = dis.readLong(); // 计时表 ThreadLocal<StopWatch> threadLocal = new ThreadLocal<>(); threadLocal.set(new StopWatch()); threadLocal.get().start(); String fileNameTmp = fileName+".tmp"; log.info("接收文件夹为:{}", fileDir); File directory = new File(fileDir); if (!directory.exists()) { directory.mkdir(); } File file = new File(directory.getAbsolutePath() + File.separatorChar + fileNameTmp); FileOutputStream fos = new FileOutputStream(file); byte[] bytes = new byte[1024 * 8]; int length; long readTotal = 0; MessageDigest md5 = MessageDigest.getInstance("MD5"); // 根据切片长度精准读取内容,不再依赖流结束标记 while (readTotal < fileLength) { // 计算本次最多能读的字节数,避免最后一次读多 int readSize = (int) Math.min(bytes.length, fileLength - readTotal); length = dis.read(bytes, 0, readSize); fos.write(bytes, 0, length); md5.update(bytes, 0, length); fos.flush(); readTotal += length; } fos.close(); // 现在可以正常读取MD5了 String clientFileMd5 = dis.readUTF(); log.info("client传来的{}的加密密钥为:{}", fileNameTmp, clientFileMd5); String fileMd5 = DigestUtil.toHexString(md5.digest()); log.info("切片{}的md5加密密钥为: {}", fileNameTmp, fileMd5);
额外注意事项
- 客户端发送的所有数据(包括长度、内容、MD5)必须和服务端读取顺序完全一致,否则会出现数据解析错误。
- 后续交互(比如客户端等待服务端的传输状态)时,要确保流不会被提前关闭,避免通信中断。
内容的提问来源于stack exchange,提问作者Krlin Homon
相关产品推荐
相关产品推荐

