Застрял в операции записи при чтении из сокета - PullRequest
0 голосов
/ 18 декабря 2018

Я отправляю файл и его имя через Socket на ServerSocket.Он работает «частично» - сервер получает файл и сохраняет его на диск, но не выходит из цикла в методе copy () класса ClientSession.

public class Client{
   DataOutputStream dos =null;
   DataInputStream dis=null; 
   File f =new File("c:/users/supernatural.mp4");
  public static void main(String[]ar) throws Exception{
    try {
          System.out.println("File upload started");
          Socket socc = new Socket("localhost",8117);
          dos = new DataOutputStream(socc.getOutputStream());
          //send file name
          dos.writeUTF(f.getName());
          //send the file
          write(f,dos);
          //Files.copy(f.toPath(),dos);
          //this prints
          System.out.println("Data has been sent...waiting for server to respond ");
          dis = new DataInputStream(socc.getInputStream());
          //this never reads; stuck here
          String RESPONSE = dis.readUTF();
          //this never prints prints
          System.out.println("Server sent: "+RESPONSE);
        } catch(Exception ex) {
            ex.printStackTrace();
        } finally {
          //close the exceptions
       clean();
        }
  }

  private static void write(File f,DataOutputStream d) throws Exception{
                int count;
                DataInputStream din = new DataInputStream(new BufferedInputStream(new FileInputStream(f)));
                byte array[] = new byte[1024*4];
                while((count =din.read(array)) >0){
                    d.write(array,0,count);
                }
                d.flush();
        //this prints
                System.out.println(" done sending...");
                din.close();    
    }
    }

    //Server
    public class MySocket implements Runnable{

        int worker_thread=2;
        volatile boolean shouldRun =false;
        ServerSocket server;
        String port = "8117";
        //ExecutorService services;
        static ExecutorService services;

    public MySocket() {
            this.server = new ServerSocket(Integer.valueOf(port));
            services = Executors.newFixedThreadPool(this.worker_thread);
        }
       //A METHOD TO RUN SERVER THREAD
        @Override
       public void run(){
           while(this.shouldRun){
               Socket client =null;
               try{
               client = server.accept();
               }catch(Exception ex){
                   ex.printStackTrace();
               }
               //hand it over to be processed
               this.services.execute(new ClientSessions(client));
           }
       }   

    public static void main(String[]ar) throws Exception{
        Thread t = new Thread(new MySocket());
            t.start();
    }
    }

    //the ClientSession
    public class ClientSessions implements Runnable{

        Socket s;

        public ClientSessions(Socket s){
        this.s = s;    
        }

        DataInputStream dis=null;
        DataOutputStream dos=null;
        boolean success =true;

        @Override
        public void run(){
            //get the data
            try{
            //get inside channels    
            dis = new DataInputStream(this.s.getInputStream());
            //get outside channels
            dos = new DataOutputStream(this.s.getOutputStream());
         //read the name
        //this works
            String name=dis.readUTF();
            String PATH_TO_SAVE ="c://folder//"+name;
                    //now copy file to disk
                   File f = new File(PATH_TO_SAVE);
                    copy(f,dis);
                    //Files.copy(dis,f.toPath());
        //this doesnt print, stuck in the copy(f,dis) method
                    System.out.println("I am done");
                    success =true;
            }catch(Exception ex){
                ex.printStackTrace();
            }finally{
                //clean resources...
               clean();
            }
        }
       //copy from the stream to the disk 
        private void copy(File f,DataInputStream d)throws Exception{
                    f.getParentFile().mkdirs();
                    f.createNewFile();
                    int count =-1;
                    DataOutputStream out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(f)));
                    byte array[] = new byte[1024*8];
                    count =d.read(array);
                    while(count >0){
                        out.write(array,0,count);
                        count =d.read(array);
                        System.out.println("byte out: "+count);
                    }
        //this never prints
                    System.out.println("last read: "+count);
                    out.flush();
                    out.close();
     if(success)dos.writeUTF("Succesful");
                else dos.writeUTF("error");
        }
    } 

//for the clean method i simply have
void clean(){
  if(dis!=null)dis.close();
  if(dos!=null)dos.close();
}

Я прокомментировал это // Files.copy (DIS, f.toPath ());с сервера, потому что он не переходит на следующую строку после записи файла на диск, иногда даже застревает там.

Могут ли некоторые PLS указать мне правильный путь, я считаю, что я делаю что-то очень неправильно здесь, не знаю, если этополезно, но клиент работает в eclipse, а сервер в netbeans

Ответы [ 2 ]

0 голосов
/ 18 декабря 2018

Проблема с вашим кодом в том, что вы читаете из входного потока сокета, который никогда не закрывается.

DataOutputStream out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(f)));
byte array[] = new byte[1024*8];
count =d.read(array);
while(count >0){
    out.write(array,0,count);
    count =d.read(array);
    System.out.println("byte out: "+count);
}
//this never prints
System.out.println("last read: "+count);

d.read(array) активно пытается прочитать из сокета, блокируя дополучает что-то.Поскольку InputStream активно блокируется, он никогда не возвращает значение, меньшее или равное 0. Это связано с тем, что поток ожидает следующий пакет с другого конца сокета.

Закрытие сокета после отправки файла должнопомочь тебе.В этом случае достигается конец потока и возвращается InputStream.

Примечание : InputStream, с которого вы читаете, будет (если сокет закрыт) возвращать -1, как вы можете видеть в JavaDoc .

В вашем случае это, однако, может быть нежизнеспособным!

Вы хотите ответить клиенту "хорошо" или "ошибка".Если вы закроете сокет, вы не сможете ответить через тот же сокет.Решение этой проблемы может быть сложным.

Эта ситуация немного сложнее.Большинство фреймворков имеют Thread, который читает из SocketInputStream и передает возвращаемое значение некоторому обработчику (при блокировке ввода-вывода).Ваш цикл while - это основной цикл чтения внутри Thread.Этот цикл завершится только в том случае, если соединение потеряно, и поэтому System.out.println("last read: "+count); можно изменить на System.out.println("disconnected");

Для простоты: вы можете дать оценку того, как big файл будет и (только для целей тестирования) напишите что-то вроде этого:

DataOutputStream out = new DataOutputStream(new 
BufferedOutputStream(new FileOutputStream(f)));
byte array[] = new byte[/* Big enough */ 1024 * 1024 * 8];
d.read(array); // Read the file content
out.write(array); // Write to the file
//this never prints
System.out.println("last read: "+count);

Я пропустил здесь каждую проверку ошибок!Это означает, что вы читаете только один пакет с сервера, который должен быть файлом.

0 голосов
/ 18 декабря 2018

Подумайте о вашем procotol:

  • Клиент отправляет имя файла, затем отправляет двоичный файл, а затем ожидает ответа сервера.
  • Сервер читает имя файла,затем двоичный файл до тех пор, пока поток не будет закрыт, затем отправляет сообщение об успешном завершении.

Но поток никогда не закрывается, так как клиент ожидает ответа, поэтому в вашем протоколе есть тупик.

Обычно это решается отправкой файла вначале с размером файла и чтением сервером именно такого количества байтов.

В качестве альтернативы вы можете использовать функцию одностороннего отключения TCP, чтобы отправить сигнал серверу, которыйвыходной поток сокета закрыт.Это можно сделать с помощью socc.shutdownOutput();

И, пожалуйста, используйте try-with-resources , чтобы избежать утечек ресурсов (вы также должны закрыть Socket).

Fixed Client:

    try {
        System.out.println("File upload started");
        try (Socket socc = new Socket("localhost", 8117);
                DataOutputStream dos = new DataOutputStream(socc.getOutputStream());
                DataInputStream dis = new DataInputStream(socc.getInputStream())) {
            // send file name
            dos.writeUTF(f.getName());
            // send the file
            Files.copy(f.toPath(), dos);
            dos.flush();
            System.out.println("Data has been sent...waiting for server to respond ");
            // signal to server that sending is finished
            socc.shutdownOutput();
            String RESPONSE = dis.readUTF();
            // this never prints prints
            System.out.println("Server sent: " + RESPONSE);
        }
    } catch (Exception ex) {
        ex.printStackTrace();
    }

Сервер:

public class MySocket implements Runnable {

    int worker_thread = 2;
    volatile boolean shouldRun = true;
    ServerSocket server;
    int port = 8117;
    ExecutorService services;

    public MySocket() throws IOException {
        this.server = new ServerSocket(port);
        services = Executors.newFixedThreadPool(this.worker_thread);
    }

    // A METHOD TO RUN SERVER THREAD
    @Override
    public void run() {
        while (this.shouldRun) {
            Socket client = null;
            try {
                client = server.accept();
            } catch (Exception ex) {
                ex.printStackTrace();
            }
            // hand it over to be processed
            this.services.execute(new ClientSessions(client));
        }
    }

    public static void main(String[] ar) throws Exception {
        new MySocket().run();
    }
}

class ClientSessions implements Runnable {
    Socket s;
    public ClientSessions(Socket s) {
        this.s = s;
    }
    @Override
    public void run() {
        // get the data
        try (DataInputStream dis = new DataInputStream(this.s.getInputStream());
                DataOutputStream dos = new DataOutputStream(this.s.getOutputStream())) {
            // read the name
            // this works
            String name = dis.readUTF();
            String PATH_TO_SAVE = name;
            // now copy file to disk
            File f = new File("c://folder", PATH_TO_SAVE);
            Files.copy(dis, f.toPath());
            dos.writeUTF("Succesful");
            System.out.println("I am done");
        } catch (Exception ex) {
            ex.printStackTrace();
        } finally {
            try {
                s.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}
...