连接丢失后恢复文件上传/下载(套接字编程)

我正在编写一个程序,使用套接字编程在客户端和服务器之间下载/上传文件。 我写的代码到目前为止,我可以成功地传输文件。 但是,如果在发生下载/上传时由于网络/客户端/服务器中的问题导致连接失败..我需要从原始点恢复下载/上传(不希望重新发送最初发送的数据)。 我不知道该怎么做。 我正在将文件读入字节数组并通过网络发送。 我最初的想法是,每次我下载..我应检查文件是否已存在并将数据读入字节数组 – >将数据发送到服务器进行比较,然后通过服务器文件返回剩余数据比较两个字节数组。 但这似乎效率低下并且不再重新开始下载(因为我再次发送数据)。 注意:文件名是唯一标识符。 如果有人能就我应该如何实现文件恢复function给我建议,我真的很感激?

Server side code: package servers; import java.io.*; import java.net.*; import java.util.Arrays; public class tcpserver1 extends Thread { public static void main(String args[]) throws Exception { ServerSocket welcomeSocket = null; try { welcomeSocket = new ServerSocket(5555); while(true) { Socket socketConnection = welcomeSocket.accept(); System.out.println("Server passing off to thread"); tcprunnable tcprunthread = new tcprunnable(socketConnection); Thread thrd = new Thread(tcprunthread); thrd.start(); System.out.println(thrd.getName()); } } catch(IOException e){ welcomeSocket.close(); System.out.println("Could not connect..."); } } } class tcprunnable implements Runnable { Socket socke; public tcprunnable(Socket sc){ socke = sc; } public void download_server(String file_name) { System.out.println("Inside server download method"); try { System.out.println("Socket port:" + socke.getPort()); //System.out.println("Inside download method of thread:clientsentence is:"+clientSentence); // Create & attach output stream to new socket OutputStream outToClient = socke.getOutputStream(); // The file name needs to come from the client which will be put in here below File myfile = new File("D:\\ "+file_name); byte[] mybytearray = new byte[(int) myfile.length()]; BufferedInputStream bis = new BufferedInputStream(new FileInputStream(myfile)); bis.read(mybytearray, 0, mybytearray.length); outToClient.write(mybytearray, 0, mybytearray.length); System.out.println("Arrays on server:"+Arrays.toString(mybytearray)); outToClient.flush(); bis.close(); } catch(FileNotFoundException f){f.printStackTrace();} catch(IOException ie){ ie.printStackTrace(); } } public void upload_server(String file_name){ try{ byte[] mybytearray = new byte[1024]; InputStream is = socke.getInputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); FileOutputStream fos = new FileOutputStream("D:\\ "+file_name); BufferedOutputStream bos = new BufferedOutputStream(fos); int bytesRead = is.read(mybytearray, 0, mybytearray.length); bos.write(mybytearray, 0, bytesRead); do { baos.write(mybytearray); bytesRead = is.read(mybytearray); } while (bytesRead != -1); bos.write(baos.toByteArray()); System.out.println("Array on server while downloading:"+Arrays.toString(baos.toByteArray())); bos.close(); } catch(FileNotFoundException fe){fe.printStackTrace();} catch(IOException ie){ie.printStackTrace();} } @Override public void run() { try { System.out.println("Server1 up and running" + socke.getPort()); // Create & attach input stream to new socket BufferedReader inFromClient = new BufferedReader (new InputStreamReader(socke.getInputStream())); // Read from socket String clientSentence = inFromClient.readLine(); String file_name = inFromClient.readLine(); System.out.println("Sever side filename:" + file_name); try{ if(clientSentence.equals("download")) { download_server(file_name); } else if(clientSentence.equals("upload")) { upload_server(file_name); System.out.println("Sever side filename:" + file_name); } else { System.out.println("Invalid input"); } } catch(NullPointerException npe){ System.out.println("Invalid input!"); } socke.close(); } catch(IOException e) { e.printStackTrace(); System.out.println("Exception caught"); } } } 

客户端代码:

 package clients; import java.io.*; import java.net.*; import java.util.Arrays; public class tcpclient1 { public static void main (String args[]) throws Exception { // Create input stream to send sentence to server BufferedReader inFromUser = new BufferedReader(new InputStreamReader(System.in)); Socket clientSocket = null; while(true){ System.out.println("Please enter the server you want to use"); System.out.println("Enter 1 for Server 1 and 2 for Server2"); String server_choice = inFromUser.readLine(); if(server_choice.equals("1")){ // Create client socket to connect to server // The server to use will be specified by the user clientSocket = new Socket("localhost",5555); break; } else if(server_choice.equals("2")) { clientSocket = new Socket("localhost",5556); break; } else { System.out.println("Invalid entry"); } } System.out.println("Please enter download for dowloading"); System.out.println("Please enter upload for uploading"); // sentence is what'll be received from input jsp String sentence = inFromUser.readLine(); if(sentence.equals("download")) { download_client(clientSocket,sentence); } else if(sentence.equals("upload")) { upload_client(clientSocket,sentence); } else { System.out.println("Invalid input"); } clientSocket.close(); } public static void download_client(Socket clientSocket , String sentence) { try{ // Create output stream attached to socket DataOutputStream outToServer = new DataOutputStream(clientSocket.getOutputStream()); // Send line to server outToServer.writeBytes(sentence+'\n'); BufferedReader inFromUser = new BufferedReader(new InputStreamReader(System.in)); System.out.println("Enter the name of file to download:"); String file_to_download = inFromUser.readLine(); if(searching(file_to_download)) { // Read local file and send that to the server for comparison // DONT THINK THIS IS THE RIGHT WAY TO GO ABOUT THINGS SINCE IT BEATS THE PURPOSE OF RESUMING A DOWNLOAD/UPLOAD } // Send filetodownload to server outToServer.writeBytes(file_to_download+'\n'); byte[] mybytearray = new byte[1024]; InputStream is = clientSocket.getInputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); FileOutputStream fos = new FileOutputStream("E:\\ "+file_to_download); BufferedOutputStream bos = new BufferedOutputStream(fos); int bytesRead = is.read(mybytearray, 0, mybytearray.length); bos.write(mybytearray, 0, bytesRead); do { baos.write(mybytearray); bytesRead = is.read(mybytearray); } while (bytesRead != -1); bos.write(baos.toByteArray()); System.out.println("Array on client while downloading:"+Arrays.toString(baos.toByteArray())); bos.close(); } catch(FileNotFoundException fe){fe.printStackTrace();} catch(IOException ie){ie.printStackTrace();} } public static void upload_client(Socket clientSocket, String sentence) { try{ // Create output stream attached to socket DataOutputStream outToServer1 = new DataOutputStream(clientSocket.getOutputStream()); // Send line to server outToServer1.writeBytes(sentence+'\n'); System.out.println("In the client upload method"); BufferedReader inFromUser = new BufferedReader(new InputStreamReader(System.in)); System.out.println("Enter the name of file to upload:"); String file_to_upload = inFromUser.readLine(); //System.out.println("Cline side file name:"+file_to_upload); outToServer1.writeBytes(file_to_upload+'\n'); System.out.println(file_to_upload); OutputStream outtoserver = clientSocket.getOutputStream(); File myfile = new File("E:\\ "+file_to_upload); byte[] mybytearray = new byte[(int) myfile.length()]; BufferedInputStream bis = new BufferedInputStream(new FileInputStream(myfile)); bis.read(mybytearray, 0, mybytearray.length); outtoserver.write(mybytearray, 0, mybytearray.length); System.out.println("filename:"+file_to_upload+"Arrays on client while uploading:"+Arrays.toString(mybytearray)); outtoserver.flush(); bis.close(); } catch(FileNotFoundException fe){fe.printStackTrace();} catch(IOException ie){ie.printStackTrace();} } public static boolean searching(String file_name) { String file_path = "E:\\ "+file_name; File f = new File(file_path); if(f.exists() && !f.isDirectory()) { return true; } else return false; } } 

上面的代码可以很好地在客户端和服务器之间传输文件。
再次,非常感谢任何帮助!

您可以通过多种方式执行此操作,我建议您为服务器创建一个单独的请求类型,该请求接受文件的名称和文件位置,该位置是文件中连接失败的位置。

这就是你如何从客户端的服务器获取文件:

 int filePosition = 0; InputStream is = clientSocket.getInputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); do { baos.write(mybytearray); bytesRead = is.read(mybytearray); if(bytesRead != -1) filePosition += bytesRead; } while (bytesRead != -1); 

现在,如果由于某种原因连接中断,您可以使用相同的文件名和filePosition再次向服务器发送请求,服务器将发回文件,如下所示:

 OutputStream outToClient = socke.getOutputStream(); // The file name needs to come from the client which will be put in here below File myfile = new File("D:\\ "+file_name); byte[] mybytearray = new byte[(int) myfile.length()]; BufferedInputStream bis = new BufferedInputStream(new FileInputStream(myfile)); bis.skip(filePosition) //Advance the stream to the desired location in the file bis.read(mybytearray, 0, mybytearray.length); outToClient.write(mybytearray, 0, mybytearray.length); System.out.println("Arrays on server:"+Arrays.toString(mybytearray)); outToClient.flush(); bis.close(); 

在客户端中,您可以打开文件流并在构造函数中指定append = true ,如下所示:

 FileOutputStream fos = new FileOutputStream("D:\\ "+file_name, true); 

这可能是一种方法,有更多的选择。 我还建议使用一些散列函数(例如MD5)在传输后validation文件,它为给定的输入创建唯一的标记,并且它总是为同一输入输出相同的结果,这意味着,您可以从同一文件创建标记无论是在服务器还是在客户端,如果文件真的相同,它将生成相同的标记。 由于邮票的大小相对于它自身的文件非常小并且它也是固定的,因此它可以在客户端/服务器之间发送而没有太多开销。

您可以使用以下代码生成MD5哈希:

 MessageDigest md = MessageDigest.getInstance("MD5"); try (InputStream is = Files.newInputStream(Paths.get("file.txt"))) { DigestInputStream dis = new DigestInputStream(is, md); /* Read stream to EOF as normal... */ } byte[] digest = md.digest(); 

(取自: 使用Java获取文件的MD5校验和 )

基本上,在请求下载时您应该附加有关需要跳过多少字节的信息(新下载时为0)。 您应该从已下载的文件的一部分获取此信息(读取它的大小)。 服务器应该跳过给定的字节数并发回文件的剩余部分。 客户端应将此附加到现有文件。 为了完整性检查,您可以在最后添加一些文件哈希检查,以确保您正确获取文件。