如何正确使用writeUTF()方法?文件传输客户端服务端异常排查
问题根源:客户端与服务端的数据流读写顺序完全不匹配
嘿,这问题太典型了——你在客户端发送数据的顺序,和服务端接收数据的顺序完全对不上,直接导致数据流错位,所以文件名读不到,后续文件操作自然抛出FileNotFoundException。
我们先理清楚客户端PUT流程的发送顺序:
- 第一步:发送操作类型字符
'P' - 第二步:发送文件名
writeUTF(FileOrDirName) - 第三步:发送文件/目录标识(
0代表文件,1代表目录) - 第四步:如果是文件,先发送文件长度(你用了
writeDouble,其实用writeInt更合理,因为字节数组长度本身就是int类型),再逐字节发送文件内容;如果是目录,直接发送压缩后的字节数组
再看你的服务端接收顺序:
- 读取
'P'之后,直接去读double类型的长度(这时候读的其实是文件名的UTF编码头,完全错误) - 接着读字节数组(这部分是被破坏的错误数据)
- 最后才去读文件名的
UTF数据——这时候数据流已经被前面的错误读取搅乱了,自然读不到有效内容,输出空行,创建文件时当然会报错
修正后的服务端代码(PUT核心逻辑)
我帮你调整了接收顺序,完全匹配客户端的发送逻辑,还优化了资源管理:
import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.IOException; import java.net.Socket; import java.nio.file.Path; import java.nio.file.Paths; class TcpFileServerThread extends Thread { private Socket client; public TcpFileServerThread(Socket client) { this.client = client; } public void run() { // 用try-with-resources自动关闭流,避免资源泄漏 try (DataOutputStream outToClient = new DataOutputStream(client.getOutputStream()); DataInputStream dataInFromClient = new DataInputStream(client.getInputStream())) { Path currentRelativePath = Paths.get(""); String currentPath = currentRelativePath.toAbsolutePath().toString(); switch (dataInFromClient.readChar()) { case 'P': // 第一步:先读取文件名,和客户端发送顺序对应 String fileOrDirName = dataInFromClient.readUTF(); System.out.println("收到的文件名/目录名:" + fileOrDirName); // 第二步:读取文件/目录标识 int isDirFlag = dataInFromClient.readInt(); File targetFile = new File(fileOrDirName); // 字符串比较要用equals,==是比较对象引用 if (targetFile.getParent() == null || currentPath.equals(targetFile.getParent())) { if (isDirFlag == 0) { // 处理文件:先读取长度,再读取字节内容 // 注意:客户端用了writeDouble,这里暂时对应readDouble,建议后续统一改成int long fileLength = (long) dataInFromClient.readDouble(); byte[] fileBytes = new byte[(int) fileLength]; // 用readFully确保读取完整字节数组 dataInFromClient.readFully(fileBytes); FileOperations.writeFromByteArrayToFile(fileBytes, fileOrDirName); } else { // 处理目录:先读取压缩包长度(建议客户端补充发送),再读取字节 // 临时方案:先读取当前可用字节,后续建议客户端先发送压缩包长度 byte[] zipBytes = new byte[dataInFromClient.available()]; dataInFromClient.readFully(zipBytes); File zipFile = new File(fileOrDirName + ".zip"); FileOperations.writeFromByteArrayToFile(zipBytes, zipFile.getAbsolutePath()); FileOperations.unzip(zipFile); zipFile.delete(); } } break; // 后续可以补充GET、EXIT的处理逻辑 } } catch (IOException e) { e.printStackTrace(); } finally { try { client.close(); } catch (IOException e) { e.printStackTrace(); } } } }
额外优化建议
- 统一数据类型:文件长度用
int就足够(字节数组最大长度为Integer.MAX_VALUE),没必要用double,避免类型转换的潜在问题。 - 补充长度发送:发送字节数组(比如压缩后的目录)时,建议先发送数组的长度,服务端根据长度精确读取,不要依赖
available()(它只能返回当前可读取的字节数,不一定是全部)。 - 字符串比较规范:客户端里的
fileOrDir.getParent()==currentPath要改成currentPath.equals(fileOrDir.getParent()),Java中==比较字符串是对比对象引用,不是内容。 - 完善异常处理:可以在关键步骤(比如文件读写、网络操作)添加更具体的异常提示,方便排查问题。
内容的提问来源于stack exchange,提问作者Dimitry
相关产品推荐
相关产品推荐

