javasocketsdataoutputstream

Stuck in write operation when reading from Socket


I'm sending a file and its name through Socket to a ServerSocket. It works "partially" -- the server gets the file and saves it to disk but it does not exit the loop in the copy() method in the ClientSession class.

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

I commented this //Files.copy(dis,f.toPath()); from server because it does not go to next line after writing file to disk, sometimes even stuck there.

Could some pls point me in the right path, I believe i am doing something very wrong here dont know if this is helpful but the client runs in eclipse and server in netbeans


Solution

  • Think about your procotol:

    But the stream is never closed since the client is waiting for the response, hence you have a deadlock in your protocol.

    This is usually solved by sending the file size first and having the server read exactly that many bytes.

    Alternatively you can use the TCP's one-way shutdown feature to send a signal to the server that the output stream of the socket is closed. That can be done with socc.shutdownOutput();

    And please use try-with-resources to avoid resource leaks (you must close the Socket, too).

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

    Server:

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