Java中的非阻塞文件传输

时间:2014-06-04 07:13:58

标签: java file-transfer nonblocking

我尝试使用Java非阻塞连接发送文件。当我使用小文件时,它可以很好地工作,但是当我发送大于30MB的文件时,文件就会刹车。

要查看问题,只需在第一个参数上运行SocketsFileServer,文件大于30MB。之后运行SocketsFileClient。

任何人都可以帮助我吗?

SocketsFileServer.java

public class SocketsFileServer {

      public final static int SOCKET_PORT = 13267;

      Selector selector = null;
      ServerSocketChannel serverSocketChannel = null;
      private File file;

      SocketsFileServer(File fileToServe) {
          try {
              file = fileToServe;
              selector = Selector.open();

              serverSocketChannel = ServerSocketChannel.open();
              serverSocketChannel.configureBlocking(false);

              serverSocketChannel.socket(). setReuseAddress (true);
              serverSocketChannel.socket(). bind (new InetSocketAddress(SOCKET_PORT));

              serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
          } catch (IOException e) {
              System.exit(-1);
              e.printStackTrace();
          }
      }

      public void run() {
          try { 
              while (selector.select() > 0) {
                  Iterator<SelectionKey> it = selector.selectedKeys().iterator();

                  while (it.hasNext ()) {
                      SelectionKey readyKey = it.next();
                      it.remove();

                      if (readyKey.isAcceptable()) {
                          ServerSocketChannel server = (ServerSocketChannel) readyKey.channel();
                          SocketChannel channel = server.accept();
                          channel.configureBlocking(false);
                          channel.register(selector, SelectionKey.OP_READ);
                      } else if (readyKey.isWritable()) {
                          SocketChannel channel = (SocketChannel) readyKey.channel();
                          sendFile(channel, file);
                          channel.close();
                      } else if (readyKey.isReadable()) {
                          receiveCommand((SocketChannel) readyKey.channel());
                          readyKey.interestOps(SelectionKey.OP_WRITE);
                      }
                  }
              }
          } catch (IOException e) {
                e.printStackTrace();
          } finally {
              stop();
          }
      }

      private void receiveCommand(SocketChannel socketChannel)
      {
          int BUFFER_SIZE = 255;
          ByteBuffer buffer = ByteBuffer.allocate(BUFFER_SIZE);
          try {
              socketChannel.read(buffer);
              buffer.flip();
              // Não interessa o que ler, apenas segue para continuar o funcionamento do protocolo
          } catch (Exception e) {
              e.printStackTrace();
          }  
      }

      private void sendFile(SocketChannel socketChannel, File file) {
            FileInputStream fis = null;
            FileChannel channel = null;
            try {
                try {
                    // Envia tamanho do arquivo
                    ByteBuffer bufferSize = ByteBuffer.allocate(8);
                    bufferSize.putLong(file.length());
                    bufferSize.rewind();
                    socketChannel.write(bufferSize);

                    // Envia arquivo de fato
                    fis = new FileInputStream(file);
                    channel = fis.getChannel();
                    ByteBuffer buffer = ByteBuffer.allocateDirect(1024);
                    int size = 0;
                    while ((size = channel.read(buffer)) > -1) {
                        buffer.rewind();
                        buffer.limit(size);
                        socketChannel.write(buffer);
                        buffer.clear();
                    }
                    socketChannel.socket().shutdownOutput();
                } finally {
                    channel.close();
                    fis.close();
                }
          } catch (IOException e) {
              e.printStackTrace();
          } 
      }

      public void stop() {
          try {
              selector.close();
              serverSocketChannel.close();
          } catch (IOException e) {
              e.printStackTrace();
          }
      }

      public static void main (String [] args) {
            if (args.length > 1) {
                System.err.println("USO: SocketsFileServer [Arquivo]");
                System.exit(1);
            }

            try {
                File file;
                if (args.length > 0) {
                    file = new File(args[0]);
                    if (!file.exists()) {
                        System.err.println("ERRO: Arquivo '" + args[0] + "' não existe");
                        System.exit(-1);
                    }
                } else {
                    file = File.createTempFile("SocketsFileServer", ".tmp");
                    FileWriter fileWriter = new FileWriter(file);
                    fileWriter.write("Este é um arquivo temporário criado apenas para teste!");
                    fileWriter.close();
                }

                Thread.currentThread().setName(SocketsFileServer.class.getName() + ".main()");

                SocketsFileServer socEx = new SocketsFileServer(file);
                socEx.run();
            } catch (Throwable e) {
                System.out.flush();
                System.err.println("ERRO: " + e);
                e.printStackTrace(System.err);
                System.exit(-1);
            }
      }
}

SocketsFileClient.java

public class SocketsFileClient {
    public final static int SOCKET_PORT = 13267;
    public final static String SOCKET_SERVER = "127.0.0.1";

    public final static int FILE_SIZE = 6022386; // precisa ser maior que o arquivo recebido

    private String ip;

    SocketsFileClient(String ip) {
        try {
            this.ip = ip;

        } catch (Exception e) {
            e.printStackTrace();
            System.exit(-1);
        }
    }

    public void run() {
        try {
            for (int i = 0; i < 10; i++) {
                System.out.println("Connecting...");
                Socket sock = new Socket(ip, SOCKET_PORT);
                try {
                    int bytesRead;
                    File content = new File("content");
                    byte [] mybytearray  = new byte [FILE_SIZE];

                    InputStream is = sock.getInputStream();
                    DataInputStream dis = new DataInputStream(is);
                    OutputStream os = sock.getOutputStream();
                    OutputStreamWriter osw = new OutputStreamWriter(os);
                    BufferedWriter bw = new BufferedWriter(osw);

                    FileOutputStream fos = new FileOutputStream(content);
                    BufferedOutputStream bos = new BufferedOutputStream(fos);

                    try {
                        long startTime = System.nanoTime();
                        bw.write("GET [FILE NAME]\n");
                        bw.flush();

                        long fileSize = dis.readLong();
                        long startTransferTime = System.nanoTime();

                        while ((bytesRead = is.read(mybytearray, 0, mybytearray.length)) > -1) {
                            bos.write(mybytearray, 0, bytesRead);
                        }
                        bos.flush();
                        long endTime = System.nanoTime();

                        if (content.length() == fileSize) {
                            System.out.println("Completo [" + (i + 1) + "]");
                            System.out.println("Tamanho: " + content.length() + " bytes");
                            System.out.println("Tempo solicitação: " + (startTransferTime - startTime) / 1e6 + " milisegundos");
                            System.out.println("Tempo transferência: " + (endTime - startTransferTime) / 1e6 + " milisegundos");
                            System.out.println("Tempo total: " + (endTime - startTime) / 1e6 + " milisegundos");
                        } else {
                            System.out.println("Falha ao baixar conteúdo");
                        }
                    } finally {
                        fos.close();
                        bos.close();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    sock.close();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main (String [] args) {
       if (args.length > 2) {
            System.err.println("USO: SocketsFileClient [IP]");
            System.exit(1);
        }
        try {
            Thread.currentThread().setName(SocketsFileClient.class.getName() + ".main()");

            String ip = "";
            if (args.length == 0) {
                ip = SOCKET_SERVER;
            } else {
                ip = args[0];
            }

            SocketsFileClient socEx = new SocketsFileClient(ip);
            socEx.run();
        } catch (Throwable e) {
            System.out.flush();
            System.err.println("ERRO: " + e);
            e.printStackTrace(System.err);
            System.exit(-1);
        }
    }
}

1 个答案:

答案 0 :(得分:2)

while ((size = channel.read(buffer)) > -1) {
                        buffer.rewind();
                        buffer.limit(size);
                        socketChannel.write(buffer);
                        buffer.clear();
                    }

这根本不正确。它可能随时丢失数据。编写此循环的正确方法如下:

while (channel.read(buffer) >= 0 || buffer.position() > 0) {
                        buffer.flip();
                        socketChannel.write(buffer);
                        buffer.compact();
                    }