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

Java实现简易FTP文件传输遇客户端冻结问题求技术指导

Java简易文件传输系统问题排查与资源推荐

问题描述

正在用Java实现支持客户端命令交互的简易文件传输系统,课程侧重网络概念,缺乏编码实践指导,求相关优质指南或文档。当前遇到的核心问题:文件上传/下载完成后,客户端经常冻结,无法输入新命令,推测是服务端与客户端的数据流同步异常,但无法定位具体原因。代码结构较混乱,优先希望获取现成指南或示例代码,无需逐行排查现有代码。

核心问题分析(针对现有代码)

客户端冻结的本质是数据流同步错误,主要由以下几点导致:

  1. 字符流与字节流混用:同时使用PrintWriter(字符流)和Socket.getOutputStream()(字节流),两者缓冲区独立,数据发送顺序混乱,导致一端卡在读取操作上。
  2. 文件传输结束判断逻辑错误:
    • 客户端下载时用bytesRead < 4096作为终止条件,不可靠——文件末尾可能刚好是4096字节,或网络分包导致中间块小于4096,会提前终止或无限等待。
    • 服务端接收文件时,试图在字节流中匹配字符串"226 Transfer complete.",但字节流与字符流编码不兼容,且文件内容可能包含该字符串,导致错误终止或无限等待。
  3. 无明确的传输同步信号:文件传输完成后,两端未通过可靠的信号确认状态,导致后续命令读取时拿到错误数据,卡住流程。

修复思路与实践建议

  1. 统一数据流类型:全程使用字节流传输,包括命令和文件数据,避免字符流的缓冲问题;或严格分离控制通道(字符流处理命令)与数据通道(字节流处理文件),类似FTP的双连接模式。
  2. 基于文件大小的传输终止机制:
    • 传输文件前,先发送文件的字节长度,另一端根据长度精确接收对应字节数,避免依赖read()返回-1或缓冲区大小判断结束。
  3. 明确的命令同步协议:所有命令和响应都使用固定格式,比如每行结尾用\r\n,确保readLine()能正确读取,避免数据粘连。

优质实现方向与示例参考

  • 双连接模式(推荐):
    1. 控制连接:使用BufferedReader和PrintWriter处理认证、LIST、DOWNLOAD/UPLOAD等命令。
    2. 数据连接:当需要传输文件时,服务端开启新端口通知客户端,或客户端主动连接服务端指定端口,通过字节流传输文件,传输完成后关闭数据连接,回到控制连接处理下一个命令。
  • 单连接字节流协议:所有数据(命令+文件)都通过字节流传输,命令使用固定长度的头部标识类型和长度,文件数据前先发送文件名长度、文件名、文件长度,再发送文件内容。

现有代码

客户端代码

import java.io.*;
import java.net.*;

public class FTPClient {
   
    public static void main(String[] args) {
        String serverAddress = "localhost"; 
        int portNumber = 8888; 
        

        try (
            Socket socket = new Socket(serverAddress, portNumber);
            BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
            BufferedReader stdIn = new BufferedReader(new InputStreamReader(System.in))
        ) {
            // Handle server's initial welcome message or prompt
            System.out.println(in.readLine());  // Read and display the "Please enter your username:" message

            // Send username and handle response
            String username = stdIn.readLine();
            out.println(username);
            System.out.println(in.readLine());  // Read and display the "Please enter your password:" message

            // Send password and check for authentication
            String password = stdIn.readLine();
            out.println(password);

            String serverResponse = in.readLine();
            if (!serverResponse.startsWith("230")) {
                System.out.println("Authentication failed: " + serverResponse);
                return;
            }

            System.out.println("Authentication successful. Enter commands:");

            // Handle commands
            String userInput;
            while ((userInput = stdIn.readLine()) != null) {
                if (userInput.equalsIgnoreCase("QUIT")) {
                    out.println("QUIT");
                    System.out.println("Quitting...");
                    break;
                } else if (userInput.equalsIgnoreCase("LIST")) {
                    out.println("LIST");
                    String response;
                    while (!(response = in.readLine()).equals("226 Directory listing complete")) {
                        System.out.println(response);
                    }
                } else if (userInput.startsWith("DOWNLOAD ")) {
                    String fileName = userInput.substring(9); // Extracting filename from the command
                    receiveFile(fileName, socket);
                } else if (userInput.startsWith("UPLOAD ")) {
                    String fileName = userInput.substring(7); // Extracting filename from the command
                    sendFile(fileName, socket, in);
                } else {
                    System.out.println("Server response: " + in.readLine());
                }
            }
        } catch (UnknownHostException e) {
            System.err.println("Unknown host: " + serverAddress);
        } catch (IOException e) {
            System.err.println("Error connecting to server: " + e.getMessage());
        }
    }

    private static void sendFile(String fileName, Socket socket, BufferedReader in) {
        try {
            File file = new File(fileName);
            if (file.exists()) {
                PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
                out.println("UPLOAD " + fileName);
                FileInputStream fis = new FileInputStream(file);
                BufferedInputStream bis = new BufferedInputStream(fis);

                byte[] buffer = new byte[4096];
                int bytesRead;
                while ((bytesRead = bis.read(buffer)) != -1) {
                    socket.getOutputStream().write(buffer, 0, bytesRead);
                }
                bis.close();    
                fis.close();


                out.println("226 Transfer complete.");
                System.out.println("File uploaded successfully");
               
                
            } else {
                System.out.println("File not found.");
            }
        } catch (IOException e) {
            System.err.println("Error sending file: " + e.getMessage());
        }
    }

    private static void receiveFile(String fileName, Socket socket) {
        try {
            PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
            out.println("DOWNLOAD " + fileName);
            FileOutputStream fos = new FileOutputStream(fileName);
            BufferedOutputStream bos = new BufferedOutputStream(fos);
            
            byte[] buffer = new byte[4096];
            int bytesRead;
            while ((bytesRead = socket.getInputStream().read(buffer)) != -1) {
                bos.write(buffer, 0, bytesRead);
                if (bytesRead < 4096) {
                    break;
                }
                
            }
            bos.close();
            fos.close();
            
            
            
            System.out.println("File downloaded successfully");
        } catch (IOException e) {
            System.err.println("Error receiving file: " + e.getMessage());
        }
    }
}

服务端代码

import java.io.*;
import java.net.*;
import java.nio.file.*;

public class FTPServer {
    private static final String ROOT_DIRECTORY = "C:\\Users\\pokem\\Documents\\CSCI 361\\Server Side"; // Directory where files are stored

    public static void main(String[] args) {
        int portNumber = 8888; 

        try (ServerSocket serverSocket = new ServerSocket(portNumber)) {
            System.out.println("Server started. Waiting for clients...");

            while (true) {
                Socket clientSocket = serverSocket.accept();
                System.out.println("Client connected: " + clientSocket);

                // Handle each client in a separate thread
                new ClientHandler(clientSocket).start();
            }
        } catch (IOException e) {
            System.err.println("Error in server: " + e.getMessage());
        }
    }

    static class ClientHandler extends Thread {
        private Socket clientSocket;
        private PrintWriter out;
        private BufferedReader in;
        private boolean authenticated = false;

        public ClientHandler(Socket socket) {
            this.clientSocket = socket;
            try {
                out = new PrintWriter(clientSocket.getOutputStream(), true);
                in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void run() {
            try {
                // Authentication
                authenticate();
                if (!authenticated) {
                    clientSocket.close();
                    return;
                }

                // Main loop for handling client commands
                String inputLine;
                while ((inputLine = in.readLine()) != null) {
                    if (inputLine.equalsIgnoreCase("QUIT")) {
                        System.out.println("Client disconnected: " + clientSocket);
                        break;
                    } else if (inputLine.equalsIgnoreCase("LIST")) {
                        listFiles();
                    } else if (inputLine.startsWith("DOWNLOAD ")) {
                        String fileName = inputLine.substring(9); // Extracting filename from the command
                        sendFile(fileName);
                    } else if (inputLine.startsWith("UPLOAD ")) {
                        String fileName = inputLine.substring(7); // Extracting filename from the command
                        receiveFile(fileName, in);
                    } else {
                        out.println("500 Invalid command.");
                    }
                }
            } catch (IOException e) {
                System.err.println("Error handling client: " + e.getMessage());
            }
        }

        private void authenticate() throws IOException {
            out.println("Please enter your username:");
            String username = in.readLine();
            out.println("Please enter your password:");
            String password = in.readLine();
            authenticated = authenticateUser(username, password);
            if (authenticated) {
                out.println("230 Authentication successful. Welcome " + username + "!");
            } else {
                out.println("530 Authentication failed. Disconnecting...");
            }
        }

        private boolean authenticateUser(String username, String password) {
            // Simulated authentication - just a placeholder
            return username.equals("admin") && password.equals("admin123");
        }

        private void listFiles() {
            try {
                File directory = new File(ROOT_DIRECTORY);
                File[] files = directory.listFiles();
                if (files != null) {
                    for (File file : files) {
                        if (file.isFile()) {
                            out.println(file.getName());
                        }
                    }
                }
                out.println("226 Directory listing complete");
            } catch (Exception e) {
                out.println("550 Error listing directory");
            }
        }

        private void sendFile(String fileName) {
            try {
                File file = new File(ROOT_DIRECTORY + File.separator + fileName);
                if (file.exists()) {
                    FileInputStream fis = new FileInputStream(file);
                    BufferedInputStream bis = new BufferedInputStream(fis);
                    
                    byte[] buffer = new byte[4096];
                    int bytesRead;
                    while ((bytesRead = bis.read(buffer)) != -1) {
                        clientSocket.getOutputStream().write(buffer, 0, bytesRead);
                    }
                    bis.close();
                    fis.close();
                    
                    System.out.println("File sent: " + fileName);
                    out.println("226 Transfer complete.");
                    
                } else {
                    out.println("550 File not found.");
                }
            } catch (IOException e) {
                out.println("550 Error sending file");
            }
        }

        private void receiveFile(String fileName, BufferedReader in) {
            try {
                FileOutputStream fos = new FileOutputStream(fileName);
                BufferedOutputStream bos = new BufferedOutputStream(fos);

                byte[] buffer = new byte[4096];
                int bytesRead;

                while ((bytesRead = clientSocket.getInputStream().read(buffer)) != -1) {
                    bos.write(buffer, 0, bytesRead);
                    if (new String(buffer, 0, bytesRead).trim().equals("226 Transfer complete.")) {
                        break;
                    }
                }
                bos.close();
                fos.close();
                System.out.println("File received: " + fileName);
                
            } catch (IOException e) {
                out.println("550 Error receiving file");
            }
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 21:55:54