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



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

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);

现在,如果连接因某种原因中断,您可以使用相同的文件名和文件位置再次向服务器发送请求,服务器会像这样发回文件:

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)验证文件。例如,它为给定的输入创建唯一的戳,并且总是为相同的输入输出相同的结果,这意味着,您可以在服务器和客户端中从同一文件创建戳,如果文件真的相同,它将生成相同的戳。由于stamp的大小相对于它本身的文件来说非常小,而且它也是固定的,所以它可以在客户端/服务器之间发送,而不会有太多开销。

您可以使用以下代码生成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)。您应该从下载的文件的一部分中获取这些信息(读取其大小)。服务器应该跳过给定的字节数,并发回文件的剩余部分。客户端应将其附加到现有文件中。对于健全性检查,您可以在最后添加一些文件哈希检查,以确保您正确获得文件。

最新更新