Java实现简易FTP文件传输遇客户端冻结问题求技术指导
Java简易文件传输系统问题排查与资源推荐
问题描述
正在用Java实现支持客户端命令交互的简易文件传输系统,课程侧重网络概念,缺乏编码实践指导,求相关优质指南或文档。当前遇到的核心问题:文件上传/下载完成后,客户端经常冻结,无法输入新命令,推测是服务端与客户端的数据流同步异常,但无法定位具体原因。代码结构较混乱,优先希望获取现成指南或示例代码,无需逐行排查现有代码。
核心问题分析(针对现有代码)
客户端冻结的本质是数据流同步错误,主要由以下几点导致:
- 字符流与字节流混用:同时使用
PrintWriter(字符流)和Socket.getOutputStream()(字节流),两者缓冲区独立,数据发送顺序混乱,导致一端卡在读取操作上。 - 文件传输结束判断逻辑错误:
- 客户端下载时用
bytesRead < 4096作为终止条件,不可靠——文件末尾可能刚好是4096字节,或网络分包导致中间块小于4096,会提前终止或无限等待。 - 服务端接收文件时,试图在字节流中匹配字符串"226 Transfer complete.",但字节流与字符流编码不兼容,且文件内容可能包含该字符串,导致错误终止或无限等待。
- 客户端下载时用
- 无明确的传输同步信号:文件传输完成后,两端未通过可靠的信号确认状态,导致后续命令读取时拿到错误数据,卡住流程。
修复思路与实践建议
- 统一数据流类型:全程使用字节流传输,包括命令和文件数据,避免字符流的缓冲问题;或严格分离控制通道(字符流处理命令)与数据通道(字节流处理文件),类似FTP的双连接模式。
- 基于文件大小的传输终止机制:
- 传输文件前,先发送文件的字节长度,另一端根据长度精确接收对应字节数,避免依赖
read()返回-1或缓冲区大小判断结束。
- 传输文件前,先发送文件的字节长度,另一端根据长度精确接收对应字节数,避免依赖
- 明确的命令同步协议:所有命令和响应都使用固定格式,比如每行结尾用
\r\n,确保readLine()能正确读取,避免数据粘连。
优质实现方向与示例参考
- 双连接模式(推荐):
- 控制连接:使用
BufferedReader和PrintWriter处理认证、LIST、DOWNLOAD/UPLOAD等命令。 - 数据连接:当需要传输文件时,服务端开启新端口通知客户端,或客户端主动连接服务端指定端口,通过字节流传输文件,传输完成后关闭数据连接,回到控制连接处理下一个命令。
- 控制连接:使用
- 单连接字节流协议:所有数据(命令+文件)都通过字节流传输,命令使用固定长度的头部标识类型和长度,文件数据前先发送文件名长度、文件名、文件长度,再发送文件内容。
现有代码
客户端代码
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
相关产品推荐
相关产品推荐

