繁体   English   中英

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

[英]Resume file upload/download after lost connection (Socket programming)

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

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之类的哈希函数在传输后验证文件,例如,它为给定的输入创建唯一的标记,并且对于相同的输入始终输出相同的结果,这意味着您可以从同一文件创建标记在服务器和客户端中,如果文件确实是相同的,它将生成相同的标记。 由于图章的大小相对于它自己的文件来说很小,并且它也是固定的,因此可以在客户端/服务器之间发送它而没有太多开销。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM