Java 从套接字读取时卡在写入操作中

Java 从套接字读取时卡在写入操作中,java,sockets,dataoutputstream,Java,Sockets,Dataoutputstream,我正在通过套接字将文件及其名称发送到ServerSocket。 它“部分”工作——服务器获取文件并将其保存到磁盘,但 它不会退出ClientSession类中copy()方法中的循环 public class Client{ DataOutputStream dos =null; DataInputStream dis=null; File f =new File("c:/users/supernatural.mp4"); public static void main(

我正在通过套接字将文件及其名称发送到ServerSocket。 它“部分”工作——服务器获取文件并将其保存到磁盘,但 它不会退出ClientSession类中copy()方法中的循环

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());从服务器 因为它在将文件写入磁盘后不会转到下一行,有时甚至会卡在那里

请给我指出正确的道路,我相信我在这里做错了什么
不知道这是否有帮助,但客户端在eclipse中运行,服务器在netbeans中运行,请考虑您的procodol:

  • 客户端发送文件名,然后发送二进制文件,然后等待服务器响应
  • 服务器读取文件名,然后读取二进制文件,直到流关闭,然后发送成功消息
但是流永远不会关闭,因为客户端正在等待响应,因此协议中存在死锁

这通常是通过先发送文件大小,然后让服务器精确读取那么多字节来解决的

或者,您可以使用TCP的单向关闭功能向服务器发送套接字输出流已关闭的信号。这可以通过
socc.shutdownOutput()完成

请使用以避免资源泄漏(您也必须关闭套接字)

固定客户端:

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

}

代码的问题是,您从套接字的输入流中读取数据,而套接字从未关闭

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,您可以在中看到

在你的情况下,这可能是不可行的

您想用“OK”或“error”回答客户。如果关闭插座,则无法通过同一插座进行应答。解决这个问题的办法可能很复杂

这种情况有点棘手。大多数框架都有一个线程,该线程从SocketInputStream读取并将返回值传递给某种处理程序(在阻塞IO中)。while循环基本上就是线程中的这个主读取循环。只有当连接断开并且因此导致
System.out.println(“上次读取:+count”)时,该循环才会退出可以更改为
System.out.println(“断开”)

为了简单起见:您可以估计文件的大小,然后(仅出于测试目的)编写如下内容:

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

我在这里漏掉了所有的错误检查!这意味着您只能从服务器读取一个包,该包必须是文件。

我只是删除了它以节省键入时间。我会更新问题以包括它们,谢谢。还有一件事,你的意思是我将.flush()放在服务器的copy()方法中,还是放在客户端的write()中,或者两者都放在其中?在
//clean resources…
?@rustyx我试图关闭dataInputStream和DataOutitStreams。副本();传输数据后,ClientSession中的方法不退出!我已经包括了第一个回复中建议的刷新,但没有任何效果。好吧,我已经做到了!那么,对于服务器在写入发送给客户端的文件后为什么不向客户端发回消息,有什么建议或更正吗?服务器没有打印“最后一个字节”,这意味着它不知何故被困在读写操作中。另外,我不应该关闭套接字,因为如果我关闭它,它将中止,并且不会等待从服务器读取。非常感谢兄弟。你真棒。