You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java客户端-服务端文件传输程序死锁排查与实现方案咨询

问题根因

你遇到的不是传统多线程锁导致的死锁,是TCP字节流无边界、两端IO阻塞等待条件不匹配导致的双向等待:

  • 服务端接收文件的循环逻辑是持续调用read()直到返回-1,而TCP流的read()只有在对端主动关闭输出流/断开连接时才会返回-1,只要连接保持、对端没关流,服务端就会一直阻塞在read调用上,根本走不到后面返回"success"响应的逻辑。
  • 你注释掉客户端读响应的代码时,程序最后会执行clientSocketOutputWriter.close()关闭输出流,服务端read拿到-1就会继续向下执行,所以看起来运行正常;但你放开读响应的代码后,客户端发完文件内容就阻塞在readLine()等服务端返回响应,服务端阻塞在read()等客户端关流,两边互相等就卡死了。

另外你现有服务端读逻辑还有个隐蔽bug:do-while循环第一次执行时会先把未赋值的空mass数组写入字节输出流,再去读数据,最终保存的文件开头会多一个无效的0字节;而且1字节的缓冲区读文件效率极低,完全没必要。

修复步骤

先解决当前的双向阻塞问题

最小改动就能让现有单文件传输+响应的逻辑跑通:

  1. 客户端发完文件内容后,不要直接关闭整个Socket,调用client.shutdownOutput()单向关闭输出流,明确告诉服务端"当前文件内容已经发完,不会再有写入数据",服务端read此时就能拿到-1,继续执行后续逻辑。
  2. 服务端写"success"响应时,末尾要加换行符,写完立刻调用flush()把缓冲区数据推出去,不然客户端的readLine()会因为读不到行结束标记一直阻塞。
  3. 修正服务端读流的顺序:先读取数据,判断读到有效字节数后再写入输出流,避免写入无效空字节,同时把缓冲区调整为4KB以上提升读写效率。
  4. 所有流、Socket资源改用try-with-resources语法自动关闭,不要手动写close逻辑,避免中途抛异常导致资源泄漏。

实现多文件传输、文件变更自动同步的合理方案

TCP本身是无边界的字节流,不会自动区分你发的内容属于第几个文件、从哪开始到哪结束,要实现长期运行、多文件传输的需求,必须自己定义简单的应用层传输边界,不要靠关流判断单文件传输结束,推荐的传输帧结构:

  • 2字节:存储后续文件名的字节长度
  • N字节(长度和前面的2字节值一致):文件名的UTF-8编码字节
  • 8字节(long类型):存储后续文件内容的总长度
  • M字节(长度和前面的8字节值一致):文件的实际内容字节
  • 1字节:服务端接收完整个文件后返回的状态标记(0代表成功,1代表失败)

按这个结构传输时,服务端每次读数据都知道自己应该读多少字节,读完立刻返回响应,不需要等连接关闭,同一个长连接可以反复传输多个文件,不会出现阻塞等待的问题。

文件变更检测直接用JDK自带的java.nio.file.WatchService实现即可,不需要自己轮询对比文件修改时间:

  • 监听目标文件所在目录的ENTRY_MODIFY事件
  • 加500ms左右的防抖逻辑:大部分编辑器保存文件时会连续触发多次modify事件,收到第一次事件后等待500ms没有新的同文件事件触发,再读取文件内容发送,避免传输写入到一半的损坏文件。
  • 客户端启动时和服务端建立一个长连接复用,不要每次传文件都新建Socket,降低连接开销。
修正后的核心代码片段

服务端核心接收逻辑

// 用try-with-resources自动释放资源
public class Server {
    public static void main(String[] args) throws IOException {
        try (ServerSocket serverSocket = new ServerSocket(5000);
             Socket client = serverSocket.accept();
             BufferedInputStream clientInputStream = new BufferedInputStream(client.getInputStream());
             BufferedWriter responseWriter = new BufferedWriter(new OutputStreamWriter(client.getOutputStream()))) {

            byte[] buffer = new byte[4096];
            ByteArrayOutputStream fileContentBuffer = new ByteArrayOutputStream();
            int bytesRead;
            // 修正读流顺序:先读再写,直到拿到流结束标记
            while ((bytesRead = clientInputStream.read(buffer)) != -1) {
                fileContentBuffer.write(buffer, 0, bytesRead);
            }

            // 写文件
            try (FileOutputStream fos = new FileOutputStream("this.txt");
                 BufferedOutputStream bos = new BufferedOutputStream(fos)) {
                bos.write(fileContentBuffer.toByteArray());
            }
            System.out.println("first file received");

            // 返回响应,加换行+flush
            responseWriter.write("success\n");
            responseWriter.flush();
        }
    }
}

客户端核心发送逻辑

public class Client {
    private final static String serverIP = "192.168.0.47";
    private final static int serverPort = 5000;
    private final static String fileOutput = "first.txt";

    public static void main(String[] args) throws IOException {
        try (Socket client = new Socket(serverIP, serverPort);
             BufferedInputStream inputFile = new BufferedInputStream(new FileInputStream(fileOutput));
             BufferedOutputStream clientSocketOutputWriter = new BufferedOutputStream(client.getOutputStream());
             BufferedReader responseReader = new BufferedReader(new InputStreamReader(client.getInputStream()))) {

            byte[] fileBytes = inputFile.readAllBytes();
            clientSocketOutputWriter.write(fileBytes);
            // 发完文件后flush缓冲区,再单向关输出流通知服务端发送结束
            clientSocketOutputWriter.flush();
            client.shutdownOutput();
            System.out.println("first file sent");

            // 读服务端响应
            String resp = responseReader.readLine();
            System.out.println("server response: " + resp);
            System.out.println("the end");
        }
    }
}

内容的提问来源于stack exchange,提问作者life_fire1

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 05:21:22